Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0fdf74a73 |
No files matched your search
+34
-35
@@ -1,36 +1,35 @@
|
||||
nohup.out
|
||||
build/
|
||||
cmake-build-debug/
|
||||
.idea/
|
||||
.dir-locals.el
|
||||
*.pyc
|
||||
.vscode/*
|
||||
!.vscode/*.json
|
||||
.vs
|
||||
obj/
|
||||
*.csproj.user
|
||||
*.vspx
|
||||
*.psess
|
||||
msbuild.log
|
||||
*.orig
|
||||
vcxproj.user
|
||||
src/client/spark/Properties/launchSettings.json
|
||||
compile_commands.json
|
||||
.cquery_cached_index/
|
||||
src/client/sparkcmd/Properties/launchSettings.json
|
||||
node_modules
|
||||
paket-files
|
||||
project.lock.json
|
||||
src/server/sparkd/orderman/perf_analysis/data
|
||||
src/server/sparkd/orderman/perf_analysis/logs/
|
||||
src/server/sparkd/orderman/perf_analysis/trace_data/
|
||||
.fake
|
||||
*.fsx.lock
|
||||
*.code-workspace
|
||||
src/lib/orderman/perf_analysis/logs/latency_data/
|
||||
trace_data/
|
||||
/src/lib/orderman/perf_analysis/last_pretty_log.csv
|
||||
/src/lib/orderman/perf_analysis/log.csv
|
||||
/doc/user/public/*
|
||||
nohup.out
|
||||
build/
|
||||
cmake-build-debug/
|
||||
.idea/
|
||||
.dir-locals.el
|
||||
*.pyc
|
||||
.vscode/*
|
||||
!.vscode/*.json
|
||||
.vs
|
||||
obj/
|
||||
*.csproj.user
|
||||
*.vspx
|
||||
*.psess
|
||||
msbuild.log
|
||||
*.orig
|
||||
vcxproj.user
|
||||
src/client/spark/Properties/launchSettings.json
|
||||
compile_commands.json
|
||||
.cquery_cached_index/
|
||||
src/client/sparkcmd/Properties/launchSettings.json
|
||||
node_modules
|
||||
paket-files
|
||||
project.lock.json
|
||||
src/server/sparkd/orderman/perf_analysis/data
|
||||
src/server/sparkd/orderman/perf_analysis/logs/
|
||||
src/server/sparkd/orderman/perf_analysis/trace_data/
|
||||
.fake
|
||||
*.fsx.lock
|
||||
*.code-workspace
|
||||
src/lib/orderman/perf_analysis/logs/latency_data/
|
||||
trace_data/
|
||||
/src/lib/orderman/perf_analysis/last_pretty_log.csv
|
||||
/src/lib/orderman/perf_analysis/log.csv
|
||||
/doc/user/public/*
|
||||
.ionide/*
|
||||
src/web/ui/.ionide/symbolCache.db
|
||||
Vendored
+1
-2
@@ -44,7 +44,6 @@
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/build/ninja_debug/sdk/examples" : "${workspaceRoot}/src/sdk/examples",
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
@@ -250,7 +249,7 @@
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": [
|
||||
"test",
|
||||
"[algo]"
|
||||
"SingleWriterConcurrentVector"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server/",
|
||||
|
||||
@@ -59,7 +59,6 @@ else ()
|
||||
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libACE-5.7.5.so ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/excel_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
|
||||
COMMENT "Creating server runtime layout"
|
||||
DEPENDS sparkd data_files_dir)
|
||||
|
||||
|
||||
+4
-13
@@ -1,11 +1,11 @@
|
||||
+++
|
||||
title = "Basic Algos"
|
||||
weight = 1
|
||||
title = "BTS Basic Algos"
|
||||
weight = 10
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
aliases = ["./bts_spark/Algos/"]
|
||||
+++
|
||||
|
||||
|
||||
## Lean
|
||||
|
||||
The goal of the lean order is to watch the opposite-inside market size and cross when that size goes below the threshold (this also implies a cross if that size goes to zero and the market trades away from you) If the market trades toward you, you can opt into several different behaviors
|
||||
@@ -43,13 +43,4 @@ An OCO order creates a relationship between a child order and a 'stop' or 'other
|
||||
Composing this order with Cover simulates scalping functionality
|
||||
|
||||
##### Parameters
|
||||
* Stop Ticks - ticks below or above main order to place stop / other order
|
||||
|
||||
## Splitter
|
||||
The splitter is useful for pro-rata markets where you want to vary your exposure independent of the order quantity that you want to fill. The splitter will complete when the number of fills reaches or exceeds the order quantity but it will put exposed_qty total size on the market. Splitter favors order size over queue position.
|
||||
|
||||
If exposed_qty is set to 0 the splitter will continue to exist in the Order Book but will cancel all of its child orders. This makes it a useful building block for custom algos that need to implement a 'paused' state.
|
||||
|
||||
##### Parameters
|
||||
* exposed_qty - The total quantity to expose on the market.
|
||||
* max_order_qty - The maximum size of a single order.
|
||||
* Stop Ticks - ticks below or above main order to place stop / other order
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -4,42 +4,6 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 2.7.2
|
||||
### User Visible Changes
|
||||
- Changed the login dialog to avoid leaking workspace names from other users.
|
||||
- Added new Splitter algo. From the manual: The splitter is useful
|
||||
for pro-rata markets where you want to vary your exposure
|
||||
independent of the order quantity that you want to fill. The
|
||||
splitter will complete when the number of fills reaches or exceeds
|
||||
the order quantity but it will put exposed_qty total size on the
|
||||
market. Splitter favors order size over queue position.
|
||||
### Internal Changes
|
||||
- Made spark more resilient to lack of connectivity to the CME web site for downloading of templates.
|
||||
|
||||
## 2.7.1
|
||||
### User Visible Changes
|
||||
- Fixed some issues with the Cover algo, where in some circumstances
|
||||
it would erroneously cancel launched hedges on completion.
|
||||
|
||||
## 2.7.0
|
||||
### User Visible Changes
|
||||
- Allowed algos to process "in-flight" fills while shutting down
|
||||
- Changed the cover algo to continue to hedge while shutting down
|
||||
### API Changes
|
||||
- Changed the API so that algos do not fully terminate until all
|
||||
their children are terminated. Algos that are in the process of
|
||||
shutting down in response to a complete or cancel call will still
|
||||
receive child updates, which they can use to calculate and report
|
||||
an accurate fill count (during this shutdown stage algos cannot
|
||||
create, cancel or update children). A new IAlgoServices method,
|
||||
is_shutting_down(), can be used to determine if an algo is
|
||||
shutting down.
|
||||
|
||||
## 2.6.2
|
||||
### User Visible Changes
|
||||
- Fixed an issue in the ladder where in some cases the mini order book could not be brought up from a badge
|
||||
- Fixed a rare race condition that could lead to erroneous limit enforcement
|
||||
- Fixed an issue with the margin limits where an error could occur if an account name with more than 12 chars was used
|
||||
|
||||
## 2.6.1
|
||||
### User Visible Changes
|
||||
@@ -224,7 +188,7 @@ insert_anchor_links = "right"
|
||||
- Integrated stunnel into UI to make VPN setup unnecessary in unsecured environments.
|
||||
## 1.5.7
|
||||
### User Visible Changes
|
||||
- New videos have been added to the manual describing the [Algo Studio](./bts_spark/Trading/_index.md#algo-studio) and [Spreader](./bts_spark/Spreader/_index.md)
|
||||
- New videos have been added to the manual describing the [Algo Studio](./bts_spark/Trading/index.md#algo-studio) and [Spreader](./bts_spark/Spreader/index.md)
|
||||
- Fixed a bug that occasionally would cause windows to be position outside the visible screen on startup
|
||||
- Fixed a bug in the order book form that would occasionally cause the Recreate Order option to be unavailable
|
||||
- Added a confirmation step to Cancel All, Cancel Buys and Cancel Sells commands
|
||||
@@ -347,7 +311,7 @@ insert_anchor_links = "right"
|
||||
- The zoom slider can be enabled from Ladder Form -> Settings button -> Left Pane tab -> check Zoom Slider checkbox
|
||||
- The zoom functionality can also be performed via hotkey and mouse wheel combinations
|
||||
- Zoom In = CTRL + Mouse wheel up
|
||||
- Zoom Out = CTRL + Mouse wheel down␍
|
||||
- Zoom Out = CTRL + Mouse wheel down
|
||||
- Reset zoom = CTRL + Mouse wheel click or double click on the zoom slider
|
||||
### Exchange simulator
|
||||
- Improved the exchange simulator to track real market data more closely
|
||||
|
||||
@@ -51,9 +51,6 @@ namespace mbts.spark {
|
||||
readonly Property<AlgoFormats> _selected_format;
|
||||
readonly EnumMaskInt32<AlgoEditingPermissions> _permissions_mask = new EnumMaskInt32<AlgoEditingPermissions>();
|
||||
|
||||
mbts.ui.spark.AlgoContentTemplateSelector.OrderKind IAlgoSelector.order_kind =>
|
||||
new mbts.ui.spark.AlgoContentTemplateSelector.OrderKind(string.Empty, string.Empty);
|
||||
|
||||
int? _favorite_index;
|
||||
Brush _badge_fill;
|
||||
Snapshot? _committed;
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace bts.shell.orderbook {
|
||||
pb.add_discrete_values(fs.symbols, o => o.security?.Symbol);
|
||||
// numeric
|
||||
pb.add_numeric(fs.qty, o => o.order.quantity());
|
||||
pb.add_price(fs.price, o => o.order.price(), o => o.security);
|
||||
pb.add_numeric(fs.price, o => o.order.price());
|
||||
pb.add_numeric(fs.qty_filled, o => o.order.filled());
|
||||
pb.add_numeric(fs.avg_price, o => o.order.avg_price());
|
||||
pb.add_numeric(fs.last_price, o => o.order.last_price());
|
||||
|
||||
@@ -26,11 +26,7 @@ namespace mbts.spark.utilities {
|
||||
public readonly Func<CustomOrderDefinition, CustomOrderDefinition> get_child;
|
||||
public readonly Action<CustomOrderDefinition, CustomOrderDefinition> assign_child;
|
||||
|
||||
public ChildAlgoData(string name, Func<IMessage, CustomOrderDefinition> get_child_from_impl,
|
||||
Action<IMessage, CustomOrderDefinition> assign_child_to_impl,
|
||||
Func<CustomOrderDefinition, CustomOrderDefinition> get_child,
|
||||
Action<CustomOrderDefinition, CustomOrderDefinition> assign_child)
|
||||
{
|
||||
public ChildAlgoData(string name, Func<IMessage, CustomOrderDefinition> get_child_from_impl, Action<IMessage, CustomOrderDefinition> assign_child_to_impl, Func<CustomOrderDefinition, CustomOrderDefinition> get_child, Action<CustomOrderDefinition, CustomOrderDefinition> assign_child) {
|
||||
this.name = name;
|
||||
this.get_child = get_child;
|
||||
this.assign_child = assign_child;
|
||||
@@ -216,7 +212,6 @@ namespace mbts.spark.utilities {
|
||||
public TriggerOrderDefinition trigger_order() => OrderDataHelper.Defaults.TriggerOrderDefinition();
|
||||
public DynamicOrderDefinition dynamic_order() => OrderDataHelper.Defaults.DynamicOrderDefinition();
|
||||
public SpreaderOrderDefinition spreader_order() => OrderDataHelper.Defaults.SpreaderOrderDefinition();
|
||||
public SplitterOrderDefinition splitter_order() => OrderDataHelper.Defaults.SplitterOrderDefinition();
|
||||
public SpreaderOrderDefinition spreader_order(SecurityImmutable security, ExecutionStrategyImmutable strategy) =>
|
||||
new SpreaderOrderDefinition {
|
||||
Security = security.CloneToMutable(),
|
||||
@@ -247,12 +242,8 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: rv.Offset = offset_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = trigger_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = spreader_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Splitter: rv.Splitter = splitter_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Dynamic: rv.Dynamic = dynamic_order(); break;
|
||||
//case CustomOrderDefinition.CustomOneofCase.QueueHolder: rv.QueueHolder = queue_holder_order(); break;
|
||||
default:
|
||||
OrderTypeVMFactory.GenericOrderFactory.setup_generic_custom_order(rv, order_type);
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -278,7 +269,6 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.off_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: return new OrderMetadata { KeyBinding = "", Color = BTSColors.Orders.spreader_order };
|
||||
// case CustomOrderDefinition.CustomOneofCase.Splitter: return new OrderMetadata { KeyBinding = "", Color = BTSColors.orange.rgb_color() };
|
||||
case CustomOrderDefinition.CustomOneofCase.Dynamic: return new OrderMetadata { KeyBinding = "", Color = BTSColors.Orders.top_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.None: goto default;
|
||||
//case CustomOrderDefinition.CustomOneofCase.QueueHolder: return new OrderMetadata { KeyBinding = "Q", Color = BTSColors.purple.rgb_color() };
|
||||
@@ -599,8 +589,9 @@ namespace mbts.spark.utilities {
|
||||
new AlgoStructureKey(defi.CustomCase)));
|
||||
|
||||
public static readonly CustomOrderDefinition.CustomOneofCase[]
|
||||
|
||||
// exchange_order_types are the types which can be submitted on an exchange security
|
||||
// The following standard types are not yet ready for production use:
|
||||
// CustomOrderDefinition.CustomOneofCase.Tick,
|
||||
// CustomOrderDefinition.CustomOneofCase.Dynamic,
|
||||
exchange_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
CustomOrderDefinition.CustomOneofCase.Limit,
|
||||
@@ -612,21 +603,17 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
||||
CustomOrderDefinition.CustomOneofCase.Splitter,
|
||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset
|
||||
},
|
||||
// The following spreader types are not yet ready for production use:
|
||||
// CustomOrderDefinition.CustomOneofCase.Dynamic,
|
||||
spreader_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Spreader,
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Splitter,
|
||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top
|
||||
},
|
||||
attachable_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
@@ -634,9 +621,7 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset
|
||||
},
|
||||
outright_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
@@ -1022,9 +1007,6 @@ namespace mbts.spark.utilities {
|
||||
case bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = TriggerOrderDefinition(); break;
|
||||
case bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = SpreaderOrderDefinition(); break;
|
||||
case bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Dynamic: rv.Dynamic = DynamicOrderDefinition(); break;
|
||||
default:
|
||||
OrderTypeVMFactory.GenericOrderFactory.setup_generic_custom_order(rv, order_type);
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -1050,9 +1032,6 @@ namespace mbts.spark.utilities {
|
||||
public static TriggerOrderDefinition TriggerOrderDefinition() => new TriggerOrderDefinition { Child = CustomOrderDefinition(), TriggerCondition = new TriggerCondition(), TriggerAction = new TriggerAction(), };
|
||||
public static DynamicOrderDefinition DynamicOrderDefinition() => new DynamicOrderDefinition();
|
||||
public static SpreaderOrderDefinition SpreaderOrderDefinition() => new SpreaderOrderDefinition { ExecutionStrategy = new ExecutionStrategy() };
|
||||
|
||||
public static SplitterOrderDefinition SplitterOrderDefinition() => new SplitterOrderDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -71,7 +71,6 @@ namespace mbts.spark {
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef: param.AsOrderDef = new CustomOrderDefinition(); break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: param.AsString = string.Empty; break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: param.AsUint32 = 0u; break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveQuantity: param.AsQuantity = new bts.spark.QuantityMeasure(); break;
|
||||
}
|
||||
break;
|
||||
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Structure: param.AsStruct = new DynamicOrderStruct(); break;
|
||||
@@ -88,16 +87,5 @@ namespace mbts.spark {
|
||||
}
|
||||
param.AsEnumerated.EnumValue = value ?? string.Empty;
|
||||
}
|
||||
|
||||
internal static bts.spark.QuantityMeasureImmutable? get_quantity_value(DynamicOrderParam param)
|
||||
{
|
||||
return new bts.spark.QuantityMeasureImmutable(param?.AsQuantity);
|
||||
}
|
||||
internal static void set_quantity_value(DynamicOrderParam param, bts.spark.QuantityMeasureImmutable? value)
|
||||
{
|
||||
if (param == null)
|
||||
return;
|
||||
param.AsQuantity = value?.CloneToMutable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using bts.api.orderman;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.ui.shell;
|
||||
using bts.spark;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -26,7 +25,7 @@ namespace mbts.spark {
|
||||
protected DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor)
|
||||
: this(descriptor, DynamicOrderHelper.init_param(descriptor)) { }
|
||||
|
||||
private DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor, DynamicOrderParam param) {
|
||||
protected DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor, DynamicOrderParam param) {
|
||||
this.descriptor = descriptor;
|
||||
_param = param;
|
||||
}
|
||||
@@ -117,41 +116,4 @@ namespace mbts.spark {
|
||||
void _sync_elements() =>
|
||||
elements.sync_with(_get_elements(descriptor));
|
||||
}
|
||||
|
||||
|
||||
class DynamicOrderQuantityVM : DynamicOrderParamVM, IDynamicOrderParamVM<QuantityMeasureImmutable?>
|
||||
{
|
||||
static readonly FieldDescriptor _field_desc = DynamicOrderParam.Descriptor.FindFieldByNumber(DynamicOrderParam.AsQuantityFieldNumber);
|
||||
public DynamicOrderQuantityVM(DynamicOrderParamDescriptor descriptor)
|
||||
: base(descriptor) { }
|
||||
|
||||
QuantityMeasureImmutable? IDynamicOrderParamVM<QuantityMeasureImmutable?>.value { get => quantity; set => quantity= value; }
|
||||
public QuantityMeasureImmutable? quantity
|
||||
{
|
||||
get => DynamicOrderHelper.get_quantity_value(param);
|
||||
set
|
||||
{
|
||||
var orig = quantity;
|
||||
DynamicOrderHelper.set_quantity_value(param, value);
|
||||
if (quantity != orig)
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public IObservable<QuantityMeasureImmutable?> subscribe_value()
|
||||
{
|
||||
return Observable.Create<QuantityMeasureImmutable?>(o =>
|
||||
{
|
||||
PropertyChanged += on_prop_changed;
|
||||
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
|
||||
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
|
||||
o.OnNext(quantity);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void on_param_changed()
|
||||
{
|
||||
RaisePropertyChanged(nameof(quantity));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ namespace mbts.ui.spark
|
||||
public DataTemplate UIntTemplate { get; set; }
|
||||
public DataTemplate StringTemplate { get; set; }
|
||||
public DataTemplate OrderDefTemplate { get; set; }
|
||||
public DataTemplate QuantityMeasureTemplate { get; set; }
|
||||
|
||||
public DataTemplate EnumTemplate { get; set; }
|
||||
|
||||
@@ -40,7 +39,6 @@ namespace mbts.ui.spark
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: return UIntTemplate;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return StringTemplate;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef: return OrderDefTemplate;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveQuantity: return QuantityMeasureTemplate;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using mbts.collections;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using bts.spark;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -47,13 +46,10 @@ namespace mbts.spark {
|
||||
protected override void on_model_internal() =>
|
||||
_sync_dynamic_params();
|
||||
|
||||
protected override IEnumerable<ChildAlgoData> get_selector_datum()
|
||||
{
|
||||
var data = OrderDataHelper.get_child_algo_data(new CustomOrderDefinition {
|
||||
protected override IEnumerable<ChildAlgoData> get_selector_datum() =>
|
||||
OrderDataHelper.get_child_algo_data(new CustomOrderDefinition {
|
||||
Dynamic = model?.dynamic_helper?.create_def(dyn_type) ?? new DynamicOrderDefinition()
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
void _sync_dynamic_params() {
|
||||
if (model != null) {
|
||||
@@ -74,14 +70,9 @@ namespace mbts.spark {
|
||||
switch (descriptor.ParamType.Primitive) {
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveBool: return mk<bool?, bool>(DynamicOrderParam.AsBoolFieldNumber, v => v, v => v.GetValueOrDefault());
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveDouble: return mk<double, double>(DynamicOrderParam.AsDoubleFieldNumber, v => v, v => v, double.NaN);
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: return mk<int?, int>(DynamicOrderParam.AsInt32FieldNumber, v => v, v => v.GetValueOrDefault());
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: return mk<uint?, uint>(DynamicOrderParam.AsInt32FieldNumber, v => v, v => v.GetValueOrDefault());
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: return mk<uint?, uint>(DynamicOrderParam.AsUint32FieldNumber, v => v, v => v.GetValueOrDefault());
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return mk<string, string>(DynamicOrderParam.AsStringFieldNumber, v => v, v => v);
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveQuantity:
|
||||
{
|
||||
var qm = new DynamicOrderQuantityVM(descriptor);
|
||||
return attached(qm, DynamicOrderHelper.get_quantity_value, DynamicOrderHelper.set_quantity_value, new QuantityMeasure { Ratio = 1.0, Absolute = 1 });
|
||||
}
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(descriptor.ParamType.Primitive, nameof(descriptor));
|
||||
}
|
||||
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated:
|
||||
@@ -106,19 +97,11 @@ namespace mbts.spark {
|
||||
mdef => mdef.OrderParams.TryGetValue(pvm.descriptor.ParamName, out DynamicOrderParam p) ? selector(p) : default_value,
|
||||
(mdef, v) => assign_to_param(mdef.OrderParams.GetOrAdd(pvm.descriptor.ParamName, pn => new DynamicOrderParam()), v),
|
||||
() => pvm.value,
|
||||
v => pvm.value = set_value_not_null_string(v),
|
||||
v => pvm.value = v,
|
||||
pvm.subscribe_value(),
|
||||
default_value);
|
||||
return pvm;
|
||||
}
|
||||
|
||||
T set_value_not_null_string<T>(T val)
|
||||
{
|
||||
if (typeof(T) == typeof(string) && val == null)
|
||||
return (T)(object)string.Empty;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
[Export]
|
||||
class GenericOrderHelper
|
||||
{
|
||||
[ImportingConstructor]
|
||||
private GenericOrderHelper(OrderService order_service)
|
||||
{
|
||||
}
|
||||
|
||||
internal static string get_enumerated_value(IMessage message, FieldDescriptor fd)
|
||||
{
|
||||
var ev = fd.Accessor.GetValue(message);
|
||||
return ev.ToString();
|
||||
}
|
||||
internal static void set_enumerated_value(IMessage message, FieldDescriptor fd, string value)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var ev = fd.EnumType.Values.First(x => x.Name == value)?.Number;
|
||||
if(ev.HasValue)
|
||||
fd.Accessor.SetValue(message, ev.Value);
|
||||
}
|
||||
|
||||
internal static bts.spark.QuantityMeasureImmutable? get_quantity_value(IMessage message, FieldDescriptor fd)
|
||||
{
|
||||
var qm = (bts.spark.QuantityMeasure)fd.Accessor.GetValue(message);
|
||||
if(qm != null)
|
||||
return new bts.spark.QuantityMeasureImmutable(qm);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
internal static void set_quantity_value(IMessage message, FieldDescriptor fd, bts.spark.QuantityMeasureImmutable? value)
|
||||
{
|
||||
if (message == null)
|
||||
return;
|
||||
fd.Accessor.SetValue(message, value?.CloneToMutable());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.ui.shell;
|
||||
using bts.spark;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using Google.Protobuf;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
interface IGenericOrderParamVM
|
||||
{
|
||||
FieldDescriptor descriptor { get; }
|
||||
Google.Protobuf.IMessage message { get; set; }
|
||||
}
|
||||
|
||||
interface IGenericOrderParamVM<T> : IGenericOrderParamVM
|
||||
{
|
||||
T value { get; set; }
|
||||
IObservable<T> subscribe_value();
|
||||
}
|
||||
|
||||
abstract class GenericOrderParamVM<TMutable> : DisposableViewModelBase, IGenericOrderParamVM
|
||||
where TMutable : class, IMessage, new()
|
||||
{
|
||||
TMutable _message;
|
||||
|
||||
protected GenericOrderParamVM(FieldDescriptor descriptor)
|
||||
{
|
||||
_message = new TMutable();
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
public FieldDescriptor descriptor { get; set; }
|
||||
public Google.Protobuf.IMessage message
|
||||
{
|
||||
get => _message;
|
||||
set
|
||||
{
|
||||
_message = (TMutable)value;
|
||||
on_param_changed();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void on_param_changed();
|
||||
}
|
||||
|
||||
class GenericOrderParamVM<TMutable, TView, TModel> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<TView>
|
||||
where TMutable : class, IMessage, new()
|
||||
{
|
||||
readonly Func<TModel, TView> _convert;
|
||||
readonly Func<TView, TModel> _convert_back;
|
||||
TView _value;
|
||||
|
||||
public GenericOrderParamVM(FieldDescriptor field_descriptor, Func<TModel, TView> convert, Func<TView, TModel> convert_back) :
|
||||
base(field_descriptor)
|
||||
{
|
||||
_convert = convert;
|
||||
_convert_back = convert_back;
|
||||
}
|
||||
|
||||
public TView value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
this.descriptor.Accessor.SetValue(this.message, _convert_back(_value = value));
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public IObservable<TView> subscribe_value() =>
|
||||
Observable.Create<TView>(o =>
|
||||
{
|
||||
PropertyChanged += on_prop_changed;
|
||||
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
|
||||
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
|
||||
o.OnNext(value);
|
||||
});
|
||||
|
||||
protected override void on_param_changed()
|
||||
{
|
||||
_value = _convert(ConvertUtils.try_convert<TModel>(this.descriptor.Accessor.GetValue(this.message)));
|
||||
RaisePropertyChanged(nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
class GenericOrderEnumeratedVM<TMutable> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<string>
|
||||
where TMutable : class, IMessage, new()
|
||||
{
|
||||
public GenericOrderEnumeratedVM(FieldDescriptor descriptor)
|
||||
: base(descriptor)
|
||||
{
|
||||
elements = new ObservableCollection<string>(_get_elements(descriptor));
|
||||
}
|
||||
|
||||
public ObservableCollection<string> elements { get; }
|
||||
|
||||
public string selected_element
|
||||
{
|
||||
get => GenericOrderHelper.get_enumerated_value(message, descriptor);
|
||||
set
|
||||
{
|
||||
var orig = selected_element;
|
||||
GenericOrderHelper.set_enumerated_value(message, descriptor, value);
|
||||
if (selected_element != orig)
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
string IGenericOrderParamVM<string>.value { get => selected_element; set => selected_element = value; }
|
||||
|
||||
IObservable<string> IGenericOrderParamVM<string>.subscribe_value() =>
|
||||
Observable.Create<string>(o =>
|
||||
{
|
||||
PropertyChanged += on_prop_changed;
|
||||
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
|
||||
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
|
||||
o.OnNext(selected_element);
|
||||
});
|
||||
|
||||
protected override void on_param_changed()
|
||||
{
|
||||
_sync_elements();
|
||||
RaisePropertyChanged(nameof(selected_element));
|
||||
}
|
||||
|
||||
static IEnumerable<string> _get_elements(FieldDescriptor descriptor)
|
||||
{
|
||||
if(descriptor.FieldType == Google.Protobuf.Reflection.FieldType.Enum)
|
||||
{
|
||||
return descriptor.EnumType.Values.Select(x => x.Name);
|
||||
}
|
||||
else
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
void _sync_elements()
|
||||
{
|
||||
elements.sync_with(_get_elements(descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GenericOrderQuantityVM<TMutable> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<QuantityMeasureImmutable?>
|
||||
where TMutable : class, IMessage, new()
|
||||
{
|
||||
public GenericOrderQuantityVM(FieldDescriptor descriptor)
|
||||
: base(descriptor) { }
|
||||
|
||||
QuantityMeasureImmutable? IGenericOrderParamVM<QuantityMeasureImmutable?>.value { get => quantity; set => quantity= value; }
|
||||
public QuantityMeasureImmutable? quantity
|
||||
{
|
||||
get => GenericOrderHelper.get_quantity_value(message, descriptor);
|
||||
set
|
||||
{
|
||||
var orig = quantity;
|
||||
GenericOrderHelper.set_quantity_value(message, descriptor, value);
|
||||
if (quantity != orig)
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public IObservable<QuantityMeasureImmutable?> subscribe_value()
|
||||
{
|
||||
return Observable.Create<QuantityMeasureImmutable?>(o =>
|
||||
{
|
||||
PropertyChanged += on_prop_changed;
|
||||
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
|
||||
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
|
||||
o.OnNext(quantity);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void on_param_changed()
|
||||
{
|
||||
RaisePropertyChanged(nameof(quantity));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
using bts.api.orderman;
|
||||
using mbts.spark;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace mbts.ui.spark
|
||||
{
|
||||
class GenericOrderParamTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate BoolTemplate { get; set; }
|
||||
public DataTemplate DoubleTemplate { get; set; }
|
||||
public DataTemplate IntTemplate { get; set; }
|
||||
public DataTemplate UIntTemplate { get; set; }
|
||||
public DataTemplate StringTemplate { get; set; }
|
||||
public DataTemplate OrderDefTemplate { get; set; }
|
||||
public DataTemplate QuantityMeasureTemplate { get; set; }
|
||||
|
||||
public DataTemplate EnumTemplate { get; set; }
|
||||
|
||||
public override DataTemplate SelectTemplate(object item, DependencyObject container)
|
||||
{
|
||||
var vm = item as IGenericOrderParamVM;
|
||||
if (vm != null)
|
||||
return select_template(vm.descriptor);
|
||||
return null;
|
||||
}
|
||||
|
||||
public DataTemplate select_template(Google.Protobuf.Reflection.FieldDescriptor descriptor)
|
||||
{
|
||||
switch (descriptor.FieldType)
|
||||
{
|
||||
case Google.Protobuf.Reflection.FieldType.Bool:
|
||||
return BoolTemplate;
|
||||
case Google.Protobuf.Reflection.FieldType.Float:
|
||||
case Google.Protobuf.Reflection.FieldType.Double:
|
||||
return DoubleTemplate;
|
||||
case Google.Protobuf.Reflection.FieldType.Int32:
|
||||
case Google.Protobuf.Reflection.FieldType.Int64:
|
||||
return IntTemplate;
|
||||
case Google.Protobuf.Reflection.FieldType.UInt32:
|
||||
case Google.Protobuf.Reflection.FieldType.UInt64:
|
||||
return UIntTemplate;
|
||||
case Google.Protobuf.Reflection.FieldType.String:
|
||||
return StringTemplate;
|
||||
case Google.Protobuf.Reflection.FieldType.Enum:
|
||||
return EnumTemplate;
|
||||
case Google.Protobuf.Reflection.FieldType.Message:
|
||||
{
|
||||
if(descriptor.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName)
|
||||
return QuantityMeasureTemplate;
|
||||
else if (descriptor.MessageType.FullName == typeof(bts.api.orderman.CustomOrderDefinition).FullName)
|
||||
return OrderDefTemplate;
|
||||
}
|
||||
break;
|
||||
default: return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
|
||||
using bts;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using mbts.collections;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using bts.spark;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
using bts.api;
|
||||
using AlgoType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
internal class GenericOrderFactory
|
||||
{
|
||||
static public IAlgoVM create_vm(AlgoKey key, AlgoType algo_type, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
{
|
||||
var prop = typeof(CustomOrderDefinition).GetProperty(algo_type.ToString(),
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
var rt = prop.PropertyType;
|
||||
|
||||
var propi = typeof(CustomOrderDefinitionImmutable).GetProperty(algo_type.ToString(),
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
var rti = propi.PropertyType;
|
||||
|
||||
var vmt = typeof(GenericOrderVM<,>);
|
||||
Type[] typeargs = { rt, rti };
|
||||
var makevm = vmt.MakeGenericType(typeargs);
|
||||
object vm = Activator.CreateInstance(makevm, key, algo_type, display_name, editing_permissions);
|
||||
return (IAlgoVM)vm;
|
||||
}
|
||||
static public void setup_generic_custom_order(CustomOrderDefinition custom, CustomOrderDefinition.CustomOneofCase type)
|
||||
{
|
||||
var prop = typeof(CustomOrderDefinition).GetProperty(type.ToString(),
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
|
||||
var rt = prop.PropertyType;
|
||||
var rtt = Activator.CreateInstance(rt);
|
||||
|
||||
var ci = rt.GetProperties().Where(x => x.PropertyType == typeof(CustomOrderDefinition));
|
||||
foreach(var cid in ci)
|
||||
cid.SetValue(rtt, new CustomOrderDefinition { Limit = new LimitOrderDefinition() });
|
||||
prop.SetValue(custom, rtt);
|
||||
}
|
||||
}
|
||||
|
||||
class GenericOrderVM<TMutable, TImmutable> : OrderTypeVMBase<TMutable, TImmutable>
|
||||
where TMutable : class, IMessage, new() where TImmutable : struct, IImmutableMessage
|
||||
{
|
||||
readonly ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string> _parameters =
|
||||
new ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string>(vm => vm.descriptor.Name);
|
||||
readonly AlgoType _algo_type;
|
||||
|
||||
public GenericOrderVM(AlgoKey key, AlgoType algo_type, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
: base(key, display_name, editing_permissions)
|
||||
{
|
||||
_algo_type = algo_type;
|
||||
_parameters = new ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string>(vm => vm.descriptor.Name);
|
||||
_resources.AddRange(
|
||||
ObservableAutoWrapper.connect_item_hooks(_parameters, p =>
|
||||
{
|
||||
p.PropertyChanged += on_param_prop_changed;
|
||||
return Disposable.Create(() =>
|
||||
{
|
||||
p.PropertyChanged -= on_param_prop_changed;
|
||||
remove_property(p.descriptor.Name);
|
||||
p.Dispose();
|
||||
});
|
||||
}, true));
|
||||
void on_param_prop_changed(object sender, PropertyChangedEventArgs e) => raise_definition_changed();
|
||||
_sync_generic_params();
|
||||
sync_selectors();
|
||||
}
|
||||
|
||||
public string algo_type => _algo_type.ToString();
|
||||
public override string abbreviation =>
|
||||
model == null ? display_name : model.factory.abbreviate_algo(new OneOf<CustomOrderDefinition.CustomOneofCase, string>(_algo_type));
|
||||
public override AlgoDisplayGroup group => AlgoDisplayGroup.API;
|
||||
public ObservableCollection<GenericOrderParamVM<TMutable>> parameters => _parameters;
|
||||
|
||||
protected override void on_model_internal()
|
||||
{
|
||||
_sync_generic_params();
|
||||
}
|
||||
|
||||
void _sync_generic_params()
|
||||
{
|
||||
if (model != null)
|
||||
{
|
||||
Google.Protobuf.Reflection.MessageDescriptor desc = new TMutable().Descriptor;
|
||||
_parameters.SyncWithDictionary(
|
||||
desc.Fields.InDeclarationOrder().Where(_is_value_param_desc).ToDictionary(p => p.Name), _mk_param_vm);
|
||||
}
|
||||
}
|
||||
static bool _is_value_param_desc(FieldDescriptor desc)
|
||||
{
|
||||
return (desc.FieldType != Google.Protobuf.Reflection.FieldType.Message ||
|
||||
_is_quantity_measure_desc(desc));
|
||||
}
|
||||
|
||||
static bool _is_quantity_measure_desc(FieldDescriptor desc)
|
||||
{
|
||||
return desc.FieldType == Google.Protobuf.Reflection.FieldType.Message &&
|
||||
desc.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ChildAlgoData> get_selector_datum()
|
||||
{
|
||||
var v = OrderDataHelper.get_child_algo_data(_algo_type);
|
||||
return v;
|
||||
}
|
||||
|
||||
GenericOrderParamVM<TMutable> _mk_param_vm(FieldDescriptor descriptor)
|
||||
{
|
||||
ExceptionAssert.Argument.NotNull(descriptor, nameof(descriptor));
|
||||
|
||||
if (descriptor.FieldType == FieldType.Message &&
|
||||
descriptor.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName)
|
||||
{
|
||||
var qm = new GenericOrderQuantityVM<TMutable>(descriptor);
|
||||
return attached(qm, GenericOrderHelper.get_quantity_value, GenericOrderHelper.set_quantity_value, new QuantityMeasure { Ratio = 1.0, Absolute = 1 });
|
||||
}
|
||||
else if(descriptor.FieldType == FieldType.Enum)
|
||||
{
|
||||
var pvm = new GenericOrderEnumeratedVM<TMutable>(descriptor);
|
||||
attached(pvm, GenericOrderHelper.get_enumerated_value, GenericOrderHelper.set_enumerated_value);
|
||||
return pvm;
|
||||
}
|
||||
else if (descriptor.FieldType == FieldType.Bool)
|
||||
return mk<bool?, bool>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
else if (descriptor.FieldType == FieldType.Double || descriptor.FieldType == FieldType.Float)
|
||||
return mk<double, double>(descriptor, v => v, v => v, double.NaN);
|
||||
else if (descriptor.FieldType == FieldType.Int32)
|
||||
return mk<int?, int>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
else if (descriptor.FieldType == FieldType.UInt32)
|
||||
return mk<uint?, uint>(descriptor, v => v, v => v.GetValueOrDefault());
|
||||
else if (descriptor.FieldType == FieldType.String)
|
||||
return mk<string, string>(descriptor, v => v, v => v);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
GenericOrderParamVM<TMutable, TView, TModel> mk<TView, TModel>(FieldDescriptor fdesc, Func<TModel, TView> convert, Func<TView, TModel> convert_back, TView default_value = default)
|
||||
{
|
||||
var pvm = new GenericOrderParamVM<TMutable, TView, TModel>(fdesc, convert, convert_back);
|
||||
return attached<TView, GenericOrderParamVM<TMutable, TView, TModel>>(pvm, get_from_param, assign_to_param, default_value);
|
||||
|
||||
TView get_from_param(IMessage param, FieldDescriptor fd) => convert(ConvertUtils.try_convert<TModel>(fdesc.Accessor.GetValue(param)));
|
||||
void assign_to_param(IMessage param, FieldDescriptor fd, TView value) => fdesc.Accessor.SetValue(param, convert_back(value));
|
||||
}
|
||||
|
||||
TParam attached<T, TParam>(TParam pvm, Func<IMessage, FieldDescriptor, T> selector, Action<IMessage, FieldDescriptor, T> assign_to_param, T default_value = default) where TParam : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<T>
|
||||
{
|
||||
add_property<T>(
|
||||
pvm.descriptor.Name,
|
||||
mdef => mdef != null ? selector(mdef, pvm.descriptor) : default_value,
|
||||
(mdef, v) => assign_to_param(mdef, pvm.descriptor, v),
|
||||
() => pvm.value,
|
||||
v => pvm.value = v,
|
||||
pvm.subscribe_value(),
|
||||
default_value);
|
||||
return pvm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,6 @@ namespace mbts.spark {
|
||||
IReadOnlyObservableCollection<IAlgoVM> algos_view { get; }
|
||||
IAlgoVM selected_algo { get; set; }
|
||||
void load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo);
|
||||
mbts.ui.spark.AlgoContentTemplateSelector.OrderKind order_kind {get;}
|
||||
}
|
||||
|
||||
static class OrderTypeVMExtensions {
|
||||
|
||||
@@ -57,14 +57,6 @@ namespace mbts.spark {
|
||||
public static IAlgoVM iceberg(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions) => new IcebergOrderVM(key, display_name, editing_permissions);
|
||||
public static IAlgoVM spreader(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions) => new SpreaderOrderVM(key, display_name, editing_permissions);
|
||||
public static IAlgoVM dynamic(AlgoKey key, string display_name, string dynamic_type, AlgoEditingPermissions editing_permissions) => new DynamicOrderVM(key, dynamic_type, display_name, editing_permissions);
|
||||
public static IAlgoVM splitter(AlgoKey key, string display_name, AlgoEditingPermissions permissions) =>
|
||||
new SplitterAlgoVM(key, display_name, permissions);
|
||||
|
||||
public static IAlgoVM generic(AlgoKey key, string display_name,
|
||||
AlgoType algo_type, AlgoEditingPermissions editing_permissions)
|
||||
{
|
||||
return GenericOrderFactory.create_vm(key, algo_type, display_name, editing_permissions);
|
||||
}
|
||||
|
||||
public static IAlgoVM create(AlgoData def_data, AlgoEditingPermissions editing_permissions = default_editing_permissions, bool is_expanded = false) {
|
||||
if (def_data.value.CustomCase == AlgoType.Composite) {
|
||||
@@ -96,9 +88,8 @@ namespace mbts.spark {
|
||||
case AlgoType.Iceberg: vm = iceberg(key, name, editing_permissions); break;
|
||||
case AlgoType.Spreader: vm = spreader(key, name, editing_permissions); break;
|
||||
case AlgoType.Dynamic: vm = dynamic(key, name, name, editing_permissions); break;
|
||||
case AlgoType.Splitter: vm = splitter(key,name,editing_permissions); break;
|
||||
case AlgoType.None: vm = new DefaultAlgoVM(); break;
|
||||
default: vm = generic(key, name, algo_type, editing_permissions); break;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
vm.is_expanded = is_expanded && vm.can_expand;
|
||||
return vm;
|
||||
@@ -959,8 +950,6 @@ namespace mbts.spark {
|
||||
|
||||
public string id { get; }
|
||||
public string display_name { get; }
|
||||
public mbts.ui.spark .AlgoContentTemplateSelector.OrderKind order_kind =>
|
||||
new mbts.ui.spark.AlgoContentTemplateSelector.OrderKind(display_name, string.Empty);
|
||||
public bool is_enabled { get; set; } = true;
|
||||
|
||||
public bool initialized { get; private set; }
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
using bts.api.orderman;
|
||||
using mbts.ui.shell;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory {
|
||||
class SplitterAlgoVM : OrderTypeVMBase<SplitterOrderDefinition, SplitterOrderDefinitionImmutable>
|
||||
{
|
||||
readonly Property<uint> _clip, _exposed;
|
||||
|
||||
public SplitterAlgoVM(AlgoKey id, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
: base(id, display_name, editing_permissions)
|
||||
{
|
||||
_clip = add_property<uint>(nameof(max_order_qty), SplitterOrderDefinition.MaxOrderQtyFieldNumber);
|
||||
_exposed = add_property<uint>(nameof(exposed), SplitterOrderDefinition.ExposedQtyFieldNumber);
|
||||
|
||||
}
|
||||
|
||||
public uint max_order_qty { get => _clip.Value; set => _clip.set(value); }
|
||||
public uint exposed { get => _exposed.Value; set => _exposed.set(value); }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,51 +37,6 @@ namespace mbts.spark.filters {
|
||||
_predicates.Add(impl);
|
||||
bool impl(TRow o) => max.CompareTo(selector(o)) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a copy of add_numeric except we do a tick_equals comparsion
|
||||
// if the ticks are not equals, then it falls back to a regular double comparison
|
||||
//
|
||||
// if the SecurityImmutable? doesn't have a value
|
||||
// then we fallback to double comparison
|
||||
public void add_price(DoubleMinMaxState value, Func<TRow, double> selector, Func<TRow, SecurityImmutable?> si_selector) {
|
||||
if (value == null)
|
||||
return;
|
||||
if (value.minimum.active) {
|
||||
var min = value.minimum.value.Value;
|
||||
if (value.maximum.active) {
|
||||
var max = value.maximum.value.Value;
|
||||
_predicates.Add(impl);
|
||||
bool impl(TRow o) {
|
||||
var v = selector(o);
|
||||
var si = si_selector(o);
|
||||
if (si.HasValue) {
|
||||
return si.Value.ContractSpec.compare_tick_fallback_to_double(min, v) <= 0 &&
|
||||
si.Value.ContractSpec.compare_tick_fallback_to_double(max, v) >= 0;
|
||||
}
|
||||
return min.CompareTo(v) <= 0 && max.CompareTo(v) >= 0;
|
||||
}
|
||||
} else {
|
||||
_predicates.Add(impl);
|
||||
bool impl(TRow o) {
|
||||
var si = si_selector(o);
|
||||
if (si.HasValue) {
|
||||
return si.Value.ContractSpec.compare_tick_fallback_to_double(min, selector(o)) <= 0;
|
||||
}
|
||||
return min.CompareTo(selector(o)) <= 0;
|
||||
}
|
||||
}
|
||||
} else if (value.maximum.active) {
|
||||
var max = value.maximum.value.Value;
|
||||
_predicates.Add(impl);
|
||||
bool impl(TRow o) {
|
||||
var si = si_selector(o);
|
||||
if (si.HasValue) {
|
||||
return si.Value.ContractSpec.compare_tick_fallback_to_double(max, selector(o)) >= 0;
|
||||
}
|
||||
return max.CompareTo(selector(o)) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void add_regex(string str, Func<TRow, string> selector) {
|
||||
|
||||
@@ -505,11 +505,8 @@
|
||||
<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}"/>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal">
|
||||
<TextBlock>Ticks Off</TextBlock>
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="Maximum distance the order can be from the top of the book or 0 for any distance"/>
|
||||
</StackPanel>
|
||||
<c:TickUpDownUInt Grid.Row="2" Grid.Column="2" Value="{Binding ticks_off}" />
|
||||
<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="4"
|
||||
@@ -1014,37 +1011,6 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoContentTemplateSelector.SpreaderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.SplitterTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Expose Quantity</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding exposed}"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Max Order Quantity</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="1" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding max_order_qty}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="3"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
SelectedItem="{Binding children_vm.child.selected_algo}"
|
||||
ContentTemplateSelector="{DynamicResource AlgoTemplateSelector}"
|
||||
OrderKind="{x:Static usp:AlgoContentTemplateSelector.ChildKind}"
|
||||
OrderKindTemplate="{StaticResource OrderKindTemplate}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoContentTemplateSelector.SplitterTemplate>
|
||||
<usp:AlgoContentTemplateSelector.DynamicOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Grid.IsSharedSizeScope="True">
|
||||
@@ -1171,30 +1137,6 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.EnumTemplate>
|
||||
<usp:DynamicOrderParamTemplateSelector.QuantityMeasureTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<uag:QuantityMeasureControl
|
||||
Grid.Column="2" Focusable="False" HorizontalAlignment="Stretch"
|
||||
QuantityMeasure="{Binding quantity, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<uag:QuantityMeasureControl.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</uag:QuantityMeasureControl.CommandBindings>
|
||||
<uag:QuantityMeasureControl.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</uag:QuantityMeasureControl.InputBindings>
|
||||
</uag:QuantityMeasureControl>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.QuantityMeasureTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector>
|
||||
</ItemsControl.ItemTemplateSelector>
|
||||
</ItemsControl>
|
||||
@@ -1215,175 +1157,5 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoContentTemplateSelector.DynamicOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.GenericOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Grid.IsSharedSizeScope="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ItemsControl Grid.ColumnSpan="3" ItemsSource="{Binding parameters}" IsTabStop="False">
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style>
|
||||
<Setter Property="Control.Margin" Value="{StaticResource Margin.TP1}"/>
|
||||
</Style>
|
||||
</ItemsControl.ItemContainerStyle>
|
||||
<ItemsControl.ItemTemplateSelector>
|
||||
<usp:GenericOrderParamTemplateSelector>
|
||||
<usp:GenericOrderParamTemplateSelector.BoolTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<CheckBox
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
IsChecked="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector.BoolTemplate>
|
||||
<usp:GenericOrderParamTemplateSelector.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<c:TickUpDownDouble
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector.DoubleTemplate>
|
||||
<usp:GenericOrderParamTemplateSelector.IntTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<c:TickUpDownInt
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector.IntTemplate>
|
||||
<usp:GenericOrderParamTemplateSelector.UIntTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<c:TickUpDownUInt
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector.UIntTemplate>
|
||||
<usp:GenericOrderParamTemplateSelector.StringTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<TextBox
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Text="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector.StringTemplate>
|
||||
<usp:GenericOrderParamTemplateSelector.EnumTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<ComboBox
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
ItemsSource="{Binding elements}"
|
||||
SelectedItem="{Binding selected_element}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector.EnumTemplate>
|
||||
<usp:GenericOrderParamTemplateSelector.QuantityMeasureTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<uag:QuantityMeasureControl
|
||||
Grid.Column="2" Focusable="False" HorizontalAlignment="Stretch"
|
||||
QuantityMeasure="{Binding quantity, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
|
||||
<uag:QuantityMeasureControl.CommandBindings>
|
||||
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
|
||||
</uag:QuantityMeasureControl.CommandBindings>
|
||||
<uag:QuantityMeasureControl.InputBindings>
|
||||
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
|
||||
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
|
||||
</uag:QuantityMeasureControl.InputBindings>
|
||||
</uag:QuantityMeasureControl>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector.QuantityMeasureTemplate>
|
||||
</usp:GenericOrderParamTemplateSelector>
|
||||
</ItemsControl.ItemTemplateSelector>
|
||||
</ItemsControl>
|
||||
<ItemsControl Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding child_algo_selectors_view}" IsTabStop="False">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<uag:AlgoPickerNodeView
|
||||
ItemsSource="{Binding algos_view}"
|
||||
SelectedItem="{Binding selected_algo}"
|
||||
CanEditStructure="{Binding DataContext.editing_permissions, Converter={StaticResource CanEditStructureConverter}, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
|
||||
ContentTemplateSelector="{DynamicResource AlgoTemplateSelector}"
|
||||
OrderKind="{Binding order_kind}"
|
||||
OrderKindTemplate="{StaticResource OrderKindTemplate}"
|
||||
/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoContentTemplateSelector.GenericOrderTemplate>
|
||||
</usp:AlgoContentTemplateSelector>
|
||||
</ResourceDictionary>
|
||||
@@ -43,10 +43,8 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate GLBXIcebergOrderTemplate { get; set; }
|
||||
public DataTemplate IcebergOrderTemplate { get; set; }
|
||||
public DataTemplate SpreaderTemplate { get; set; }
|
||||
public DataTemplate SplitterTemplate { get; set; }
|
||||
public DataTemplate DynamicOrderTemplate { get; set; }
|
||||
public DataTemplate DefaultOrderTemplate { get; set; }
|
||||
public DataTemplate GenericOrderTemplate { get; set; }
|
||||
|
||||
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
||||
if (item is IDynamicAlgoVMImpl) {
|
||||
@@ -68,9 +66,7 @@ namespace mbts.ui.spark {
|
||||
case CustomOrderDefinition.CustomOneofCase.GlbxIceberg: return GLBXIcebergOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: return SpreaderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Splitter: return SplitterTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Dynamic: return DynamicOrderTemplate;
|
||||
default: return GenericOrderTemplate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -1,265 +1,143 @@
|
||||
<Window
|
||||
x:Class="mbts.ui.shell.LoginView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="clr-namespace:mbts.ui"
|
||||
xmlns:uicore="clr-namespace:mbts.ui"
|
||||
xmlns:shell="clr-namespace:mbts.ui.shell"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:converters="clr-namespace:mbts.ui.converters"
|
||||
xmlns:autocomplete="clr-namespace:WPFTextBoxAutoComplete;assembly=WPFTextBoxAutoComplete"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
x:Name="_login_view"
|
||||
Width="460"
|
||||
Height="254"
|
||||
WindowStyle="None"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
MouseDown="Window_MouseDown"
|
||||
>
|
||||
<ui:StateManager.Groups>
|
||||
<ui:StateGroupsManager>
|
||||
<ui:StateDefinitionCollection>
|
||||
<ui:StateDefinition State="{Binding DataContext.login_status, Source={ui:DirectReference _login_view}, Converter={x:Static shell:LoginView.LoginStatusVisualStateConverter}}"/>
|
||||
<ui:StateDefinition State="{Binding DataContext.SelectedWorkspace, Source={ui:DirectReference _login_view}, Converter={x:Static shell:LoginView.WorkspaceVisualStateConverter}}"/>
|
||||
</ui:StateDefinitionCollection>
|
||||
</ui:StateGroupsManager>
|
||||
</ui:StateManager.Groups>
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<sys:Double x:Key="LoginControlPaddingLeft">40</sys:Double>
|
||||
<sys:Double x:Key="LoginControlPaddingTop">10</sys:Double>
|
||||
<sys:Double x:Key="LoginControlPaddingRight">22</sys:Double>
|
||||
<converters:BooleanToVisibilityConverter x:Key="InverseBoolToVisibility" VisibilityForFalse="Visible" VisibilityForTrue="Collapsed"/>
|
||||
<converters:StringToBrushConverter x:Key="ErrorStringToBrush"/>
|
||||
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" VisibilityForNull="Hidden"/>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Class="mbts.ui.shell.LoginView"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ui="clr-namespace:mbts.ui"
|
||||
xmlns:uicore="clr-namespace:mbts.ui"
|
||||
xmlns:converters="clr-namespace:mbts.ui.converters"
|
||||
xmlns:autocomplete="clr-namespace:WPFTextBoxAutoComplete;assembly=WPFTextBoxAutoComplete"
|
||||
mc:Ignorable="d" MouseDown="Window_MouseDown"
|
||||
Width="500" Height="309" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
|
||||
ui:StateManager.VisualState="{Binding VisualState}">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:BooleanToVisibilityConverter x:Key="InverseBoolToVisibility" VisibilityForFalse="Visible" VisibilityForTrue="Collapsed" />
|
||||
<converters:StringToBrushConverter x:Key="ErrorStringToBrush" />
|
||||
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" VisibilityForNull="Hidden" />
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
|
||||
<Window.Background>
|
||||
<StaticResource ResourceKey="CommonBackground"/>
|
||||
</Window.Background>
|
||||
<Window.Background>
|
||||
<StaticResource ResourceKey="CommonBackground"/>
|
||||
</Window.Background>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
|
||||
<VisualState Name="{x:Static Initializing, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}"/>
|
||||
<VisualState Name="{x:Static Ready, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5"/>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static Error, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_error_control" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimation Storyboard.TargetName="_error_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static Loading, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_loading_control" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimation Storyboard.TargetName="_loading_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" BeginTime="0:0:0.5"/>
|
||||
<DoubleAnimation Storyboard.TargetName="_login_button" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
|
||||
<DoubleAnimation Storyboard.TargetName="_cancel_button" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_dialog_skirt" Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_dialog_skirt" Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static Rejected, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_username" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Red}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_password" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Red}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_reject_message" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={ui:Nested shell:LoginView.VisualStateNames.Workspace}}">
|
||||
<VisualState Name="{x:Static Active, MemberType={ui:Nested shell:LoginView.VisualStateNames.Workspace}}"/>
|
||||
<VisualState Name="{x:Static Inactive, MemberType={ui:Nested shell:LoginView.VisualStateNames.Workspace}}">
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_resume_workspace_checkbox" Storyboard.TargetProperty="(ToggleButton.IsChecked)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_resume_workspace_checkbox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="LoginState">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" />
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="IsHitTestVisible">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="ErrorState">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="_error_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Loading">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5" />
|
||||
<DoubleAnimation Storyboard.TargetName="_loading_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" BeginTime="0:0:0.5" />
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="login_button" Storyboard.TargetProperty="IsEnabled" >
|
||||
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False" />
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="cancel_button" Storyboard.TargetProperty="IsEnabled" >
|
||||
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False" />
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="LoginError">
|
||||
<VisualState x:Name="BadPassword">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_password" Storyboard.TargetProperty="BorderBrush" >
|
||||
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Red}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Border BorderBrush="{x:Static uicore:BTSColors.form_border_brush}" BorderThickness="1">
|
||||
<Grid ClipToBounds="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border BorderBrush="{x:Static uicore:BTSColors.form_border_brush}" BorderThickness="1">
|
||||
<Grid ClipToBounds="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Login details -->
|
||||
<Grid
|
||||
x:Name="_login_controls"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Opacity="0"
|
||||
IsHitTestVisible="False"
|
||||
>
|
||||
<Grid.Margin>
|
||||
<Thickness Left="{StaticResource LoginControlPaddingLeft}" Top="30" Right="{StaticResource LoginControlPaddingRight}"/>
|
||||
</Grid.Margin>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
|
||||
<!-- User -->
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
|
||||
<!-- Workspace -->
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
|
||||
<!-- Pass -->
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
|
||||
<!-- message -->
|
||||
<RowDefinition/>
|
||||
<!-- buttons -->
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Login details -->
|
||||
<Grid Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="_login_controls" Opacity="0" IsHitTestVisible="False">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition /> <!-- User -->
|
||||
<RowDefinition /> <!-- Workspace -->
|
||||
<RowDefinition /> <!-- Pass -->
|
||||
<RowDefinition Height="Auto" /> <!-- message -->
|
||||
<RowDefinition /><!-- buttons -->
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
x:Name="_username"
|
||||
autocomplete:AutoCompleteBehavior.AutoCompleteItemsSource="{Binding KnownUsers}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
|
||||
>
|
||||
</TextBox>
|
||||
<!--Password-->
|
||||
<PasswordBox
|
||||
x:Name="_password"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Style="{DynamicResource PasswordBoxStyle1}"
|
||||
PasswordChanged="_passwordChanged"
|
||||
>
|
||||
<PasswordBox.BorderBrush>
|
||||
<LinearGradientBrush EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
|
||||
<GradientStop Color="#ABADB3" Offset="0.05"/>
|
||||
<GradientStop Color="#E2E3EA" Offset="0.07"/>
|
||||
<GradientStop Color="#E3E9EF" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</PasswordBox.BorderBrush>
|
||||
</PasswordBox>
|
||||
<TextBlock
|
||||
x:Name="_pw_mark"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Margin="4,0,0,0"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0.5"
|
||||
Text="Password"
|
||||
Visibility="{Binding IsFocused, Converter={StaticResource InverseBoolToVisibility}, ElementName=_password}"
|
||||
/>
|
||||
<TextBox
|
||||
Grid.ColumnSpan="2"
|
||||
autocomplete:AutoCompleteBehavior.AutoCompleteItemsSource="{Binding KnownUsers}"
|
||||
Margin="10"
|
||||
Width="160"
|
||||
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
<ComboBox ItemsSource="{Binding Workspaces}" Grid.Row="1" Grid.ColumnSpan="2" Margin="10" SelectedItem="{Binding SelectedWorkspace, Mode=TwoWay}" Width="160" MaxWidth="160" />
|
||||
|
||||
<CheckBox x:Name="_resume_workspace_checkbox" Grid.Row="4" HorizontalAlignment="Left" IsChecked="{Binding resume_workspace}" IsEnabled="True" ToolTip="{Binding SelectedWorkspace}">
|
||||
Load last workspace
|
||||
</CheckBox>
|
||||
<PasswordBox Grid.Row="2" Margin="10" MinWidth="120" Grid.ColumnSpan="2" x:Name="_password" PasswordChanged="_passwordChanged" Style="{DynamicResource PasswordBoxStyle1}" Width="160">
|
||||
<PasswordBox.BorderBrush>
|
||||
<LinearGradientBrush EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
|
||||
<GradientStop Color="#ABADB3" Offset="0.05"/>
|
||||
<GradientStop Color="#E2E3EA" Offset="0.07"/>
|
||||
<GradientStop Color="#E3E9EF" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</PasswordBox.BorderBrush>
|
||||
</PasswordBox>
|
||||
<TextBlock Text="Password" Grid.Row="2" Grid.ColumnSpan="2" Margin="15,10,10,10" Opacity="0.5"
|
||||
IsHitTestVisible="False" HorizontalAlignment="Left" x:Name="_pw_mark"
|
||||
Visibility="{Binding IsFocused, Converter={StaticResource InverseBoolToVisibility}, ElementName=_password}" />
|
||||
|
||||
<TextBlock
|
||||
x:Name="_reject_message"
|
||||
Grid.Row="6"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding WarningMessage}"
|
||||
Foreground="Firebrick"
|
||||
FontWeight="Bold"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Hidden"
|
||||
/>
|
||||
<Button Content="Cancel" x:Name="cancel_button" Grid.Row="4" Grid.Column="0" MinWidth="60" Margin="10" IsCancel="True" Command="{Binding CancelCommand}" />
|
||||
<Button Content="Login" x:Name="login_button" Grid.Row="4" Grid.Column="1" MinWidth="60" Margin="10" IsDefault="True" Command="{Binding LoginCommand}" />
|
||||
|
||||
<!-- Background="{StaticResource CommonBackground}"-->
|
||||
<Border Grid.Row="3" Grid.ColumnSpan="2" Visibility="{Binding WarningMessage, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
<TextBlock Text="{Binding WarningMessage}" HorizontalAlignment="Center" Foreground="Firebrick" FontWeight="Bold" TextWrapping="Wrap" Margin="5" MaxWidth="160" />
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- Fatal error display -->
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="_error_control" Margin="10" Opacity="0" Visibility="{Binding ErrorMessage, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
<TextBlock Text="The application is currently unavailable." FontWeight="Bold" TextWrapping="Wrap" Margin="0,0,0,10" />
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
<Run>Please call BTS Support at </Run>
|
||||
<Run FontWeight="Bold">(919) 913-0860</Run>
|
||||
<Run>or email</Run>
|
||||
<Hyperlink CommandParameter="mailto:support@bluetradingsystems.com" Command="{Binding EmailCommand}">support@bluetradingsystems.com</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Error Details:" Margin="0,10,0,0" />
|
||||
<TextBlock Text="{Binding ErrorMessage}" TextWrapping="Wrap" Margin="5" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Loading -->
|
||||
<StackPanel Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="_loading_control" IsHitTestVisible="False" Opacity="0">
|
||||
<ui:BusySpinner Width="64" Height="64" />
|
||||
<TextBlock Text="Loading..." HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- logo -->
|
||||
<Image Grid.ColumnSpan="2" HorizontalAlignment="Left" Source="/Images/Icons/BTS.png" Width="220" Stretch="Uniform" SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="HighQuality" x:Name="_logo" Margin="32,0,0,0" />
|
||||
|
||||
|
||||
<Button Grid.ColumnSpan="2" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5" Command="{Binding CancelCommand}" Style="{StaticResource RoundCloseButton}" />
|
||||
</Grid>
|
||||
<!-- Fatal error display -->
|
||||
<StackPanel
|
||||
x:Name="_error_control"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Visibility="Hidden"
|
||||
Opacity="0"
|
||||
>
|
||||
<TextBlock Text="The application is currently unavailable." FontWeight="Bold" TextWrapping="Wrap" Margin="0,0,0,10" />
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
<Run>Please call BTS Support at </Run>
|
||||
<Run FontWeight="Bold">(919) 913-0860</Run>
|
||||
<Run>or email</Run>
|
||||
<Hyperlink CommandParameter="mailto:support@bluetradingsystems.com" Command="{Binding EmailCommand}">support@bluetradingsystems.com</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock Text="Error Details:" Margin="0,10,0,0" />
|
||||
<TextBlock Text="{Binding ErrorMessage}" TextWrapping="Wrap" Margin="5" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Loading -->
|
||||
<StackPanel
|
||||
x:Name="_loading_control"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0"
|
||||
Visibility="Hidden"
|
||||
>
|
||||
<ui:BusySpinner Width="64" Height="64"/>
|
||||
<TextBlock Text="Loading..." HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- logo -->
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Margin="32,0,0,0"
|
||||
Width="220"
|
||||
Source="/Images/Icons/BTS.png"
|
||||
Stretch="Uniform"
|
||||
SnapsToDevicePixels="True"
|
||||
/>
|
||||
<Button Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5" Command="{Binding CancelCommand}" Style="{StaticResource RoundCloseButton}"/>
|
||||
<c:DialogSkirt x:Name="_dialog_skirt" Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
|
||||
<c:DialogSkirt.RightItems>
|
||||
<x:Array Type="{x:Type Button}">
|
||||
<Button x:Name="_login_button" Width="60" Margin="10" IsDefault="True" IsHitTestVisible="True" Command="{Binding LoginCommand}">
|
||||
Login
|
||||
</Button>
|
||||
<Button x:Name="_cancel_button" Width="60" Margin="10" IsCancel="True" IsHitTestVisible="True" Command="{Binding CancelCommand}">
|
||||
Cancel
|
||||
</Button>
|
||||
</x:Array>
|
||||
</c:DialogSkirt.RightItems>
|
||||
</c:DialogSkirt>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -1,43 +1,15 @@
|
||||
using bts;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.native;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media.Imaging;
|
||||
using mbts.ui.native;
|
||||
using bts;
|
||||
|
||||
namespace mbts.ui.shell {
|
||||
public partial class LoginView : Window {
|
||||
public enum LoginStatus {
|
||||
Initializing,
|
||||
Ready,
|
||||
Error,
|
||||
Rejected,
|
||||
LoggingIn
|
||||
}
|
||||
|
||||
public static IValueConverter WorkspaceVisualStateConverter =>
|
||||
ValueConverter.create<string, string>(workspace =>
|
||||
!string.IsNullOrEmpty(workspace) && workspace != LoginViewModel.NEW_WORKSPACE_NAME ?
|
||||
VisualStateNames.Workspace.Active :
|
||||
VisualStateNames.Workspace.Inactive);
|
||||
|
||||
public static IValueConverter LoginStatusVisualStateConverter =>
|
||||
ValueConverter.create<LoginStatus, string>(status => {
|
||||
switch (status) {
|
||||
case LoginStatus.Initializing: return VisualStateNames.LoginStatus.Initializing;
|
||||
case LoginStatus.Ready: return VisualStateNames.LoginStatus.Ready;
|
||||
case LoginStatus.LoggingIn: return VisualStateNames.LoginStatus.Loading;
|
||||
case LoginStatus.Rejected: return VisualStateNames.LoginStatus.Rejected;
|
||||
case LoginStatus.Error: return VisualStateNames.LoginStatus.Error;
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(status, nameof(status));
|
||||
}
|
||||
});
|
||||
|
||||
public partial class LoginView : Window
|
||||
{
|
||||
public LoginView(LoginViewModel model) {
|
||||
this.DataContext = model;
|
||||
if (model is IRequestViewClose) {
|
||||
@@ -93,24 +65,6 @@ namespace mbts.ui.shell {
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
public static class VisualStateNames {
|
||||
public static class LoginStatus {
|
||||
public const string GroupName = nameof(LoginStatus);
|
||||
public static readonly string
|
||||
Initializing = $"{GroupName}{nameof(Initializing)}",
|
||||
Ready = $"{GroupName}{nameof(Ready)}",
|
||||
Loading = $"{GroupName}{nameof(Loading)}",
|
||||
Rejected = $"{GroupName}{nameof(Rejected)}",
|
||||
Error = $"{GroupName}{nameof(Error)}";
|
||||
}
|
||||
|
||||
public static class Workspace {
|
||||
public const string GroupName = nameof(Workspace);
|
||||
|
||||
public static readonly string
|
||||
Active = $"{GroupName}{VisualStates.Active}",
|
||||
Inactive = $"{GroupName}{VisualStates.Inactive}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,15 +2,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using LoginStatus = mbts.ui.shell.LoginView.LoginStatus;
|
||||
|
||||
namespace mbts.ui.shell {
|
||||
namespace mbts.ui.shell
|
||||
{
|
||||
public class LoginViewModel : ViewModelBase, IRequestViewClose
|
||||
{
|
||||
public const string NEW_WORKSPACE_NAME = "New workspace...";
|
||||
private LoginModel _model;
|
||||
private Dispatcher _dispatcher;
|
||||
public LoginViewModel(LoginModel model, Dispatcher disp) {
|
||||
@@ -41,7 +42,7 @@ namespace mbts.ui.shell {
|
||||
void _model_Ready() {
|
||||
_dispatcher.BeginInvoke(() => {
|
||||
if (!_model.HasFatalError) {
|
||||
login_status = LoginStatus.Ready;
|
||||
this.VisualState = "LoginState";
|
||||
load_username();
|
||||
if (!string.IsNullOrEmpty(_name))
|
||||
KnownUsers.Add(_name);
|
||||
@@ -49,21 +50,16 @@ namespace mbts.ui.shell {
|
||||
}
|
||||
else {
|
||||
ErrorMessage = _model.ErrorMessage;
|
||||
login_status = LoginStatus.Error;
|
||||
VisualState = "ErrorState";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
LoginStatus _login_status;
|
||||
public LoginStatus login_status { get => _login_status; private set => Set(ref _login_status, value); }
|
||||
private string _vstate = "Normal";
|
||||
public string VisualState { get { return _vstate; } set { _vstate = value; RaisePropertyChanged("VisualState"); } }
|
||||
|
||||
/// <see cref="KnownUsers"/> should only contain the user-name of the last login (if none exists, it should be empty).
|
||||
public ObservableCollection<string> KnownUsers { get; } = new ObservableCollection<string>();
|
||||
bool _resume_workspace = true;
|
||||
public bool resume_workspace {
|
||||
get => _resume_workspace;
|
||||
set => Set(ref _resume_workspace, value);
|
||||
}
|
||||
private string _name;
|
||||
public string Name {
|
||||
get { return _name; }
|
||||
@@ -86,7 +82,7 @@ namespace mbts.ui.shell {
|
||||
public string SelectedWorkspace { get; set; }
|
||||
private async void update_workspaces(string name) {
|
||||
Workspaces.Clear();
|
||||
Workspaces.Add(NEW_WORKSPACE_NAME);
|
||||
Workspaces.Add("New workspace...");
|
||||
SelectedWorkspace = Workspaces.First();
|
||||
if (_model.try_get_user(name, out var user)) {
|
||||
var to_add = await _model.workspaces_for(user);
|
||||
@@ -140,13 +136,15 @@ namespace mbts.ui.shell {
|
||||
|
||||
private async void OnLogin(object arg) {
|
||||
save_selection();
|
||||
string selected_workspace = _resume_workspace && SelectedWorkspace != NEW_WORKSPACE_NAME ?
|
||||
SelectedWorkspace : null;
|
||||
string selected_workspace = SelectedWorkspace;
|
||||
if (selected_workspace == "New workspace...") {
|
||||
selected_workspace = null;
|
||||
}
|
||||
var result = await _model.do_login(Name, selected_workspace, Password);
|
||||
if (result.success) { login_status = LoginStatus.LoggingIn; }
|
||||
if (result.success) { VisualState = "Loading"; }
|
||||
if (!result.success && !string.IsNullOrEmpty(result.reason)) {
|
||||
WarningMessage = result.reason;
|
||||
login_status = LoginStatus.Rejected;
|
||||
VisualState = "BadPassword";
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(result.reason)) {
|
||||
// Login was successful but we have a message for the user
|
||||
|
||||
@@ -128,9 +128,6 @@ namespace mbts.spark {
|
||||
public static IEqualityComparer<double> tick_equality_comparer(this ContractSpecificationImmutable contract) =>
|
||||
EqualityComparer.Create((double a, double b) => tick_equals(contract, a, b), x => MathUtils.RoundFractionalUnits(x, contract.LowTick));
|
||||
|
||||
public static int compare_tick_fallback_to_double(this ContractSpecificationImmutable contract, double x, double y) =>
|
||||
tick_equals(contract, x, y) ? 0 : x.CompareTo(y);
|
||||
|
||||
public static double round_to_tick(this ContractSpecificationImmutable spec, double value) {
|
||||
double intpart = (long)value;
|
||||
double floatpart = value % 1;
|
||||
|
||||
@@ -253,18 +253,15 @@ namespace mbts.ui {
|
||||
sd.StateChanged += on_state_changed;
|
||||
return Disposable.Create(() => sd.StateChanged -= on_state_changed);
|
||||
void on_state_changed(string oldval, string newval) => transition(newval);
|
||||
void transition(string state) {
|
||||
async void transition(string state) {
|
||||
version++;
|
||||
if (target.IsLoaded)
|
||||
StateManager.GoToState(target, state);
|
||||
else {
|
||||
int v = version;
|
||||
target.Loaded += on_loaded;
|
||||
void on_loaded(object sender, RoutedEventArgs e) {
|
||||
target.Loaded -= on_loaded;
|
||||
if (v == version)
|
||||
StateManager.GoToState(target, state);
|
||||
}
|
||||
await target.LoadedAsync();
|
||||
if (v == version)
|
||||
StateManager.GoToState(target, state);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -234,8 +234,6 @@ namespace bts.md
|
||||
sec = find_security(filter);
|
||||
if (sec == null)
|
||||
await Task.Delay(500);
|
||||
if(src.IsCancellationRequested)
|
||||
break;
|
||||
}
|
||||
while (sec == null);
|
||||
return sec;
|
||||
|
||||
Binary file not shown.
@@ -36,18 +36,15 @@ namespace bts.excel
|
||||
int open = sn.IndexOf('[');
|
||||
int close = sn.IndexOf(']', open + 1);
|
||||
var ssn = sn.Substring(0, open) + sn.Substring(close + 1);
|
||||
ssn = stringize_workbook(ssn);
|
||||
return ssn;
|
||||
}
|
||||
|
||||
public static string stringize_workbook(string ssn)
|
||||
{
|
||||
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>
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
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 IParameters
|
||||
{
|
||||
IReadOnlyDictionary<string, string> algo_params { get; }
|
||||
void register_algo_parameter(string type, string name);
|
||||
void unregister_algo_parameter(string name);
|
||||
|
||||
IReadOnlyDictionary<string, string> special_params { get; }
|
||||
void register_special_parameter(string alias, string name);
|
||||
void unregister_special_parameter(string name);
|
||||
}
|
||||
|
||||
public interface IDefinitionVisitor
|
||||
{
|
||||
void add_def(string def);
|
||||
void map_def(string def, string mapped);
|
||||
List<string> get_defs();
|
||||
|
||||
|
||||
IReadOnlyDictionary<string, string> order_setups {get;}
|
||||
void register_order_setup(string setup, string attach);
|
||||
|
||||
void add_dual_definition(string code);
|
||||
List<string> get_dual_definitions();
|
||||
|
||||
void add_dual_impl(string code);
|
||||
List<string> get_dual_impls();
|
||||
|
||||
void register_algo_parameter(string type, string name);
|
||||
}
|
||||
|
||||
public class CodeParts : IDefinitionVisitor, IParameters
|
||||
{
|
||||
List<string> _defs = new List<string>();
|
||||
ConcurrentDictionary<string, string> _def_map = new ConcurrentDictionary<string, string>();
|
||||
ConcurrentDictionary<string, string> _order_setups = new ConcurrentDictionary<string, string>();
|
||||
List<string> _dual_defs = new List<string>();
|
||||
List<string> _dual_blocks = new List<string>();
|
||||
|
||||
ConcurrentDictionary<string, string> _algo_params = new ConcurrentDictionary<string, string>();
|
||||
ConcurrentDictionary<string, string> _special_params = new ConcurrentDictionary<string, string>();
|
||||
|
||||
public CodeParts()
|
||||
{
|
||||
}
|
||||
|
||||
public string function_name { get; set; }
|
||||
public string function_impl { get; set; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, string> order_setups => _order_setups;
|
||||
public void register_order_setup(string setup, string attach)
|
||||
{
|
||||
_order_setups[attach] = setup;
|
||||
}
|
||||
|
||||
public void add_dual_impl(string code)
|
||||
{
|
||||
_dual_blocks.Add(code);
|
||||
}
|
||||
|
||||
public List<string> get_dual_impls()
|
||||
{
|
||||
return _dual_blocks;
|
||||
}
|
||||
|
||||
public void add_dual_definition(string code)
|
||||
{
|
||||
_dual_defs.Add(code);
|
||||
}
|
||||
|
||||
public List<string> get_dual_definitions()
|
||||
{
|
||||
return _dual_defs;
|
||||
}
|
||||
|
||||
|
||||
public IReadOnlyDictionary<string, string> algo_params => _algo_params;
|
||||
public void register_algo_parameter(string type, string name)
|
||||
{
|
||||
_algo_params[name] = string.IsNullOrEmpty(type) ? "double" : type;
|
||||
}
|
||||
public void unregister_algo_parameter(string name)
|
||||
{
|
||||
_algo_params.TryRemove(name, out _);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, string> special_params => _special_params;
|
||||
public void register_special_parameter(string alias, string name)
|
||||
{
|
||||
_special_params[alias] = name;
|
||||
}
|
||||
public void unregister_special_parameter(string name)
|
||||
{
|
||||
_special_params.TryRemove(name, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExcelDna.Integration;
|
||||
using Microsoft.Office.Interop.Excel;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public static class RangeHelper
|
||||
{
|
||||
public static ExcelReference RangeToExcelReference(Range range)
|
||||
|
||||
{
|
||||
|
||||
return new ExcelReference(range.Row - 1,
|
||||
range.Row - 1 + range.Rows.Count - 1,
|
||||
range.Column - 1,
|
||||
range.Column - 1 + range.Columns.Count - 1,
|
||||
range.Worksheet.Name);
|
||||
|
||||
}
|
||||
public static Range ReferenceToRange(ExcelReference xlref)
|
||||
{
|
||||
var app = ExcelDnaUtil.Application as Microsoft.Office.Interop.Excel.Application;
|
||||
var refText = (string)XlCall.Excel(XlCall.xlfReftext, xlref, true);
|
||||
var range = app.Range[refText];
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
||||
// Select an ExcelReference (perhaps on another sheet) allowing changes to be made there.
|
||||
// On clean-up, resets all the selections and the active sheet.
|
||||
// Should not be used if the work you are going to do will switch sheets, amke new sheets etc.
|
||||
public class ExcelSelectionHelper : XlCall, IDisposable
|
||||
{
|
||||
object oldSelectionOnActiveSheet;
|
||||
object oldActiveCellOnActiveSheet;
|
||||
|
||||
object oldSelectionOnRefSheet;
|
||||
object oldActiveCellOnRefSheet;
|
||||
|
||||
public ExcelSelectionHelper(ExcelReference refToSelect)
|
||||
{
|
||||
// Remember old selection state on the active sheet
|
||||
oldSelectionOnActiveSheet = Excel(xlfSelection);
|
||||
oldActiveCellOnActiveSheet = Excel(xlfActiveCell);
|
||||
|
||||
// Switch to the sheet we want to select
|
||||
string refSheet = (string)Excel(xlSheetNm, refToSelect);
|
||||
Excel(xlcWorkbookSelect, new object[] { refSheet });
|
||||
|
||||
// record selection and active cell on the sheet we want to select
|
||||
oldSelectionOnRefSheet = Excel(xlfSelection);
|
||||
oldActiveCellOnRefSheet = Excel(xlfActiveCell);
|
||||
|
||||
// make the selection
|
||||
Excel(xlcFormulaGoto, refToSelect);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Reset the selection on the target sheet
|
||||
Excel(xlcSelect, oldSelectionOnRefSheet, oldActiveCellOnRefSheet);
|
||||
|
||||
// Reset the sheet originally selected
|
||||
string oldActiveSheet = (string)Excel(xlSheetNm, oldSelectionOnActiveSheet);
|
||||
Excel(xlcWorkbookSelect, new object[] { oldActiveSheet });
|
||||
|
||||
// Reset the selection in the active sheet (some bugs make this change sometimes too)
|
||||
Excel(xlcSelect, oldSelectionOnActiveSheet, oldActiveCellOnActiveSheet);
|
||||
}
|
||||
}
|
||||
|
||||
public class ExcelEchoOffHelper : XlCall, IDisposable
|
||||
{
|
||||
object oldEcho;
|
||||
|
||||
public ExcelEchoOffHelper()
|
||||
{
|
||||
oldEcho = Excel(xlfGetWorkspace, 40);
|
||||
Excel(xlcEcho, false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Excel(xlcEcho, oldEcho);
|
||||
}
|
||||
}
|
||||
|
||||
public class ExcelCalculationManualHelper : XlCall, IDisposable
|
||||
{
|
||||
object oldCalculationMode;
|
||||
|
||||
public ExcelCalculationManualHelper()
|
||||
{
|
||||
oldCalculationMode = Excel(xlfGetDocument, 14);
|
||||
Excel(xlcOptionsCalculation, 3);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Excel(xlcOptionsCalculation, oldCalculationMode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using ExcelDna.Integration;
|
||||
using spark_excel.grammar;
|
||||
|
||||
using Antlr4.Runtime;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public class Generator
|
||||
{
|
||||
readonly IParameters _globals = new CodeParts();
|
||||
public IParameters globals => _globals;
|
||||
|
||||
public string bts_generate_algo_class(string algoname, IEnumerable<CodeParts> functions, string[] filepaths = null)
|
||||
{
|
||||
var ass = Assembly.GetExecutingAssembly();
|
||||
var resources = ass.GetManifestResourceNames();
|
||||
var res = $"{ass.GetName().Name}.ExcelAlgoTemplate.cpp";
|
||||
|
||||
using (Stream stream = ass.GetManifestResourceStream(res))
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
string cpp = reader.ReadToEnd();
|
||||
//cpp = cpp.Replace("#define __AlgoName__", "//#define __#AlgoName__");
|
||||
cpp = cpp.Replace("#define __FunctionDefs__", "//#define __#FunctionDefs__");
|
||||
cpp = cpp.Replace("#define __FunctionCalls__", "//#define __#FunctionCalls__");
|
||||
cpp = cpp.Replace("#define __FunctionImpls__", "//#define __#FunctionImpls__");
|
||||
|
||||
cpp = cpp.Replace("#define __DualDefs__", "//#define __#DualDefs__");
|
||||
cpp = cpp.Replace("#define __DualImpls__", "//#define __#DualImpls__");
|
||||
|
||||
cpp = cpp.Replace("#define __ParameterDefs__", "//#define __#ParameterDefs__");
|
||||
cpp = cpp.Replace("#define __ParameterLaunches__", "//#define __#ParameterLaunches__");
|
||||
cpp = cpp.Replace("#define __ParameterUpdates__", "//#define __#ParameterUpdates__");
|
||||
cpp = cpp.Replace("#define __ParameterSetups__", "//#define __#ParameterSetups__");
|
||||
cpp = cpp.Replace("#define __OrderSetups__", "//#define __#OrderSetups__");
|
||||
cpp = cpp.Replace("#define __OrderAttaches__", "//#define __#OrderAttaches__");
|
||||
cpp = cpp.Replace("__Remove__", string.Empty);
|
||||
cpp = cpp.Replace("__AlgoName__", algoname);
|
||||
|
||||
string funcdefs = string.Join(Environment.NewLine, functions.Select(x => $" void {x.function_name}();"));
|
||||
cpp = cpp.Replace("__FunctionDefs__", "//__FunctionDefs__" + Environment.NewLine + funcdefs);
|
||||
|
||||
string funccalls = string.Join(Environment.NewLine, functions.Select(x => " " + x.function_name + "();" ));
|
||||
cpp = cpp.Replace("__FunctionCalls__", "//__FunctionCalls__" + Environment.NewLine + funccalls);
|
||||
|
||||
string funcimpls = string.Join(Environment.NewLine, functions.Select(x => x.function_impl.Replace("__AlgoName__", algoname) ));
|
||||
cpp = cpp.Replace("__FunctionImpls__", "//__FunctionImpls__" + Environment.NewLine + funcimpls);
|
||||
|
||||
string dualdefs = string.Join(Environment.NewLine, functions.SelectMany(x => x.get_dual_definitions().Select( xx => xx.Replace("__AlgoName__::", "") ) ).Distinct());
|
||||
cpp = cpp.Replace("__DualDefs__", "//__DualDefs__" + Environment.NewLine + dualdefs);
|
||||
|
||||
string dualimpls = string.Join(Environment.NewLine, functions.SelectMany(x => x.get_dual_impls().Select(xx => xx.Replace("__AlgoName__", algoname))).Distinct());
|
||||
cpp = cpp.Replace("__DualImpls__", "//__DualImpls__" + Environment.NewLine + dualimpls);
|
||||
|
||||
//__ParameterDefs__
|
||||
string globalparamdefs = string.Join(Environment.NewLine, globals.algo_params.Select( kv => " " + kv.Value + " " + kv.Key + ";"));
|
||||
string localparamdefs = string.Join(Environment.NewLine, functions.SelectMany(x => x.algo_params).Select(kv => " " + kv.Value + " " + kv.Key + ";"));
|
||||
cpp = cpp.Replace("__ParameterDefs__",
|
||||
"//__ParameterDefs__" + Environment.NewLine + globalparamdefs + Environment.NewLine + localparamdefs);
|
||||
|
||||
//__ParameterLaunches__
|
||||
{
|
||||
List<string> launches = new List<string>();
|
||||
foreach (var kv in globals.algo_params)
|
||||
{
|
||||
string launch = $@"
|
||||
auto {kv.Key}_param = params.find(""{kv.Key}"");
|
||||
if ( {kv.Key}_param == params.end() )
|
||||
return OrderOpResult{{ ORDER_ERROR_INVALID_ORDER_DEFINITON,""{kv.Key} parameter must be defined""}};
|
||||
me->{kv.Key} = {kv.Key}_param->second.{get_as(kv.Key, kv.Value)}();
|
||||
";
|
||||
launches.Add(launch);
|
||||
}
|
||||
string paramlaunch = string.Join(Environment.NewLine, launches);
|
||||
cpp = cpp.Replace("__ParameterLaunches__", "//__ParameterLaunches__" + Environment.NewLine + paramlaunch);
|
||||
}
|
||||
|
||||
//__ParameterUpdates__
|
||||
{
|
||||
List<string> updates = new List<string>();
|
||||
foreach(var kv in globals.algo_params)
|
||||
{
|
||||
string update = $@"
|
||||
auto {kv.Key}_param = params.find(""{kv.Key}"");
|
||||
if ( {kv.Key}_param == params.end() )
|
||||
{{
|
||||
algo_services().terminate_error(""Can't find parameter {kv.Key} "");
|
||||
return;
|
||||
}}
|
||||
me->{kv.Key} = {kv.Key}_param->second.{get_as(kv.Key, kv.Value)}();
|
||||
";
|
||||
updates.Add(update);
|
||||
}
|
||||
string paramupdates = string.Join(Environment.NewLine, updates);
|
||||
cpp = cpp.Replace("__ParameterUpdates__", "//__ParameterUpdates__" + Environment.NewLine + paramupdates);
|
||||
}
|
||||
|
||||
//__ParameterSetups__
|
||||
{
|
||||
List<string> setups = new List<string>();
|
||||
setups.Add($@"
|
||||
DynamicOrderDescriptor algo_desc;
|
||||
algo_desc.set_dynamic_order_type(""{algoname}"");
|
||||
");
|
||||
foreach (var kv in globals.algo_params)
|
||||
{
|
||||
string setup = $@"
|
||||
auto {kv.Key}_param = algo_desc.add_param_desc();
|
||||
{kv.Key}_param->set_param_name(""{kv.Key}"");
|
||||
{kv.Key}_param->mutable_param_type()->set_primitive({get_param_type_enum(kv.Key, kv.Value)});
|
||||
";
|
||||
setups.Add(setup);
|
||||
}
|
||||
setups.Add($@"
|
||||
serv.register_dynamic_algo_order(algo_desc, []() {{ return std::make_unique<{algoname}>(); }});
|
||||
");
|
||||
string paramsetups = string.Join(Environment.NewLine, setups);
|
||||
cpp = cpp.Replace("__ParameterSetups__", "//__ParameterSetups__" + Environment.NewLine + paramsetups);
|
||||
}
|
||||
|
||||
//__OrderSetups__
|
||||
{
|
||||
var setups = functions.SelectMany(x => x.order_setups.Values.SelectMany(xx => xx.Split(new[]{'\r','\n'})));
|
||||
string ordersetups = string.Join(Environment.NewLine, setups.Distinct());
|
||||
cpp = cpp.Replace("__OrderSetups__", "//__OrderSetups__" + Environment.NewLine + ordersetups);
|
||||
}
|
||||
//__OrderAttaches__
|
||||
{
|
||||
var attach = functions.SelectMany(x => x.order_setups.Keys);
|
||||
string orderattach = string.Join(Environment.NewLine, attach);
|
||||
cpp = cpp.Replace("__OrderAttaches__", "//__OrderAttaches__" + Environment.NewLine + orderattach);
|
||||
}
|
||||
|
||||
string cppname = string.Empty;
|
||||
if(filepaths != null && filepaths.Length > 0)
|
||||
try_write_file_fullpath(filepaths, cpp);
|
||||
else
|
||||
try_write_file(algoname + ".cpp", cpp);
|
||||
return cppname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static private string get_as(string paramname, string paramtype)
|
||||
{
|
||||
switch (paramtype.ToLower())
|
||||
{
|
||||
case "int":
|
||||
return "as_int32";
|
||||
case "uint":
|
||||
return "as_uint32";
|
||||
case "double":
|
||||
return "as_double";
|
||||
case "string":
|
||||
return "as_string";
|
||||
case "bool":
|
||||
return "as_bool";
|
||||
case "order":
|
||||
case "orderdef":
|
||||
return "as_order_def";
|
||||
default:
|
||||
throw new ArgumentException("unknown type " + paramname + " " + paramtype);
|
||||
}
|
||||
}
|
||||
|
||||
static private string get_param_type_enum(string paramname, string paramtype)
|
||||
{
|
||||
switch(paramtype.ToLower())
|
||||
{
|
||||
case "int":
|
||||
return "DYNAMIC_ORDER_PRIMITIVE_INT";
|
||||
case "uint":
|
||||
return "DYNAMIC_ORDER_PRIMITIVE_UINT";
|
||||
case "double":
|
||||
return "DYNAMIC_ORDER_PRIMITIVE_DOUBLE";
|
||||
case "string":
|
||||
return "DYNAMIC_ORDER_PRIMITIVE_STRING";
|
||||
case "bool":
|
||||
return "DYNAMIC_ORDER_PRIMITIVE_BOOL";
|
||||
case "order":
|
||||
case "orderdef":
|
||||
return "DYNAMIC_ORDER_PRIMITIVE_ORDER_DEF";
|
||||
default:
|
||||
throw new ArgumentException("unknown type " + paramname + " " + paramtype);
|
||||
}
|
||||
}
|
||||
|
||||
//[ExcelFunction(IsMacroType = true, IsThreadSafe = false)]
|
||||
//we can't call this as a UDF or the array / selectspecial thing doesn't always work
|
||||
public CodeParts bts_generate_function_code(ExcelReference rref, ref string function_name)
|
||||
{
|
||||
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)
|
||||
{
|
||||
try_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);
|
||||
}
|
||||
|
||||
CodeParts defs = new CodeParts();
|
||||
GeneratorVisitor visitor = new GeneratorVisitor(rref, defs, globals);
|
||||
object val = visitor.VisitMain(parsetree);
|
||||
|
||||
if(string.IsNullOrEmpty(function_name))
|
||||
function_name = new CellRef(rref).to_name();
|
||||
|
||||
string ret = string.Empty;
|
||||
ret += Environment.NewLine;
|
||||
ret += $"void __AlgoName__::{function_name}() {Environment.NewLine}{{{Environment.NewLine}";
|
||||
defs.get_defs().ForEach(x => ret += " " + x + Environment.NewLine);
|
||||
ret += " " + val.ToString() + " ;";
|
||||
ret += $"{Environment.NewLine} }}";
|
||||
defs.function_impl = ret;
|
||||
defs.function_name = function_name;
|
||||
try_write_file(function_name + ".function", ret);
|
||||
return defs;
|
||||
}
|
||||
|
||||
static string try_write_file(string filename, string text)
|
||||
{
|
||||
string fname = string.Empty;
|
||||
try
|
||||
{
|
||||
fname = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\{filename}";
|
||||
using(System.IO.StreamWriter file = new System.IO.StreamWriter(fname))
|
||||
{
|
||||
file.WriteLine(text);
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
return fname;
|
||||
}
|
||||
|
||||
static void try_write_file_fullpath(string[] filename, string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach(string fname in filename)
|
||||
{
|
||||
using (System.IO.StreamWriter file = new System.IO.StreamWriter(fname))
|
||||
{
|
||||
file.WriteLine(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,35 +10,54 @@ using bts.excel.extensions;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
|
||||
public class GeneratorVisitor : ExcelFormulaBaseVisitor<object>
|
||||
public interface IDefinitionVisitor
|
||||
{
|
||||
void add_def(string def);
|
||||
List<string> get_defs();
|
||||
}
|
||||
|
||||
|
||||
public class GeneratorVisitor : ExcelFormulaBaseVisitor<object>, IDefinitionVisitor
|
||||
{
|
||||
ExcelReference _orig_ref, _current_ref;
|
||||
functions.lib.FunctionLibrary _library;
|
||||
ExcelReference _ref;
|
||||
functions.lib.FunctionLibrary _library = new functions.lib.FunctionLibrary();
|
||||
ExcelFormulaErrorListener _error_listener = new ExcelFormulaErrorListener();
|
||||
IDefinitionVisitor _defs;
|
||||
IParameters _params;
|
||||
string _document_name;
|
||||
public GeneratorVisitor(ExcelReference rref, IDefinitionVisitor defs, IParameters parameters, bool include_setups = true)
|
||||
List<string> _defs = new List<string>();
|
||||
Dictionary<string, string> _def_map = new Dictionary<string, string>();
|
||||
|
||||
|
||||
public GeneratorVisitor(ExcelReference rref)
|
||||
{
|
||||
_current_ref = _orig_ref = rref;
|
||||
_library = new functions.lib.FunctionLibrary(include_setups);
|
||||
_defs = defs;
|
||||
_params = parameters;
|
||||
|
||||
var cref = new CellRef(rref);
|
||||
_document_name = (string)XlCall.Excel(XlCall.xlfGetDocument, 88, cref.SheetName);
|
||||
}
|
||||
|
||||
|
||||
#region Context visitors overrides
|
||||
|
||||
public override object VisitBoolean(ExcelFormulaParser.BooleanContext context)
|
||||
{
|
||||
var expr = context.BOOLEAN();
|
||||
return expr.ToString().ToLower();
|
||||
_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();
|
||||
@@ -52,12 +71,56 @@ namespace bts.excel
|
||||
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, _orig_ref.GetSheetName());
|
||||
process_cell(rref, out _);
|
||||
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);
|
||||
@@ -146,12 +209,14 @@ namespace bts.excel
|
||||
{
|
||||
var name = context.name.Text;
|
||||
|
||||
ExcelReference current = _orig_ref;
|
||||
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, _defs, current);
|
||||
return _library.call_function(name, args, this);
|
||||
}
|
||||
|
||||
public override object VisitParenthesis(ExcelFormulaParser.ParenthesisContext context)
|
||||
@@ -223,47 +288,21 @@ namespace bts.excel
|
||||
|
||||
if(rng.IsOneCell())
|
||||
{
|
||||
var rref = new ExcelReference(row - 1, row - 1, column - 1, column - 1, rng.SheetName);
|
||||
process_cell(rref, out _);
|
||||
var rref = new ExcelReference(row - 1, row - 1, column - 1, column - 1, start.SheetName);
|
||||
process_cell(rref);
|
||||
}
|
||||
else
|
||||
{
|
||||
var initdef = $@"
|
||||
auto range_init = [](auto& initval, int nrows, int ncols)
|
||||
{{
|
||||
Range< std::decay_t<decltype((initval))> > rng(nrows, ncols);
|
||||
rng(0,0) = initval;
|
||||
return rng;
|
||||
}};";
|
||||
_defs.add_def(initdef);
|
||||
var loaddef = $@"
|
||||
auto range_set = [](auto& range, auto& val, int r, int c)
|
||||
{{
|
||||
range(r,c) = val;
|
||||
}};";
|
||||
_defs.add_def(loaddef);
|
||||
var vec = rng.get_all_cells();
|
||||
if(vec.Count > 0)
|
||||
{
|
||||
for(int i = 0; i < vec.Count; i++)
|
||||
{
|
||||
int r = (vec[i].Row-1) - (vec[0].Row-1);
|
||||
int c = (vec[i].Column-1) - (vec[0].Column-1);
|
||||
var rref = new ExcelReference(vec[i].Row-1, vec[i].Row-1, vec[i].Column-1, vec[i].Column-1, vec[i].SheetName);
|
||||
var cellref = process_cell(rref, out _);
|
||||
if(i == 0)
|
||||
{
|
||||
string init = $"auto&& {rng.to_name()} = range_init({cellref.ToString()}, {rng.NumRows()}, {rng.NumCols()}); " ;
|
||||
_defs.add_def(init);
|
||||
}
|
||||
else
|
||||
{
|
||||
string next = $"range_set({rng.to_name()}, {cellref.ToString()}, {r}, {c}); ";
|
||||
_defs.add_def(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
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;
|
||||
@@ -307,59 +346,64 @@ namespace bts.excel
|
||||
{
|
||||
var colRow = context.colRow.Text;
|
||||
var (col1, row1) = CellRef.ConvertAddressToCoord(colRow);
|
||||
var sheetname = context.sheet?.Text ?? _orig_ref.GetSheetName();
|
||||
var sheetname = context.sheet?.Text ?? _ref.GetSheetName();
|
||||
|
||||
var rref = new ExcelReference(row1 - 1, row1 - 1, col1 - 1, col1 - 1, sheetname);
|
||||
return process_cell(rref, out _);
|
||||
return process_cell(rref);
|
||||
}
|
||||
|
||||
private object process_cell(ExcelReference rref, out string def)
|
||||
{
|
||||
_current_ref = rref;
|
||||
var cellref = new CellRef(rref);
|
||||
string r1c1 = cellref.r1_c1();
|
||||
|
||||
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 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 Exception(_error_listener.offendingSymbol + _error_listener.msg, _error_listener.exception);
|
||||
else
|
||||
{
|
||||
def = $"auto&& {cellref.ToString()} = {new GeneratorVisitor(rref, _defs, _params).VisitMain(parsetree)} ;";
|
||||
_defs.add_def(def);
|
||||
if (named && _params.algo_params.ContainsKey(cellref.named_range))
|
||||
{
|
||||
_defs.map_def(def, $"auto&& {cellref.named_range} = me->{cellref.named_range};");
|
||||
}
|
||||
if (named && _params.special_params.ContainsKey(cellref.named_range))
|
||||
{
|
||||
string realname = cellref.named_range;
|
||||
_params.special_params.TryGetValue(cellref.named_range, out realname);
|
||||
_defs.map_def(def, $"auto&& {cellref.named_range} = get_{realname}();");
|
||||
}
|
||||
}
|
||||
return cellref;
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -23,12 +23,7 @@ namespace bts.excel
|
||||
var end = End.Offset(columnOffset, rowOffset);
|
||||
return new RangeRef(start, end);
|
||||
}
|
||||
|
||||
public (int col, int row) compute_indexes(int col, int row)
|
||||
{
|
||||
return (col - Start.Column, row - Start.Row);
|
||||
}
|
||||
|
||||
|
||||
public CellRef Start { get; }
|
||||
|
||||
public CellRef End { get; }
|
||||
@@ -62,10 +57,6 @@ namespace bts.excel
|
||||
}
|
||||
}
|
||||
|
||||
public string r1_c1()
|
||||
{
|
||||
return $"{Start.r1_c1()}:{End.r1_c1()}";
|
||||
}
|
||||
|
||||
public bool IsVector()
|
||||
{
|
||||
@@ -77,7 +68,7 @@ namespace bts.excel
|
||||
return IsSingleRow() && IsSingleColumn();
|
||||
}
|
||||
|
||||
internal List<CellRef> get_all_cells()
|
||||
internal List<CellRef> Get2DVector()
|
||||
{
|
||||
var result = new List<CellRef>();
|
||||
var sheetName = Start.SheetName;
|
||||
@@ -96,27 +87,6 @@ namespace bts.excel
|
||||
return result;
|
||||
}
|
||||
|
||||
internal CellRef[,] cell_matrix_col_row()
|
||||
{
|
||||
var numCols = NumCols();
|
||||
var numRows = NumRows();
|
||||
var result = new CellRef[numCols, numRows];
|
||||
var sheetName = Start.SheetName;
|
||||
|
||||
var lastCol = Start.Column + numCols - 1;
|
||||
var lastRow = Start.Row + numRows - 1;
|
||||
int c = 0;
|
||||
for (var col = Start.Column; col <= lastCol; col++)
|
||||
{
|
||||
int r = 0;
|
||||
for (var row = Start.Row; row <= lastRow; row++)
|
||||
{
|
||||
result[c, r++] = new CellRef(sheetName, (col, row));
|
||||
}
|
||||
c++; //it's haunting me
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal List<CellRef> GetVector()
|
||||
{
|
||||
|
||||
@@ -4,43 +4,36 @@ using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using bts.excel.functions;
|
||||
using ExcelDna.Registration;
|
||||
using ExcelDna.Integration;
|
||||
using System.Reflection;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public interface IFunction
|
||||
{
|
||||
object function(IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell);
|
||||
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, SparkAlgoCodeAttribute> _dnafunctions =
|
||||
new Dictionary<string, SparkAlgoCodeAttribute>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<string, Delegate> _dnafunctions =
|
||||
new Dictionary<string, Delegate>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public FunctionLibrary(bool include_setups)
|
||||
public FunctionLibrary()
|
||||
{
|
||||
add_functions(Logical.functions);
|
||||
add_functions(Mathematics.functions);
|
||||
add_functions(Stat.functions);
|
||||
if(include_setups)
|
||||
add_function(nameof(sparkalgo_setup_order), new sparkalgo_setup_order());
|
||||
|
||||
var dna = ExcelRegistration.GetExcelFunctions(); string fn = string.Empty;
|
||||
foreach (var xl in dna)
|
||||
var dna = ExcelRegistration.GetExcelFunctions();
|
||||
foreach(var xl in dna)
|
||||
{
|
||||
foreach(var att in xl.CustomAttributes)
|
||||
{
|
||||
if(att is SparkAlgoCodeAttribute ac)
|
||||
_dnafunctions[xl.FunctionAttribute.Name] = ac;
|
||||
}
|
||||
_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);
|
||||
@@ -51,74 +44,32 @@ namespace bts.excel.functions.lib
|
||||
foreach (var f in delegates)
|
||||
_functions.Add(f.Key, f.Value);
|
||||
}
|
||||
|
||||
public object call_function(string name, IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell)
|
||||
|
||||
public object call_function(string name, IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
object code = string.Empty;
|
||||
if(string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name))
|
||||
return code;
|
||||
if (_functions.TryGetValue(name, out IFunction func))
|
||||
code = func.function(args, dv, containing_cell);
|
||||
else
|
||||
code = bts_function_or_throw(name, args, dv, containing_cell);
|
||||
return code;
|
||||
if(_functions.TryGetValue(name, out IFunction func))
|
||||
return func.function(args, dv);
|
||||
else
|
||||
return call_dna_function(name, args, dv);
|
||||
}
|
||||
|
||||
object bts_function_or_throw(string name, IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell)
|
||||
{
|
||||
SparkAlgoCodeAttribute att = null;
|
||||
if (_dnafunctions.TryGetValue(name, out att))
|
||||
object call_dna_function(string name, IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
if(_dnafunctions.TryGetValue(name, out Delegate func))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(att.dual_definition))
|
||||
dv.add_dual_definition(att.dual_definition);
|
||||
if (!string.IsNullOrEmpty(att.dual_impl))
|
||||
dv.add_dual_impl(att.dual_impl);
|
||||
if(!string.IsNullOrEmpty(att.replacement_call))
|
||||
{
|
||||
return att.replacement_call;
|
||||
}
|
||||
else
|
||||
{
|
||||
object[] param = args.Select(x => x.evaluate()).ToArray();
|
||||
string function_call = $"{name} ( "; int z = 0;
|
||||
foreach (var p in param)
|
||||
function_call += p.ToString() + (z++ < param.Length - 1 ? ", " : "");
|
||||
function_call += ")";
|
||||
return modify_if_array_formula(function_call, containing_cell);
|
||||
}
|
||||
}
|
||||
throw new NotSupportedException("Function " + name + " not currently supported by algogen");
|
||||
}
|
||||
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);
|
||||
|
||||
internal bool is_array_formula(ExcelReference containing_cell)
|
||||
{
|
||||
bool is_array_formula = (bool)XlCall.Excel(XlCall.xlfGetCell, 49, containing_cell);
|
||||
return is_array_formula;
|
||||
}
|
||||
|
||||
internal object modify_if_array_formula(object code, ExcelReference containing_cell)
|
||||
{
|
||||
if(!is_array_formula(containing_cell))
|
||||
string code = $"{name} ( "; int i = 0;
|
||||
foreach(var p in param)
|
||||
code += p.ToString() + (i++ < param.Length-1 ? ", " : "");
|
||||
code += ")";
|
||||
return code;
|
||||
using (var ech = new ExcelEchoOffHelper())
|
||||
{
|
||||
using (var calc = new ExcelCalculationManualHelper())
|
||||
{
|
||||
using (var sel = new ExcelSelectionHelper(containing_cell))
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, containing_cell);
|
||||
var c = new CellRef(containing_cell);
|
||||
var a = XlCall.Excel(XlCall.xlcSelectSpecial, 6);
|
||||
ExcelReference array = (ExcelReference)XlCall.Excel(XlCall.xlfSelection);
|
||||
code += $"({containing_cell.RowFirst - array.RowFirst},{containing_cell.ColumnFirst - array.ColumnFirst})";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return code;
|
||||
else
|
||||
return ErrorValue.Name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
||||
public class SparkAlgoCodeAttribute : Attribute
|
||||
{
|
||||
public string dual_definition;
|
||||
public string dual_impl;
|
||||
public string replacement_call;
|
||||
|
||||
public SparkAlgoCodeAttribute()
|
||||
{
|
||||
//nulls ok
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using ExcelDna.Integration;
|
||||
using bts.api;
|
||||
using ExcelDna.Registration.Utils;
|
||||
using System.Reactive.Linq;
|
||||
using bts.md;
|
||||
|
||||
using MD = bts.excel.rtd.MDSubscribe;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public class SparkAlgoFunctions
|
||||
{
|
||||
static ConcurrentDictionary<string, object> _cache = new ConcurrentDictionary<string, object>();
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_parameter([ExcelArgument(AllowReference = true)] object parameter, string type)
|
||||
{
|
||||
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))
|
||||
{
|
||||
BTSRibbon.globals.register_algo_parameter(type ?? "double", cellname.ToString());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return $"{ErrorValue.Name}, please make algo parameters named ranges";
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_unregister_parameter(string parameter)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(parameter))
|
||||
{
|
||||
BTSRibbon.globals.unregister_algo_parameter(parameter);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return $"{ErrorValue.Name}, no parameter named " + parameter;
|
||||
}
|
||||
|
||||
//See ExcelAlgoTemplate.cpp __AlgoName__ class def
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_user_parameter([ExcelArgument(AllowReference = true)] object parameter)
|
||||
{
|
||||
return sparkalgo_register_special_parameter(parameter, "user");
|
||||
}
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_account_parameter([ExcelArgument(AllowReference = true)] object parameter)
|
||||
{
|
||||
return sparkalgo_register_special_parameter(parameter, "account");
|
||||
}
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_side_parameter([ExcelArgument(AllowReference = true)] object parameter)
|
||||
{
|
||||
return sparkalgo_register_special_parameter(parameter, "side");
|
||||
}
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_price_parameter([ExcelArgument(AllowReference = true)] object parameter)
|
||||
{
|
||||
return sparkalgo_register_special_parameter(parameter, "price");
|
||||
}
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_quantity_parameter([ExcelArgument(AllowReference = true)] object parameter)
|
||||
{
|
||||
return sparkalgo_register_special_parameter(parameter, "quantity");
|
||||
}
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_tif_parameter([ExcelArgument(AllowReference = true)] object parameter)
|
||||
{
|
||||
return sparkalgo_register_special_parameter(parameter, "time_in_force");
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_special_parameter([ExcelArgument(AllowReference = true)] object parameter, string name)
|
||||
{
|
||||
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))
|
||||
{
|
||||
if(string.IsNullOrEmpty(name))
|
||||
name = cellname.ToString();
|
||||
BTSRibbon.globals.register_special_parameter(cellname.ToString(), name);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return $"{ErrorValue.Name}, please make special parameters named ranges";
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_unregister_special_parameter(string parameter)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(parameter))
|
||||
{
|
||||
BTSRibbon.globals.unregister_special_parameter(parameter);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return $"{ErrorValue.Name}, no special parameter named " + parameter;
|
||||
}
|
||||
|
||||
[ExcelFunction]
|
||||
[SparkAlgoCode]
|
||||
static public object sparkalgo_take_snapshot(string tag, object data)
|
||||
{
|
||||
object val;
|
||||
if(!_cache.TryGetValue(tag, out val))
|
||||
{
|
||||
_cache[tag] = val = data;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
[ExcelFunction()]
|
||||
[SparkAlgoCode(
|
||||
dual_definition = @"
|
||||
int sparkalgo_snapshot_switch(const std::string& name, int on_off);",
|
||||
dual_impl = @"
|
||||
int __AlgoName__::sparkalgo_snapshot_switch(const std::string& name, int on_off)
|
||||
{
|
||||
auto tag = name+""_switch"";
|
||||
if(on_off)
|
||||
return sparkalgo_take_snapshot(tag, on_off);
|
||||
else
|
||||
{
|
||||
auto it = me->snapshots.find(tag);
|
||||
if(it == me->snapshots.end())
|
||||
return on_off;
|
||||
else
|
||||
return std::get<int>(it->second);
|
||||
}
|
||||
}"
|
||||
)]
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Resolve a security id from contract details")]
|
||||
[SparkAlgoCode(
|
||||
replacement_call = "sparkalgo_resolve_security_id()"
|
||||
)]
|
||||
public async static void sparkalgo_resolve_primary_security_id(
|
||||
[ExcelArgument("Required. The specific contract, GCZ8 or ZNU8-ZNZ8, etc")] string contract,
|
||||
[ExcelArgument("Required. The symbol, GC or ZN, etc")] string symbol,
|
||||
[ExcelArgument("Optional. The type, Future or Spread, default = Future")] string security_type,
|
||||
[ExcelArgument("Optional. The exchange, default = GLBX")] string exchange,
|
||||
ExcelAsyncHandle handle)
|
||||
{
|
||||
exchange = string.IsNullOrEmpty(exchange) ? Security.ExchangeDataOneofCase.Glbx.ToString() : exchange;
|
||||
Func<SecurityImmutable, bool> where = MD.md_where(contract, symbol, security_type, exchange, out Venue ven);
|
||||
var md = bts.excel.rtd.MDSubscribe.market_data(ven);
|
||||
try
|
||||
{
|
||||
SecurityImmutable? sec = await md.find_security_async(where, TimeSpan.FromSeconds(15));
|
||||
if (sec != null && sec.HasValue)
|
||||
handle.SetResult(sec.Value.Id);
|
||||
else
|
||||
handle.SetResult(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handle.SetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Resolve a security id from contract details")]
|
||||
[SparkAlgoCode()]
|
||||
public async static void sparkalgo_resolve_security_id(
|
||||
[ExcelArgument("Required. The specific contract, GCZ8 or ZNU8-ZNZ8, etc")] string contract,
|
||||
[ExcelArgument("Required. The symbol, GC or ZN, etc")] string symbol,
|
||||
[ExcelArgument("Optional. The type, Future or Spread, default = Future")] string security_type,
|
||||
[ExcelArgument("Optional. The exchange, default = GLBX")] string exchange,
|
||||
ExcelAsyncHandle handle)
|
||||
{
|
||||
exchange = string.IsNullOrEmpty(exchange) ? Security.ExchangeDataOneofCase.Glbx.ToString() : exchange;
|
||||
Func<SecurityImmutable, bool> where = MD.md_where(contract, symbol, security_type, exchange, out Venue ven);
|
||||
var md = bts.excel.rtd.MDSubscribe.market_data(ven);
|
||||
try
|
||||
{
|
||||
SecurityImmutable? sec = await md.find_security_async(where, TimeSpan.FromSeconds(15));
|
||||
if (sec != null && sec.HasValue)
|
||||
handle.SetResult(sec.Value.Id);
|
||||
else
|
||||
handle.SetResult(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handle.SetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Subscribe to BTS Spark full depth market data by security_id")]
|
||||
[SparkAlgoCode(
|
||||
dual_definition = @"
|
||||
ExcelBookAccessor sparkalgo_md_book_id(SecurityKey sk);",
|
||||
dual_impl = @"
|
||||
ExcelBookAccessor __AlgoName__::sparkalgo_md_book_id(SecurityKey sk)
|
||||
{
|
||||
ExcelBookAccessor acc(me->last_book);
|
||||
return acc;
|
||||
}"
|
||||
)]
|
||||
public static object sparkalgo_md_book_id([ExcelArgument("Required. The security_id")] long security_id)
|
||||
{
|
||||
|
||||
var param = new object[] { security_id };
|
||||
return ObservableRtdUtil.Observe(nameof(sparkalgo_md_book_id), param,
|
||||
() => MD.md_observable_id(Convert.ToUInt32(security_id)).Select(data =>
|
||||
{
|
||||
var x = data.FirstOrDefault();
|
||||
if (x != null && x is IMDBook upd)
|
||||
return rtd.BookMDFormatter.format(upd);
|
||||
return x;
|
||||
}));
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS")]
|
||||
[SparkAlgoCode(
|
||||
dual_impl = @"
|
||||
OrderKey __AlgoName__::sparkalgo_generate_local_orderid()
|
||||
{
|
||||
static uint32_t id = 0;
|
||||
return OrderKey( ++id );
|
||||
}"
|
||||
)]
|
||||
public static object sparkalgo_generate_local_orderid(object dependency)
|
||||
{
|
||||
ExcelReference caller = XlCall.Excel(XlCall.xlfCaller) as ExcelReference;
|
||||
CellRef rref = new CellRef(caller);
|
||||
return rref.Row*1000 + rref.Column;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using spark_excel.grammar;
|
||||
using Antlr4.Runtime;
|
||||
using ExcelDna.Integration;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class sparkalgo_setup_order : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
CodeParts _parts = new CodeParts();
|
||||
var gen = new GeneratorVisitor(containing_cell, _parts, BTSRibbon.globals, false);
|
||||
|
||||
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, containing_cell);
|
||||
|
||||
var antlrstream = new AntlrInputStream(formula.ToString());
|
||||
var lexer = new ExcelFormulaLexer(antlrstream);
|
||||
var tokens = new CommonTokenStream(lexer);
|
||||
var parser = new ExcelFormulaParser(tokens);
|
||||
|
||||
var parsetree = parser.main();
|
||||
|
||||
var name = new CellRef(containing_cell).ToString();
|
||||
var expr = gen.VisitMain(parsetree);
|
||||
|
||||
foreach (var dd in _parts.get_dual_definitions())
|
||||
dv.add_dual_definition(dd);
|
||||
foreach (var di in _parts.get_dual_impls())
|
||||
dv.add_dual_impl(di);
|
||||
|
||||
string setup = string.Join(Environment.NewLine, _parts.get_defs());
|
||||
setup += $"{Environment.NewLine} me->{name}_order_id = { expr.ToString() };";
|
||||
dv.register_order_setup(setup, $"me->{name}_order_id = cache_attach(attached_order, st);");
|
||||
|
||||
dv.register_algo_parameter("OrderKey", name + "_order_id");
|
||||
return "me->" + name + "_order_id";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
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, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// 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);
|
||||
var first = args[0].evaluate();
|
||||
if(first is CellRef)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else if (first is RangeRef)
|
||||
{
|
||||
string rng = first.ToString();
|
||||
string loop = $@"
|
||||
auto {rng}_avg = 0.0;
|
||||
for(auto& {rng}_i : {rng} )
|
||||
{rng}_avg += {rng}_i;
|
||||
{rng}_avg /= {rng}.size();
|
||||
";
|
||||
dv.add_def(loop);
|
||||
return rng + "_avg";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
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, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// 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(
|
||||
$"Sum function takes at least 1 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string name = "sum_" + new Random().Next(0, int.MaxValue);
|
||||
var first = args[0].evaluate();
|
||||
if (first is CellRef)
|
||||
{
|
||||
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}";
|
||||
}
|
||||
dv.add_def(def);
|
||||
return name;
|
||||
}
|
||||
else if (first is RangeRef)
|
||||
{
|
||||
string rng = first.ToString();
|
||||
string loop = $@"
|
||||
auto {rng}_sum = 0.0;
|
||||
for(auto& {rng}_i : {rng} )
|
||||
{rng}_sum += {rng}_i;
|
||||
";
|
||||
dv.add_def(loop);
|
||||
return rng + "_sum";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class And : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class If : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
+1
-1
@@ -3,7 +3,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class IfError : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Ln : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: LN(number)
|
||||
// https://support.office.com/en-gb/article/LN-function-81fe1ed7-dac9-4acd-ba1d-07a142c6118f
|
||||
+1
-1
@@ -3,7 +3,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Log : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Max : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Min : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Or : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Pi : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: PI()
|
||||
// https://support.office.com/en-gb/article/PI-function-264199d0-a3ba-46b8-975a-c4a04608989b
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Power : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
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
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Sin : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: SIN(number)
|
||||
// https://support.office.com/en-gb/article/SIN-function-654975c2-05c4-4831-9a24-2c65e4040fdf
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Sqrt : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: SQRT(number)
|
||||
// https://support.office.com/en-gb/article/SQRT-function-654975c2-05c4-4831-9a24-2c65e4040fdf
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using ExcelDna.Integration.CustomUI;
|
||||
using ExcelDna.Integration;
|
||||
using Excel = Microsoft.Office.Interop.Excel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
[ComVisible(true)]
|
||||
public class BTSRibbon : ExcelRibbon
|
||||
{
|
||||
static readonly Generator _generator = new Generator();
|
||||
static public IParameters globals => _generator.globals;
|
||||
|
||||
public override string GetCustomUI(string RibbonID)
|
||||
{
|
||||
return @"
|
||||
<customUI xmlns='http://schemas.microsoft.com/office/2006/01/customui'>
|
||||
<ribbon>
|
||||
<tabs>
|
||||
<tab id='tab1' label='BTS'>
|
||||
<group id='group1' label='Algos'>
|
||||
<button id='button1' label='Build Algo From Selection' onAction='ribbon_generate_code'/>
|
||||
</group >
|
||||
</tab>
|
||||
</tabs>
|
||||
</ribbon>
|
||||
</customUI>";
|
||||
}
|
||||
|
||||
public void ribbon_generate_code(IRibbonControl control)
|
||||
{
|
||||
//MessageBox.Show("Hello from control " + control.Id + " " + control.Context.ToString());
|
||||
Excel.Application app = (Excel.Application)((dynamic)control.Context).Application;
|
||||
app.GetType().InvokeMember("Run", System.Reflection.BindingFlags.InvokeMethod, null,
|
||||
app, new object[]{ nameof(bts_generate_algo_code) }, new System.Globalization.CultureInfo(1033));
|
||||
}
|
||||
|
||||
[ExcelCommand()]
|
||||
static public void bts_generate_algo_code()
|
||||
{
|
||||
Excel.Application app = (Excel.Application)ExcelDnaUtil.Application;
|
||||
var wind = new NativeWindow();
|
||||
wind.AssignHandle(ExcelDnaUtil.WindowHandle);
|
||||
//var r = app.ActiveCell.FunctionWizard();
|
||||
try
|
||||
{
|
||||
string document_name = string.Empty;
|
||||
List<CodeParts> functions = new List<CodeParts>();
|
||||
// ExcelAsyncUtil.QueueAsMacro( () =>
|
||||
MessageBox.Show(wind, "About to generate code, pretend you see a progress bar and click ok");
|
||||
foreach(var cell in app.Selection as Excel.Range)
|
||||
{
|
||||
ExcelReference rref = RangeHelper.RangeToExcelReference(cell as Excel.Range);
|
||||
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
|
||||
if(formula == null || string.IsNullOrEmpty(formula.ToString()))
|
||||
continue;
|
||||
var cellref = new CellRef(rref);
|
||||
document_name = (string)XlCall.Excel(XlCall.xlfGetDocument, 88, cellref.SheetName);
|
||||
|
||||
string fname = null;
|
||||
var f = _generator.bts_generate_function_code(rref, ref fname);
|
||||
functions.Add(f);
|
||||
}
|
||||
if(functions.Count > 0)
|
||||
{
|
||||
//@"C:\code\bts\spark\build\ninja_debug\sdk\examples\excel_plugin_example.cpp",
|
||||
string[] filepath = new [] {
|
||||
@"c:\code\bts\spark\src\sdk\examples\excel_plugin_example.cpp",
|
||||
|
||||
};
|
||||
string fname = _generator.bts_generate_algo_class(CellRef.stringize_workbook( document_name ),
|
||||
functions, filepath);
|
||||
MessageBox.Show(wind, "Code Generation Succesful " + fname);
|
||||
}
|
||||
else
|
||||
MessageBox.Show(wind, "Nothing to do! Please select one or more 'Algo Sinks' and try again ");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(wind, ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
wind.ReleaseHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using ExcelDna.Integration;
|
||||
using ExcelDna.Integration.Rtd;
|
||||
using ExcelDna.Registration.Utils;
|
||||
@@ -13,22 +12,19 @@ using bts.excel.extensions;
|
||||
using bts.excel.rtd;
|
||||
using Newtonsoft.Json;
|
||||
using bts.api;
|
||||
using System.Threading.Tasks;
|
||||
using bts.utils;
|
||||
|
||||
|
||||
namespace bts.api.orderman
|
||||
{
|
||||
public partial class OrderService_Excel
|
||||
public partial class OrderService_Excel
|
||||
{
|
||||
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);
|
||||
static AsyncLazy<core.Result<AdminData>> _admin_data = new AsyncLazy<core.Result<AdminData>>(() => _admin.Value.get_admin_data_async(new VoidMessage()));
|
||||
|
||||
class OrderDef
|
||||
struct OrderDef
|
||||
{
|
||||
public Create create;
|
||||
public string order_type;
|
||||
public core.Result<Response>? response;
|
||||
public Response response;
|
||||
}
|
||||
static ConcurrentDictionary<long, OrderDef> _orders = new ConcurrentDictionary<long, OrderDef>();
|
||||
|
||||
@@ -43,7 +39,7 @@ namespace bts.api.orderman
|
||||
if (admin.success)
|
||||
{
|
||||
foreach (var acc in admin.value.Accounts)
|
||||
if (acc.IsPresent)
|
||||
if(acc.IsPresent)
|
||||
accts.Add(acc.Name);
|
||||
}
|
||||
var res = Utilities.make_column_vector(accts.ToArray());
|
||||
@@ -55,148 +51,116 @@ namespace bts.api.orderman
|
||||
}
|
||||
}
|
||||
|
||||
static Task<core.Result<Response>> cancel_order(long order_id)
|
||||
static Response cancel_order(long order_id)
|
||||
{
|
||||
var resp = _service.Value.cancel_order_async(new Cancel() { OrderKey = Convert.ToUInt32(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")]
|
||||
public static async void spark_orderservice_cancel_order(long order_id, ExcelAsyncHandle handle)
|
||||
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID", IsMacroType = true)]
|
||||
public static object spark_orderservice_cancel_order(long order_id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resp = await cancel_order(order_id);
|
||||
handle.SetResult(parse_response(resp));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
handle.SetException(ex);
|
||||
}
|
||||
var resp = cancel_order(order_id);
|
||||
return parse_response(resp);
|
||||
}
|
||||
|
||||
static Task<core.Result<Response>> update_order(long order_id, double price, long quantity, CustomOrderDefinition cust = null)
|
||||
|
||||
static Response update_order(long order_id, double price, long quantity, CustomOrderDefinition cust = null)
|
||||
{
|
||||
var resp = _service.Value.update_order_async(new Update()
|
||||
{
|
||||
OrderKey = Convert.ToUInt32(order_id),
|
||||
Price = price,
|
||||
Quantity = Convert.ToUInt32(quantity),
|
||||
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")]
|
||||
public static async void spark_orderservice_update_order(long order_id, double price, long quantity, ExcelAsyncHandle handle)
|
||||
[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)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resp = await update_order(order_id, price, quantity);
|
||||
handle.SetResult(parse_response(resp));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handle.SetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async static Task<Create> create_order(long security_key, string user, string account, string side,
|
||||
double price, long quantity, string time_in_force,
|
||||
string order_type, string order_params)
|
||||
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 > 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 = 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)
|
||||
{
|
||||
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),
|
||||
};
|
||||
cr.Price = price;
|
||||
cr.Quantity = Convert.ToUInt32(quantity);
|
||||
|
||||
var btsuser = await resolve_user(user);
|
||||
if (btsuser == null)
|
||||
throw new ArgumentException("Could not resolve user: " + user);
|
||||
cr.Quantity = Convert.ToUInt32(quantity);
|
||||
|
||||
var btsuser = resolve_user(user);
|
||||
if(btsuser == null)
|
||||
return "Could not resolve user: " + user;
|
||||
cr.NewOrder.User = btsuser.Name;
|
||||
cr.NewOrder.UserId = btsuser.Id;
|
||||
var btaccount = await resolve_account(account);
|
||||
if (btaccount == null)
|
||||
throw new ArgumentException("Could not resolve account: " + account);
|
||||
var btaccount = resolve_account(account);
|
||||
if(btaccount == null)
|
||||
return "Could not resolve account: " + account;
|
||||
cr.NewOrder.Account = btaccount.Name;
|
||||
cr.NewOrder.AccountId = btaccount.Id;
|
||||
if (string.IsNullOrEmpty(order_type))
|
||||
order_type = "Limit";
|
||||
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)
|
||||
throw new ArgumentException("Could not parse order_type and/or order_params info");
|
||||
return cr;
|
||||
}
|
||||
|
||||
static Tuple<Create, Task<core.Result<Response>>> send_order(Create cr)
|
||||
{
|
||||
var resp = Tuple.Create(cr, _service.Value.create_order_async(cr));
|
||||
return resp;
|
||||
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);
|
||||
_orders[local_order_id] = new OrderDef { create = cr, response = resp };
|
||||
return parse_response(resp, true);
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key")]
|
||||
public static async void spark_orderservice_send_order_by_security_key(
|
||||
[ExcelArgument("Required. Security key")] long security_key,
|
||||
[ExcelArgument("Required. User")] string user,
|
||||
[ExcelArgument("Required. Account")] string account,
|
||||
[ExcelArgument("Required. Side BUY or SELL")] string side,
|
||||
[ExcelArgument("Required. Price")] double price,
|
||||
[ExcelArgument("Required. Quantity")] long quantity,
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force,
|
||||
[ExcelArgument("OPTIONAL. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params,
|
||||
ExcelAsyncHandle handle)
|
||||
private static object parse_response(Response resp, bool show_vertically = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Create cr = await create_order(security_key, user, account, side, price, quantity, order_type, order_params, time_in_force);
|
||||
var order = send_order(cr);
|
||||
var resp = await order.Item2;
|
||||
|
||||
handle.SetResult(parse_response(resp, true));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
handle.SetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static object parse_response(core.Result<Response> resp, bool show_vertically = false)
|
||||
{
|
||||
object[,] response_cells = new JsonFormatter(resp.value).format(false);
|
||||
if (show_vertically)
|
||||
object[,] response_cells = new JsonFormatter(resp).format(false);
|
||||
if(show_vertically)
|
||||
response_cells = bts.excel.Utilities.transpose_keep_dimension(response_cells);
|
||||
return response_cells;
|
||||
}
|
||||
|
||||
async static Task<BTSUser> resolve_user(string username)
|
||||
{
|
||||
var admin = await _admin_data.Value;
|
||||
if (!admin.success)
|
||||
return null;
|
||||
var user = admin.value.Users.FirstOrDefault(x => username != null ? x.Name.ToLowerInvariant() == username.ToLowerInvariant() : true);
|
||||
|
||||
static Lazy<AdminData> _admin_data = new Lazy<AdminData>(() => _admin.Value.get_admin_data(new VoidMessage()), true);
|
||||
static BTSUser resolve_user(string username)
|
||||
{
|
||||
var user = _admin_data.Value.Users.FirstOrDefault(x => username != null ? x.Name.ToLowerInvariant() == username.ToLowerInvariant() : true);
|
||||
return user;
|
||||
}
|
||||
|
||||
async static Task<BTSAccount> resolve_account(string acctname)
|
||||
static BTSAccount resolve_account(string acctname)
|
||||
{
|
||||
var admin = await _admin_data.Value;
|
||||
if(!admin.success)
|
||||
return null;
|
||||
var acc = admin.value.Accounts.FirstOrDefault(x => x.Name.ToLowerInvariant() == acctname.ToLowerInvariant());
|
||||
var acc = _admin_data.Value.Accounts.FirstOrDefault(x => x.Name.ToLowerInvariant() == acctname.ToLowerInvariant());
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
.Where(x => x.Name.ToLower() == order_type.ToLower()).FirstOrDefault();
|
||||
if (member != null)
|
||||
{
|
||||
var mi = member.member_type();
|
||||
@@ -207,207 +171,79 @@ namespace bts.api.orderman
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Setup Spark Order ")]
|
||||
[SparkAlgoCode]
|
||||
public async static void sparkalgo_setup_order(
|
||||
[ExcelArgument("Required. Security Key")] long local_order_id,
|
||||
[ExcelArgument("Required. Security Key")] long security_key,
|
||||
[ExcelArgument("Required. User")] string user,
|
||||
[ExcelArgument("Required. Account")] string account,
|
||||
[ExcelArgument("Required. Side BUY or SELL")] string side,
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force,
|
||||
[ExcelArgument("OPTIONAL. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params,
|
||||
ExcelAsyncHandle handle)
|
||||
{
|
||||
if (_orders.ContainsKey(local_order_id))
|
||||
handle.SetResult(local_order_id);
|
||||
// throw new ArgumentException("use a new local order id per order");
|
||||
|
||||
Create cr = await create_order(security_key, user, account, side, double.NaN, 0, order_type, order_params, time_in_force);
|
||||
_orders[local_order_id] = new OrderDef { create = cr, order_type = order_type, response = null};
|
||||
handle.SetResult(local_order_id);
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Send or Modify or Cancel a Spark Order ")]
|
||||
[SparkAlgoCode]
|
||||
public async static void sparkalgo_send_or_update_order(
|
||||
[ExcelArgument("Required. Local Order returned from sparkalgo_setup_order")] long local_order_id,
|
||||
[ExcelArgument("Required. Only process inputs for the order if > 1")] long should_process_inputs,
|
||||
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,
|
||||
[ExcelArgument("Required. User")] string user,
|
||||
[ExcelArgument("Required. Account")] string account,
|
||||
[ExcelArgument("Required. Price")] double price,
|
||||
[ExcelArgument("Required. Quantity")] long quantity,
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_param_update,
|
||||
ExcelAsyncHandle handle)
|
||||
[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,
|
||||
ExcelAsyncHandle handle = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(should_process_inputs <= 0)
|
||||
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(-1);
|
||||
return;
|
||||
}
|
||||
if (_orders.TryGetValue(local_order_id, out OrderDef def))
|
||||
handle.SetResult("Can't find security desc " + security_desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!def.response.HasValue) //is new
|
||||
if (_orders.TryGetValue(local_order_id, out OrderDef def) && def.response.OrderKey > 0)
|
||||
{
|
||||
def.create.Price = price;
|
||||
def.create.Quantity = Convert.ToUInt32(quantity);
|
||||
var resp = await send_order(def.create).Item2;
|
||||
if(resp.success)
|
||||
if(quantity > 0)
|
||||
{
|
||||
def.response = resp;
|
||||
handle.SetResult(resp.value.OrderKey);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
handle.SetException(new Exception(resp.error_message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else //is working
|
||||
{
|
||||
if (quantity > 0)
|
||||
{
|
||||
CustomOrderDefinition cust = null;
|
||||
if(!string.IsNullOrEmpty(order_param_update))
|
||||
cust = custom_def(def.order_type, order_param_update);
|
||||
|
||||
if (def.create.Price != price || def.create.Quantity != Convert.ToUInt32(quantity) ||
|
||||
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 = await update_order(def.response.Value.value.OrderKey, price, quantity, cust);
|
||||
if (resp.success && resp.value.ErrorCode == OrderError.Ok)
|
||||
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(resp.value.OrderKey);
|
||||
handle.SetResult( parse_response(resp, true) );
|
||||
}
|
||||
else
|
||||
handle.SetResult(def.response.Value.value.OrderKey);
|
||||
return;
|
||||
handle.SetResult( parse_response(def.response, true) );
|
||||
}
|
||||
else
|
||||
{
|
||||
var resp = await cancel_order(def.response.Value.value.OrderKey);
|
||||
if(resp.success)
|
||||
_orders.TryRemove(local_order_id, out _); //take out of working so we don't try to update it
|
||||
handle.SetResult(def.response.Value.value.OrderKey);
|
||||
return;
|
||||
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) );
|
||||
}
|
||||
handle.SetResult(-1);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handle.SetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
enum ChildStatus { WORKING, COMPLETED, CANCELED };
|
||||
class ChildState
|
||||
{
|
||||
public double price;
|
||||
public uint quantity;
|
||||
public uint total_fills;
|
||||
public double avg_price;
|
||||
public uint last_fills;
|
||||
public double last_price;
|
||||
public ChildStatus status;
|
||||
};
|
||||
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Receive Child Updates from Spark Order ")]
|
||||
[SparkAlgoCode]
|
||||
public static object sparkalgo_child_state([ExcelArgument("Required. Local Order from sparkalgo_setup_order")] long local_order_key,
|
||||
bool display_headers)
|
||||
{
|
||||
Func<bts.api.orderman.OrderRecord, bool> myorder = (x =>
|
||||
{
|
||||
if(_orders.TryGetValue(local_order_key, out OrderDef ord))
|
||||
{
|
||||
if(ord.response.HasValue && ord.response.Value.success)
|
||||
return ord.response.Value.value.OrderKey == x.Key;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
var sub = _service.Value.subscribe_orders().SelectMany(x => x.all.Where(xx => myorder(xx)));
|
||||
var obj = sub.Select(x =>
|
||||
{
|
||||
var ch = new ChildState ();
|
||||
ch.price = x.State.Price;
|
||||
ch.quantity = x.State.Quantity;
|
||||
ch.total_fills = x.State.FilledQuantity;
|
||||
ch.avg_price = x.State.AvgPrice;
|
||||
ch.last_fills = 0;
|
||||
ch.last_price = x.State.LastPrice;
|
||||
switch(x.State.Status)
|
||||
{
|
||||
case OrderStatus.Inactive:
|
||||
case OrderStatus.Working:
|
||||
case OrderStatus.PendingWorking:
|
||||
ch.status = ChildStatus.WORKING;
|
||||
break;
|
||||
case OrderStatus.Canceled:
|
||||
case OrderStatus.Disconnected:
|
||||
case OrderStatus.Error:
|
||||
case OrderStatus.RejectedBySpark:
|
||||
ch.status = ChildStatus.CANCELED;
|
||||
break;
|
||||
default:
|
||||
ch.status = ChildStatus.COMPLETED;
|
||||
break;
|
||||
}
|
||||
return Utilities.transpose_keep_dimension( DataParser.scatter_data(ch, display_headers) );
|
||||
});
|
||||
|
||||
return ObservableRtdUtil.Observe(nameof(sparkalgo_child_state),
|
||||
new object[] { local_order_key, display_headers },
|
||||
() => obj);
|
||||
}
|
||||
|
||||
static System.Timers.Timer _timer = new System.Timers.Timer(1000);
|
||||
[ExcelFunction(Category = "BTS", Description = "Receive Child Updates from Spark Order ")]
|
||||
[SparkAlgoCode]
|
||||
public static void sparkalgo_timer(ExcelAsyncHandle handle)
|
||||
{
|
||||
_timer.Elapsed += (x, e) => handle.SetResult(System.DateTime.Now);
|
||||
_timer.Enabled = true;
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Opposite side ")]
|
||||
[SparkAlgoCode(
|
||||
dual_definition = @"
|
||||
bts::api::Side sparkalgo_other_side(bts::api::Side side);",
|
||||
dual_impl = @"
|
||||
bts::api::Side __AlgoName__::sparkalgo_other_side(bts::api::Side side)
|
||||
{
|
||||
return securities::other_side(side);
|
||||
}"
|
||||
)]
|
||||
public static object sparkalgo_other_side(object side)
|
||||
{
|
||||
bts.api.Side the_side;
|
||||
if (Enum.TryParse<bts.api.Side>("SIDE_" + side.ToString(), true, out the_side) ||
|
||||
Enum.TryParse<bts.api.Side>(side.ToString(), true, out the_side))
|
||||
{
|
||||
switch (the_side)
|
||||
{
|
||||
case bts.api.Side.Buy:
|
||||
return bts.api.Side.Sell.ToString();
|
||||
case bts.api.Side.Sell:
|
||||
return bts.api.Side.Buy.ToString();
|
||||
default:
|
||||
return side.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,50 +48,30 @@ namespace bts.excel.rtd
|
||||
MDSubscribe.market_data(ven).lookup_security_desc((uint)security_key, out string desc);
|
||||
return desc ?? string.Empty;
|
||||
}
|
||||
|
||||
internal static Func<SecurityImmutable, bool> md_where(string contract, string symbol, string security_type, string exchange, out Venue ven)
|
||||
|
||||
static IObservable<IEnumerable<object>> md_observable(string contract, string symbol, string security_type, string exchange)
|
||||
{
|
||||
security_type = string.IsNullOrEmpty(security_type) ? SecurityType.Future.ToString() : security_type;
|
||||
exchange = string.IsNullOrEmpty(exchange) ? Security.ExchangeDataOneofCase.Glbx.ToString() : exchange;
|
||||
symbol = string.IsNullOrEmpty(symbol) ? contract.Substring(0, 2) : symbol;
|
||||
|
||||
Venue ven;
|
||||
SecurityType sectype;
|
||||
if (!Utilities.parse_venue(exchange, out ven))
|
||||
throw new ArgumentException("Can't Parse Exchange: " + exchange ?? string.Empty);
|
||||
return Observable.Return(new [] {"Can't Parse Exchange: " + exchange ?? string.Empty});
|
||||
if (!Utilities.parse_security_type(security_type, out sectype))
|
||||
throw new ArgumentException("Can't Parse Security Type: " + security_type ?? string.Empty);
|
||||
return Observable.Return(new [] {"Can't Parse Security Type: " + security_type ?? string.Empty});
|
||||
if (string.IsNullOrEmpty(symbol))
|
||||
throw new ArgumentException("Can't Parse Symbol: " + symbol ?? string.Empty);
|
||||
return Observable.Return(new []{"Can't Parse Symbol: " + symbol ?? string.Empty});
|
||||
if (string.IsNullOrEmpty(contract))
|
||||
throw new ArgumentException("Can't Parse Contract: " + contract ?? string.Empty );
|
||||
return Observable.Return(new []{"Can't Parse Contract: " + contract ?? string.Empty});
|
||||
|
||||
Venue ven_ = ven;
|
||||
Func<SecurityImmutable, bool> where = (x => x.Exchange == ven_ && x.Symbol == symbol &&
|
||||
x.security_type() == sectype && x.security_desc() == contract);
|
||||
return where;
|
||||
}
|
||||
Func<SecurityImmutable, bool> where = (x => x.Exchange == ven && x.Symbol == symbol && x.security_type() == sectype &&
|
||||
x.Glbx.SecurityDesc == contract);
|
||||
|
||||
internal static IObservable<IEnumerable<object>> md_observable(string contract, string symbol, string security_type, string exchange)
|
||||
{
|
||||
try
|
||||
{
|
||||
Func<SecurityImmutable, bool> where = md_where(contract, symbol, security_type, exchange, out Venue ven);
|
||||
var stream = market_data(ven).subscribe_combined(where);
|
||||
return stream;
|
||||
}
|
||||
catch(ArgumentException ex)
|
||||
{
|
||||
return Observable.Return(new[] { ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
internal static IObservable<IEnumerable<object>> md_observable_id(uint security_id)
|
||||
{
|
||||
Func<SecurityImmutable, bool> where = (x => x.Id == security_id);
|
||||
|
||||
var stream = _md.subscribe_combined(where);
|
||||
var stream = market_data(ven).subscribe_combined(where);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Subscribe to BTS Spark full depth market data")]
|
||||
public static object spark_md_book(
|
||||
@@ -110,7 +90,16 @@ namespace bts.excel.rtd
|
||||
return BookMDFormatter.format(upd);
|
||||
return x;
|
||||
}));
|
||||
}
|
||||
// security_type = string.IsNullOrEmpty(security_type) ? SecurityType.Future.ToString() : security_type;
|
||||
// exchange = string.IsNullOrEmpty(exchange) ? Security.ExchangeDataOneofCase.Glbx.ToString() : exchange;
|
||||
// symbol = string.IsNullOrEmpty(symbol) ? contract.Substring(0, 2) : symbol;
|
||||
// string[] topic = new[] { MDTopic.topic_type, MDTopic.book_request_type,
|
||||
// contract, symbol, security_type, exchange};
|
||||
// object val = XlCall.RTD(typeof(RTDServer).FullName, null, topic);
|
||||
// return Subscribe.spark_unpack(val);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Subscribe to BTS Spark top of book market data")]
|
||||
public static object spark_md_top_of_book(
|
||||
@@ -185,6 +174,7 @@ namespace bts.excel.rtd
|
||||
return TopicMDFormatter.format(upd, topic);
|
||||
return x;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<Antlr4UseCSharpGenerator>true</Antlr4UseCSharpGenerator>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -58,8 +57,6 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -92,11 +89,6 @@
|
||||
<None Include="spark_excel_example_2018.xlsm"><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="../../sdk/examples/ExcelAlgoTemplate.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<!-- <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
|
||||
<ItemGroup>
|
||||
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,6 @@
|
||||
<None Include="$(OutputPath)\net471\spark_excel_example_2018.xlsm" pack="true" PackagePath=".\excel\spark_excel_example_2018.xlsm" />
|
||||
<None Include="$(OutputPath)\net471\spark_excel-addin64-packed.xll" pack="true" PackagePath=".\excel\spark_excel-addin64-packed.xll" />
|
||||
<None Include="$(OutputPath)\net471\spark_excel-addin64.dna" pack="true" PackagePath=".\excel\spark_excel-addin64.dna" />
|
||||
<None Include="$(OutputPath)\net471\ExcelAlgoTemplate.cpp" pack="true" PackagePath=".\excel\ExcelAlgoTemplate.cpp" />
|
||||
<None Include="doc/**" pack="true" PackagePath=".\doc\" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -100,7 +99,6 @@
|
||||
<Copy SourceFiles="..\..\excel\spark_excel_example_2018.xlsm" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="..\..\excel\spark_excel-addin64-packed.xll" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="..\..\excel\spark_excel-addin64.dna" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
|
||||
<Copy SourceFiles="..\..\excel\ExcelAlgoTemplate.cpp" DestinationFolder="$(OutputPath)\" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@@ -169,7 +169,7 @@ namespace bts.tests
|
||||
var second_short_order_def = create_order_def(second_sec.Id, Side.Sell, test_account, test_user);
|
||||
|
||||
uint short_no_limit_order_key;
|
||||
await Task.Delay(10000);
|
||||
|
||||
Result<Response> short_no_limit_order_result = await OrderUtilities.send_order(_order_service, first_short_order_def, 50000, 7);
|
||||
|
||||
short_no_limit_order_key = short_no_limit_order_result.value.OrderKey;
|
||||
|
||||
@@ -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/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
|
||||
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
|
||||
@@ -470,35 +470,22 @@ message FilteredAdminDataRequest { }
|
||||
service AdminService {
|
||||
option (bts.api.service_description) = "Provides a management interface to the user model";
|
||||
// account
|
||||
rpc create_account (AccountCreateAPIArgument) returns (AdminServiceCreateAccountResult)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc update_account (AccountUpdateAPIArgument) returns (AdminServiceOperationResult)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc delete_account(IDAPIArgument) returns (AdminServiceOperationResult)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc create_account (AccountCreateAPIArgument) returns (AdminServiceCreateAccountResult);
|
||||
rpc update_account (AccountUpdateAPIArgument) returns (AdminServiceOperationResult);
|
||||
rpc delete_account(IDAPIArgument) returns (AdminServiceOperationResult);
|
||||
// users
|
||||
rpc create_user(UserCreateAPIArgument) returns (AdminServiceCreateEntityResult)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc update_user(UserUpdateAPIArgument) returns (AdminServiceOperationResult)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc delete_user(IDAPIArgument) returns (AdminServiceOperationResult)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc create_user(UserCreateAPIArgument) returns (AdminServiceCreateEntityResult);
|
||||
rpc update_user(UserUpdateAPIArgument) returns (AdminServiceOperationResult);
|
||||
rpc delete_user(IDAPIArgument) returns (AdminServiceOperationResult);
|
||||
// subscription
|
||||
rpc admin_data(bts.api.VoidMessage) returns (stream AdminData)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc get_admin_data(bts.api.VoidMessage) returns (AdminData)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc get_filtered_admin_data(FilteredAdminDataRequest) returns (AdminData)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc admin_data(bts.api.VoidMessage) returns (stream AdminData);
|
||||
rpc get_admin_data(bts.api.VoidMessage) returns (AdminData);
|
||||
rpc get_filtered_admin_data(FilteredAdminDataRequest) returns (AdminData);
|
||||
// orders
|
||||
rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary);
|
||||
rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage);
|
||||
// reports
|
||||
rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEntitlementReportUnit)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEntitlementReportUnit);
|
||||
// misc
|
||||
rpc md_permissions_data(bts.api.VoidMessage) returns (stream UserSymbolMDPermissionUnit)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
rpc md_permissions_data(bts.api.VoidMessage) returns (stream UserSymbolMDPermissionUnit);
|
||||
}
|
||||
@@ -101,9 +101,6 @@ message CustomOrderDefinition
|
||||
OCOOrderDefinition oco = 25;
|
||||
PricerOrderDefinition pricer = 26;
|
||||
CompositeOrderDefinition composite = 27;
|
||||
SplitterOrderDefinition splitter = 29;
|
||||
ScratchOrderDefinition scratch = 31;
|
||||
ProRataOrderDefinition prorata = 32;
|
||||
|
||||
DynamicOrderDefinition dynamic = 1000;
|
||||
}
|
||||
@@ -207,29 +204,6 @@ message CompositeOrderDefinition {
|
||||
uint32 algo_template_key = 1;
|
||||
CustomOrderDefinition definition = 2;
|
||||
}
|
||||
message SplitterOrderDefinition {
|
||||
uint32 max_order_qty = 1;
|
||||
uint32 exposed_qty = 2;
|
||||
// uint32 target_qty = 3;
|
||||
CustomOrderDefinition child = 10;
|
||||
}
|
||||
message ScratchOrderDefinition {
|
||||
spark.QuantityMeasure scratch_threshold = 1;
|
||||
uint32 chase_ticks = 2;
|
||||
CustomOrderDefinition child = 10;
|
||||
}
|
||||
message ProRataOrderDefinition {
|
||||
uint32 hedge_ticks = 1;
|
||||
uint32 chase_ticks = 2;
|
||||
spark.QuantityMeasure fill_limit = 5;
|
||||
spark.QuantityMeasure stay_in_threshold = 6;
|
||||
bool enable_scratch_on_overfill = 7;
|
||||
bool enable_stay_in = 8;
|
||||
|
||||
CustomOrderDefinition child = 10;
|
||||
CustomOrderDefinition hedge = 20;
|
||||
CustomOrderDefinition overfill_hedge = 30;
|
||||
}
|
||||
message DynamicOrderDefinition {
|
||||
string dynamic_order_type = 1;
|
||||
map<string, DynamicOrderParam> order_params = 2;
|
||||
@@ -279,12 +253,9 @@ message OrderState {
|
||||
TriggerOrderState trigger = 21;
|
||||
CoverOrderState cover = 22;
|
||||
OffsetOrderState offset = 24;
|
||||
QueueHolderState queue_holder = 30;
|
||||
OCOOrderState oco = 25;
|
||||
PricerOrderState pricer = 26;
|
||||
QueueHolderState queue_holder = 30;
|
||||
SplitterOrderState splitter = 31;
|
||||
ScratchOrderState scratch = 32;
|
||||
ProRataOrderState prorata = 33;
|
||||
|
||||
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
|
||||
}
|
||||
@@ -361,11 +332,8 @@ message CoverOrderState {}
|
||||
message OffsetOrderState {}
|
||||
message OCOOrderState {}
|
||||
message PricerOrderState {}
|
||||
message ScratchOrderState {}
|
||||
message ProRataOrderState {}
|
||||
message DynamicOrderState {}
|
||||
message QueueHolderState {}
|
||||
message SplitterOrderState {}
|
||||
|
||||
enum OrderStatus {
|
||||
|
||||
@@ -583,7 +551,6 @@ enum DynamicOrderPrimitiveDescriptor {
|
||||
DYNAMIC_ORDER_PRIMITIVE_STRING = 3;
|
||||
DYNAMIC_ORDER_PRIMITIVE_BOOL = 4;
|
||||
DYNAMIC_ORDER_PRIMITIVE_ORDER_DEF = 5;
|
||||
DYNAMIC_ORDER_PRIMITIVE_QUANTITY = 6;
|
||||
}
|
||||
|
||||
message DynamicOrderTypeDescriptor {
|
||||
@@ -632,7 +599,6 @@ message DynamicOrderParam {
|
||||
DynamicOrderArray as_array = 7;
|
||||
DynamicOrderStruct as_struct = 8;
|
||||
DynamicOrderEnumerated as_enumerated = 9;
|
||||
spark.QuantityMeasure as_quantity = 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,6 @@ MarketDataConfig {
|
||||
}
|
||||
}
|
||||
|
||||
PublisherOptions {
|
||||
rule: "DeploymentType.type == 'dev'"
|
||||
value {
|
||||
disable_heartbeat: true
|
||||
}
|
||||
}
|
||||
|
||||
PlatformMap {
|
||||
value {
|
||||
glbx_platform: "PLATFORM_MDP3ONIXS"
|
||||
|
||||
@@ -34,9 +34,9 @@ class IntPrice {
|
||||
#endif
|
||||
|
||||
public:
|
||||
constexpr IntPrice() : _mantissa(0), _exponent(127) {/*nan*/}
|
||||
constexpr IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {}
|
||||
constexpr IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {}
|
||||
IntPrice() : _mantissa(0), _exponent(127) {/*nan*/}
|
||||
IntPrice(int64_t mantissa, int8_t exponent) : _mantissa(mantissa), _exponent(exponent) {}
|
||||
IntPrice(const IntPrice& other) : _mantissa(other._mantissa), _exponent(other._exponent) {}
|
||||
|
||||
bool operator==(const IntPrice& other) const {
|
||||
if(_exponent == other._exponent) {
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
extern PriceSize NO_MARKET;
|
||||
static_assert(sizeof(PriceSize) == 16, "PriceSize should be small");
|
||||
|
||||
template <uint8_t PRECISION=11>
|
||||
template <uint8_t PRECISION>
|
||||
PriceSize PriceSize_from_double(double price, Size size) {
|
||||
if (size == 0 || os::is_nan(price)) return NO_MARKET;
|
||||
return { IntPrice_from_double<PRECISION>(price), size };
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <pugixml/pugixml.hpp>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
using namespace std;
|
||||
@@ -57,8 +56,6 @@ bool check_file_exists_with_instructions(const string& filename, const string& u
|
||||
bool is_pow_two_or_0(uint32_t x) { return (x & (~x + 1)) == x; }
|
||||
|
||||
void await_cme_config() {
|
||||
static std::mutex config_mutex;
|
||||
std::lock_guard<std::mutex> guard(config_mutex);
|
||||
std::string config_url;
|
||||
std::string templates_url;
|
||||
|
||||
@@ -115,7 +112,6 @@ const std::vector<CMEChannel> get_configured_cme_channels() {
|
||||
return parse_channels(symbols);
|
||||
}
|
||||
const vector<CMEChannel> parse_channels(const vector<UserSymbol>& symbols) {
|
||||
await_cme_config();
|
||||
vector<CMEChannel> result;
|
||||
bool skip_options = config::get<PublisherOptions>().skip_options();
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "lib/md/protos/co2.pb.h"
|
||||
#include "lib/md/SecurityIDRecorder.h"
|
||||
#include "lib/config/protos/system.pb.h"
|
||||
#include "lib/config/protos/MarketData.pb.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
@@ -174,7 +173,7 @@ void replay_file(const std::string& filename, IRelayWriter& writer, int32_t nagg
|
||||
} while (loop);
|
||||
}
|
||||
|
||||
bool replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms, bool loop) {
|
||||
void replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms) {
|
||||
// split up url parts
|
||||
size_t http_len = BTS_ARRAYSIZE("http://") - 1;
|
||||
auto after_host = url.find('/', http_len);
|
||||
@@ -185,53 +184,21 @@ bool replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms,
|
||||
filename = os::sparkdata_path("data/replay/") + filename;
|
||||
utils::download_file_if_newer(host, path, filename);
|
||||
if (boost::filesystem::exists(filename)) {
|
||||
replay_file(filename, writer, naggle_ms, loop);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms, bool loop) {
|
||||
if (!replay_url(url, writer, naggle_ms, loop)) {
|
||||
const auto fallback = config::get<ReplayOptions>().fallback();
|
||||
if (boost::filesystem::exists(fallback)) {
|
||||
replay_file(fallback, writer, naggle_ms, loop);
|
||||
}
|
||||
replay_file(filename, writer, naggle_ms, true);
|
||||
}
|
||||
}
|
||||
|
||||
struct SecurityExtractorDone : public std::bad_exception {};
|
||||
struct SecurityExtractor : public IRelayWriter {
|
||||
size_t _count;
|
||||
std::function<void(const api::Security&)> _cb;
|
||||
std::set<uint32_t> _keys;
|
||||
SecurityExtractor(std::function<void(const api::Security&)>& cb, size_t count)
|
||||
: _count(count), _cb(cb), _keys() { }
|
||||
~SecurityExtractor() override {};
|
||||
void write(const co2::MDReply& data, bool*) override {
|
||||
for (auto const &ch : data.channels()) {
|
||||
for (auto const &cr : ch.updates()) {
|
||||
if (cr.has_security() && _keys.find(cr.security().id()) == _keys.end()) {
|
||||
_keys.insert(cr.security().id());
|
||||
_cb(cr.security());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_keys.size() >= _count) {
|
||||
throw SecurityExtractorDone();
|
||||
}
|
||||
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms) {
|
||||
// loops forever
|
||||
replay_url(url, writer, naggle_ms);
|
||||
// If it returns it means it failed to download or play cached
|
||||
const auto fallback = config::get<ReplayOptions>().fallback();
|
||||
if (boost::filesystem::exists(fallback)) {
|
||||
replay_file(fallback, writer, naggle_ms);
|
||||
}
|
||||
};
|
||||
|
||||
void get_securities_from_replay(std::function<void(const api::Security&)> cb) {
|
||||
SecurityExtractor extractor(cb, 100);
|
||||
auto url = config::get<ReplayOptions>().url();
|
||||
try {
|
||||
replay_url_with_fallback(url, extractor, 0, false);
|
||||
} catch(const SecurityExtractorDone&) { }
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<IRelayWriter> mkrelayproducer(bool shared);
|
||||
|
||||
/*this is the public one*/
|
||||
|
||||
@@ -29,10 +29,9 @@ void print_recording(const std::string&);
|
||||
void unprint_recording(const std::string& src, const std::string& dest);
|
||||
void trace_recording(const std::string& file, security_key key);
|
||||
void replay_recording(const std::string&, int32_t delay_ms, const std::string fallback="");
|
||||
bool replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms, bool loop=true);
|
||||
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms, bool loop=true);
|
||||
void replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms);
|
||||
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms);
|
||||
void replay_file(const std::string& filename, IRelayWriter& writer, int32_t naggle_ms, bool loop=false);
|
||||
void get_securities_from_replay(std::function<void(const api::Security&)>);
|
||||
|
||||
}}//namespace
|
||||
|
||||
@@ -150,18 +150,9 @@ class OrderStateUpdateEvent : public mercury::Event<OrderStateUpdateEvent> {
|
||||
case api::orderman::OrderState::kPricer:
|
||||
result.mutable_pricer();
|
||||
break;
|
||||
case api::orderman::OrderState::kScratch:
|
||||
result.mutable_scratch();
|
||||
break;
|
||||
case api::orderman::OrderState::kProrata:
|
||||
result.mutable_prorata();
|
||||
break;
|
||||
case api::orderman::OrderState::kDynamic:
|
||||
result.mutable_dynamic();
|
||||
break;
|
||||
case api::orderman::OrderState::kSplitter:
|
||||
result.mutable_splitter();
|
||||
break;
|
||||
case api::orderman::OrderState::CUSTOM_NOT_SET:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "lib/orderman/orderman_utils/orderman_common.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
#include "lib/md/protos/co2.pb.h"
|
||||
#include "boost/container/small_vector.hpp"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
@@ -28,10 +27,9 @@ class TickerUpdateEvent : public mercury::Event<TickerUpdateEvent> {
|
||||
co2::AggressorSide aggressor_side;
|
||||
};
|
||||
|
||||
boost::container::small_vector<TickerUpdateEvent::Trade,5> trades;
|
||||
boost::container::small_vector<Trade,5> trades;
|
||||
TickerUpdateEvent(SecurityKey key) : _key(key), trades() {}
|
||||
|
||||
};
|
||||
typedef boost::container::small_vector<TickerUpdateEvent::Trade,5> TradeVector;
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,7 @@
|
||||
#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/SplitterAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
|
||||
#include "lib/orderman/orders/book_algos/OCOOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/PricerOrder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
|
||||
@@ -197,14 +194,6 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
case CustomOrderDefinition::kPricer:
|
||||
order_ptr.reset(new orderman::PricerOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kScratch:
|
||||
order_ptr.reset(
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<ScratchAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::kProrata:
|
||||
order_ptr.reset(
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<ProRataAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::kDynamic: {
|
||||
order_ptr = me->dynamic_order_registry.build(
|
||||
def.custom_def().dynamic().dynamic_order_type());
|
||||
@@ -219,12 +208,8 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
case CustomOrderDefinition::kComposite:
|
||||
order_ptr.reset(new orderman::CompositeOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kSplitter:
|
||||
order_ptr.reset(
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<splitter::SplitterAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
bts_assert_msg(false, "Cannot create order with unknown type");
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
order_ptr->_key = k;
|
||||
|
||||
@@ -54,127 +54,5 @@ bool should_forward_attachment(
|
||||
}
|
||||
}
|
||||
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(int32_t v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.set_as_int32(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(uint32_t v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.set_as_uint32(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(double v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.set_as_double(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(std::string v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.set_as_string(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(bool v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.set_as_bool(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::CustomOrderDefinition& v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.mutable_as_order_def()->CopyFrom(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::DynamicOrderArray& v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.mutable_as_array()->CopyFrom(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::DynamicOrderStruct& v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.mutable_as_struct()->CopyFrom(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::DynamicOrderEnumerated& v) {
|
||||
api::orderman::DynamicOrderParam res;
|
||||
res.mutable_as_enumerated()->CopyFrom(v);
|
||||
return res;
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_array(const std::vector<api::orderman::DynamicOrderParam>& v) {
|
||||
api::orderman::DynamicOrderArray a;
|
||||
for (const auto& item : v) {
|
||||
a.add_element()->CopyFrom(item);
|
||||
}
|
||||
return dynamic_parameter(a);
|
||||
}
|
||||
api::orderman::DynamicOrderParam dynamic_map(
|
||||
const std::vector<std::tuple<std::string,api::orderman::DynamicOrderParam>>& v) {
|
||||
DynamicOrderStruct res;
|
||||
auto& map = *res.mutable_field();
|
||||
for (const auto& tup : v) {
|
||||
map[std::get<std::string>(tup)] = std::get<1>(tup);
|
||||
}
|
||||
return dynamic_parameter(res);
|
||||
}
|
||||
|
||||
//
|
||||
// Registration
|
||||
|
||||
api::orderman::DynamicOrderParamDescriptor param_int32(const std::string& name) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
doap.mutable_param_type()->set_primitive(api::orderman::DYNAMIC_ORDER_PRIMITIVE_INT);
|
||||
return doap;
|
||||
}
|
||||
|
||||
api::orderman::DynamicOrderParamDescriptor param_uint32(const std::string& name) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
doap.mutable_param_type()->set_primitive(api::orderman::DYNAMIC_ORDER_PRIMITIVE_UINT);
|
||||
return doap;
|
||||
}
|
||||
|
||||
api::orderman::DynamicOrderParamDescriptor param_double(const std::string& name) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
doap.mutable_param_type()->set_primitive(api::orderman::DYNAMIC_ORDER_PRIMITIVE_DOUBLE);
|
||||
return doap;
|
||||
}
|
||||
|
||||
api::orderman::DynamicOrderParamDescriptor param_string(const std::string& name) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
doap.mutable_param_type()->set_primitive(api::orderman::DYNAMIC_ORDER_PRIMITIVE_STRING);
|
||||
return doap;
|
||||
}
|
||||
|
||||
api::orderman::DynamicOrderParamDescriptor param_bool(const std::string& name) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
doap.mutable_param_type()->set_primitive(api::orderman::DYNAMIC_ORDER_PRIMITIVE_BOOL);
|
||||
return doap;
|
||||
}
|
||||
|
||||
api::orderman::DynamicOrderParamDescriptor param_order(const std::string& name) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
doap.mutable_param_type()->set_primitive(api::orderman::DYNAMIC_ORDER_PRIMITIVE_ORDER_DEF);
|
||||
return doap;
|
||||
}
|
||||
api::orderman::DynamicOrderParamDescriptor param_array(const std::string& name, const DynamicOrderTypeDescriptor& entry) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
doap.mutable_param_type()->mutable_array()->mutable_element_type()->CopyFrom(entry);
|
||||
return doap;
|
||||
}
|
||||
api::orderman::DynamicOrderParamDescriptor param_struct(const std::string& name, const std::vector<DynamicOrderParamDescriptor>& entries) {
|
||||
api::orderman::DynamicOrderParamDescriptor doap;
|
||||
doap.set_param_name(name);
|
||||
api::orderman::DynamicOrderStructDescriptor& dstruct = *doap.mutable_param_type()->mutable_structure();
|
||||
for (const auto& field : entries) {
|
||||
*dstruct.add_field() = field;
|
||||
}
|
||||
return doap;
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
@@ -32,74 +30,5 @@ inline bool is_parent_order(const bts::api::orderman::OrderRecord &ord) {
|
||||
bool should_forward_attachment(const api::orderman::CustomOrderDefinition &def);
|
||||
bool can_consume_attachment(api::orderman::CustomOrderDefinition::CustomCase) ;
|
||||
|
||||
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(int32_t);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(uint32_t);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(double);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(std::string);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(bool);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::CustomOrderDefinition&);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::DynamicOrderArray&);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::DynamicOrderStruct&);
|
||||
api::orderman::DynamicOrderParam dynamic_parameter(const api::orderman::DynamicOrderEnumerated&);
|
||||
api::orderman::DynamicOrderParam dynamic_array(const std::vector<api::orderman::DynamicOrderParam>&);
|
||||
api::orderman::DynamicOrderParam dynamic_map( const std::vector<std::tuple<std::string,api::orderman::DynamicOrderParam>>&);
|
||||
|
||||
template <class T>
|
||||
std::optional<T> get_if(const api::orderman::DynamicOrderParam&) { return nullptr; }
|
||||
|
||||
template <>
|
||||
inline std::optional<int32_t> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsInt32 ? std::optional<int32_t>{p.as_int32()} : std::nullopt;
|
||||
}
|
||||
template <>
|
||||
inline std::optional<uint32_t> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsUint32 ? std::optional<uint32_t>{p.as_uint32()} : std::nullopt;
|
||||
}
|
||||
template <>
|
||||
inline std::optional<double> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsDouble ? std::optional<double>{p.as_double()} : std::nullopt;
|
||||
}
|
||||
template <>
|
||||
inline std::optional<bool> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsBool ? std::optional<bool>{p.as_bool()} : std::nullopt;
|
||||
}
|
||||
template <>
|
||||
inline std::optional<api::orderman::CustomOrderDefinition> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsOrderDef ? std::optional<api::orderman::CustomOrderDefinition>{p.as_order_def()} : std::nullopt;
|
||||
}
|
||||
template <>
|
||||
inline std::optional<api::orderman::DynamicOrderArray> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsArray ? std::optional<api::orderman::DynamicOrderArray>{p.as_array()} : std::nullopt;
|
||||
}
|
||||
template <>
|
||||
inline std::optional<api::orderman::DynamicOrderStruct> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsStruct ? std::optional<api::orderman::DynamicOrderStruct>{p.as_struct()} : std::nullopt;
|
||||
}
|
||||
template <>
|
||||
inline std::optional<api::orderman::DynamicOrderEnumerated> get_if(const api::orderman::DynamicOrderParam& p) {
|
||||
return p.value_case() == api::orderman::DynamicOrderParam::kAsEnumerated ? std::optional<api::orderman::DynamicOrderEnumerated>{p.as_enumerated()} : std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
std::optional<T> get_if(const google::protobuf::Map<std::string,api::orderman::DynamicOrderParam>& map, const std::string& key) {
|
||||
auto c = map.find(key);
|
||||
if (c != map.end()) {
|
||||
return get_if<T>(c->second);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
api::orderman::DynamicOrderParamDescriptor param_int32(const std::string& name);
|
||||
api::orderman::DynamicOrderParamDescriptor param_uint32(const std::string& name);
|
||||
api::orderman::DynamicOrderParamDescriptor param_double(const std::string& name);
|
||||
api::orderman::DynamicOrderParamDescriptor param_string(const std::string& name);
|
||||
api::orderman::DynamicOrderParamDescriptor param_bool(const std::string& name);
|
||||
api::orderman::DynamicOrderParamDescriptor param_order(const std::string& name);
|
||||
api::orderman::DynamicOrderParamDescriptor param_array(const std::string& name, const api::orderman::DynamicOrderTypeDescriptor&);
|
||||
api::orderman::DynamicOrderParamDescriptor param_struct(const std::string& name, const std::vector<api::orderman::DynamicOrderParamDescriptor>&);
|
||||
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -16,13 +16,7 @@ namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
template <class K, class V> using Map = boost::container::flat_map<K, V>;
|
||||
enum class AlgoWrapperOrder::Status {
|
||||
INACTIVE,
|
||||
W_ATTACH,
|
||||
WORKING,
|
||||
SHUTTING_DOWN,
|
||||
COMPLETED
|
||||
};
|
||||
enum class AlgoWrapperOrder::Status { INACTIVE, W_ATTACH, WORKING, COMPLETED };
|
||||
|
||||
class ChildTemplate {
|
||||
public:
|
||||
@@ -55,7 +49,6 @@ struct AlgoWrapperOrder::PImpl {
|
||||
OrderState const &state() const { return _state; }
|
||||
|
||||
Map<SecurityKey, OrderServices::BookSubID> book_subs;
|
||||
Map<SecurityKey, OrderServices::TickerSubID> ticker_subs;
|
||||
Map<OrderKey, ChildRecord> children;
|
||||
bool has_timer_sub = false;
|
||||
|
||||
@@ -69,7 +62,6 @@ struct AlgoWrapperOrder::PImpl {
|
||||
OrderKey attach_target = null_order_key();
|
||||
|
||||
std::optional<api::orderman::Update> pending_update;
|
||||
std::optional<std::pair<OrderStatus, std::string>> pending_terminate;
|
||||
OrderStatus external_status() const { return _state.status(); }
|
||||
|
||||
void set_external_status(OrderStatus s) {
|
||||
@@ -230,7 +222,6 @@ OrderOpResult AlgoWrapperOrder::on_activate(Price p, Quantity q,
|
||||
return {};
|
||||
|
||||
case Status::W_ATTACH:
|
||||
case Status::SHUTTING_DOWN:
|
||||
case Status::WORKING:
|
||||
bts_assert(false);
|
||||
}
|
||||
@@ -247,7 +238,6 @@ OrderOpResult AlgoWrapperOrder::on_update(Update const &update) {
|
||||
me->pending_update = update;
|
||||
return {};
|
||||
case Status::COMPLETED:
|
||||
case Status::SHUTTING_DOWN:
|
||||
return {};
|
||||
case Status::WORKING: {
|
||||
me->set_price_quantity(Price(update.price()),
|
||||
@@ -285,7 +275,6 @@ orderman::OrderOpResult AlgoWrapperOrder::on_detach() {
|
||||
switch (me->status) {
|
||||
case Status::W_ATTACH:
|
||||
case Status::WORKING:
|
||||
case Status::SHUTTING_DOWN:
|
||||
for (auto &[_, c] : me->children) {
|
||||
(void)_;
|
||||
c.order.mark_detached_from_parent();
|
||||
@@ -320,7 +309,6 @@ orderman::OrderOpResult AlgoWrapperOrder::on_cancel() {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Status::SHUTTING_DOWN:
|
||||
case Status::COMPLETED:
|
||||
break;
|
||||
case Status::INACTIVE:
|
||||
@@ -359,20 +347,13 @@ Quantity AlgoWrapperOrder::total_fills() const {
|
||||
return Quantity(me->state().filled_quantity());
|
||||
}
|
||||
|
||||
bool AlgoWrapperOrder::is_shutting_down() const {
|
||||
return me->status == Status::SHUTTING_DOWN;
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::subscribe_book(SecurityKey k) {
|
||||
if (!is_working())
|
||||
return;
|
||||
bts_assert(me->book_subs.find(k) == me->book_subs.end());
|
||||
|
||||
me->book_subs[k] = me->services->subscribe_to_book(
|
||||
k, [this, k](auto const &ev) {
|
||||
ReportStateGuard _(*this);
|
||||
me->algo->on_book(k, ev.book);
|
||||
});
|
||||
k, [this, k](auto const &ev) { me->algo->on_book(k, ev.book); });
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::unsubscribe_book(SecurityKey k) {
|
||||
@@ -383,24 +364,6 @@ void AlgoWrapperOrder::unsubscribe_book(SecurityKey k) {
|
||||
me->services->unsubscribe_from_book(it->second);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::subscribe_ticker(SecurityKey k) {
|
||||
if (!is_working()) return;
|
||||
bts_assert(me->ticker_subs.find(k) == me->ticker_subs.end());
|
||||
|
||||
me->ticker_subs[k] = me->services->subscribe_to_ticker(
|
||||
k, [this,k](const auto& ev) {
|
||||
ReportStateGuard _(*this);
|
||||
me->algo->on_ticker(k, ev.trades);
|
||||
});
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::unsubscribe_ticker(SecurityKey k) {
|
||||
if (!is_working()) return;
|
||||
bts_assert(me->ticker_subs.find(k) != me->ticker_subs.end());
|
||||
if (auto it = me->ticker_subs.find(k); it != me->ticker_subs.end())
|
||||
me->services->unsubscribe_from_ticker(it->second);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::subscribe_child_change(OrderKey k) {
|
||||
if (!is_working())
|
||||
return;
|
||||
@@ -429,10 +392,7 @@ void AlgoWrapperOrder::subscribe_timer() {
|
||||
bts_assert(!me->has_timer_sub);
|
||||
me->has_timer_sub = true;
|
||||
me->services->subscribe_to_time(
|
||||
[this](auto const &) {
|
||||
ReportStateGuard _(*this);
|
||||
me->algo->on_timer();
|
||||
});
|
||||
[this](auto const &) { me->algo->on_timer(); });
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::unsubscribe_timer() {
|
||||
@@ -525,9 +485,10 @@ void AlgoWrapperOrder::detach_all_children() {
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::update_fill_state(Quantity q, Price p) {
|
||||
if (!is_working() && !is_shutting_down())
|
||||
if (!is_working())
|
||||
return;
|
||||
|
||||
ReportStateGuard _(*this);
|
||||
auto previous_qty = me->state().filled_quantity();
|
||||
if (previous_qty > q.value()) {
|
||||
LOG(OrderMan, Error,
|
||||
@@ -549,37 +510,35 @@ void AlgoWrapperOrder::update_fill_state(Quantity q, Price p) {
|
||||
void AlgoWrapperOrder::set_ladder_visibility(bool is_visible) {
|
||||
if (!is_working())
|
||||
return;
|
||||
|
||||
ReportStateGuard _(*this);
|
||||
me->set_ladder_visibility(is_visible);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::cancel(std::string const &msg) {
|
||||
if (!is_working())
|
||||
return;
|
||||
if (working_children_count() == 0)
|
||||
terminate(ORDER_STATUS_CANCELED, msg);
|
||||
else
|
||||
shutdown(ORDER_STATUS_CANCELED, msg);
|
||||
ReportStateGuard _(*this);
|
||||
terminate(ORDER_STATUS_CANCELED, msg);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::terminate_error(std::string const &msg) {
|
||||
if (!is_working() && !is_shutting_down() && me->status != Status::W_ATTACH)
|
||||
if (!is_working() && me->status != Status::W_ATTACH)
|
||||
return;
|
||||
ReportStateGuard _(*this);
|
||||
// If we attached and an error was signaled we do not want to
|
||||
// cancel our child because that child might be a long running
|
||||
// order with a good queue position that we want to preserve
|
||||
if (is_attaching())
|
||||
me->children.erase(me->attach_target);
|
||||
|
||||
terminate(ORDER_STATUS_ERROR, msg);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::complete() {
|
||||
if (!is_working())
|
||||
return;
|
||||
if (working_children_count() == 0)
|
||||
terminate(ORDER_STATUS_COMPLETED);
|
||||
else
|
||||
shutdown(ORDER_STATUS_COMPLETED);
|
||||
ReportStateGuard _(*this);
|
||||
terminate(ORDER_STATUS_COMPLETED);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
@@ -614,7 +573,6 @@ void AlgoWrapperOrder::on_attach_complete(
|
||||
break;
|
||||
case Status::COMPLETED:
|
||||
break;
|
||||
case Status::SHUTTING_DOWN:
|
||||
case Status::INACTIVE:
|
||||
case Status::WORKING:
|
||||
bts_assert(false);
|
||||
@@ -629,7 +587,6 @@ void AlgoWrapperOrder::on_child_order_update(
|
||||
switch (me->status) {
|
||||
case Status::WORKING:
|
||||
case Status::W_ATTACH:
|
||||
case Status::SHUTTING_DOWN:
|
||||
if (auto it = me->children.find(child_key); it != me->children.end()) {
|
||||
auto &child_record = it->second;
|
||||
switch (new_child_state.status()) {
|
||||
@@ -668,38 +625,16 @@ void AlgoWrapperOrder::on_child_order_update(
|
||||
break;
|
||||
}
|
||||
} else
|
||||
LOG(OrderMan, Info,
|
||||
"AlgoOrder {} received update for presumably detached order {}. "
|
||||
"Ignoring it",
|
||||
key().value(), child_key.value());
|
||||
break;
|
||||
terminate_error(
|
||||
fmt::format("Unknown child order update: {}", child_key.value()));
|
||||
return;
|
||||
case Status::COMPLETED:
|
||||
return;
|
||||
case Status::INACTIVE:
|
||||
bts_assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (me->status == Status::SHUTTING_DOWN)
|
||||
try_complete_shutdown();
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::try_complete_shutdown() {
|
||||
bts_assert(me->status == Status::SHUTTING_DOWN);
|
||||
bts_assert(me->children.size());
|
||||
bts_assert(me->pending_terminate.has_value());
|
||||
|
||||
if (working_children_count() == 0) {
|
||||
LOG(OrderMan, Info, "AlgoOrder {} completed shutdown. Terminated",
|
||||
key().value());
|
||||
terminate(me->pending_terminate.value().first,
|
||||
me->pending_terminate.value().second);
|
||||
} else {
|
||||
LOG(OrderMan, Info,
|
||||
"AlgoOrder {} still has {} orders working. Shutdown still in "
|
||||
"progress",
|
||||
key().value(), working_children_count());
|
||||
}
|
||||
bts_assert(false);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::setup_transitions() {
|
||||
@@ -741,21 +676,8 @@ void AlgoWrapperOrder::setup_transitions() {
|
||||
// From WORKING
|
||||
me->transition_actions[{Status::WORKING, Status::COMPLETED}] = [this]() {
|
||||
cleanup();
|
||||
me->children.clear();
|
||||
_transition(Status::COMPLETED);
|
||||
};
|
||||
me->transition_actions[{Status::WORKING, Status::SHUTTING_DOWN}] = [this]() {
|
||||
cleanup();
|
||||
_transition(Status::SHUTTING_DOWN);
|
||||
};
|
||||
|
||||
// From SHUTTING_DOWN
|
||||
me->transition_actions[{Status::SHUTTING_DOWN, Status::COMPLETED}] =
|
||||
[this]() {
|
||||
me->children.clear();
|
||||
me->pending_terminate.reset();
|
||||
_transition(Status::COMPLETED);
|
||||
};
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::check_invariants() {
|
||||
@@ -775,7 +697,6 @@ void AlgoWrapperOrder::check_invariants() {
|
||||
bts_assert(me->book_subs.empty());
|
||||
bts_assert(me->children.empty());
|
||||
bts_assert(!me->has_timer_sub);
|
||||
bts_assert(!me->pending_terminate.has_value());
|
||||
break;
|
||||
|
||||
case Status::WORKING:
|
||||
@@ -783,23 +704,10 @@ void AlgoWrapperOrder::check_invariants() {
|
||||
bts_assert(me->security);
|
||||
bts_assert(me->external_status() == ORDER_STATUS_WORKING);
|
||||
bts_assert(!me->pending_update.has_value());
|
||||
bts_assert(!me->pending_terminate.has_value());
|
||||
break;
|
||||
|
||||
case Status::SHUTTING_DOWN:
|
||||
bts_assert(working_children_count());
|
||||
bts_assert(me->services);
|
||||
bts_assert(me->security);
|
||||
bts_assert(me->external_status() == ORDER_STATUS_WORKING);
|
||||
bts_assert(me->book_subs.empty());
|
||||
bts_assert(!me->has_timer_sub);
|
||||
bts_assert(!me->pending_update.has_value());
|
||||
bts_assert(me->pending_terminate.has_value());
|
||||
break;
|
||||
|
||||
case Status::COMPLETED:
|
||||
bts_assert(me->children.empty());
|
||||
bts_assert(!me->pending_update.has_value());
|
||||
bts_assert(me->external_status() == ORDER_STATUS_CANCELED ||
|
||||
me->external_status() == ORDER_STATUS_COMPLETED ||
|
||||
me->external_status() == ORDER_STATUS_ERROR ||
|
||||
@@ -807,7 +715,6 @@ void AlgoWrapperOrder::check_invariants() {
|
||||
bts_assert(me->book_subs.empty());
|
||||
bts_assert(!me->has_timer_sub);
|
||||
bts_assert(!me->pending_update.has_value());
|
||||
bts_assert(!me->pending_terminate.has_value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -838,25 +745,15 @@ void AlgoWrapperOrder::terminate(api::orderman::OrderStatus st,
|
||||
transition(Status::COMPLETED);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::shutdown(api::orderman::OrderStatus st,
|
||||
std::string const &error) {
|
||||
bts_assert(!me->pending_terminate.has_value());
|
||||
bts_assert(working_children_count());
|
||||
me->pending_terminate = std::make_pair(st, error);
|
||||
LOG(OrderMan, Info,
|
||||
"AlgoOrder {} is shutting down. Waiting for {} child orders to complete "
|
||||
"before terminating",
|
||||
key().value(), working_children_count());
|
||||
transition(Status::SHUTTING_DOWN);
|
||||
}
|
||||
|
||||
void AlgoWrapperOrder::cleanup() {
|
||||
for (auto const &s : me->book_subs) {
|
||||
me->services->unsubscribe_from_book(s.second);
|
||||
me->book_subs.clear();
|
||||
}
|
||||
for (auto &c : me->children)
|
||||
for (auto &c : me->children) {
|
||||
c.second.order.cancel();
|
||||
me->children.clear();
|
||||
}
|
||||
if (me->has_timer_sub)
|
||||
unsubscribe_timer();
|
||||
me->pending_update.reset();
|
||||
@@ -888,16 +785,6 @@ OrderKey AlgoWrapperOrder::index_and_launch_child(ChildOrder &&child, Price p,
|
||||
return k;
|
||||
}
|
||||
|
||||
size_t AlgoWrapperOrder::working_children_count() const {
|
||||
auto working_children_count = 0;
|
||||
for (auto &c : me->children)
|
||||
if (!c.second.state.has_value() ||
|
||||
c.second.state.value().status == ChildStatus::WORKING) {
|
||||
working_children_count++;
|
||||
}
|
||||
return working_children_count;
|
||||
}
|
||||
|
||||
bool AlgoWrapperOrder::is_working() const {
|
||||
return me->status == Status::WORKING;
|
||||
}
|
||||
@@ -912,8 +799,6 @@ log::CharPtrWrapper AlgoWrapperOrder::state_name(Status st) {
|
||||
return {"INACTIVE"};
|
||||
case Status::WORKING:
|
||||
return {"WORKING"};
|
||||
case Status::SHUTTING_DOWN:
|
||||
return {"SHUTTING_DOWN"};
|
||||
}
|
||||
bts_assert(false);
|
||||
return {""};
|
||||
|
||||
@@ -40,15 +40,12 @@ class AlgoWrapperOrder : public Order,
|
||||
Price price() const override;
|
||||
Quantity quantity() const override;
|
||||
Quantity total_fills() const override;
|
||||
bool is_shutting_down() const override;
|
||||
void subscribe_book(SecurityKey) override;
|
||||
void unsubscribe_book(SecurityKey) override;
|
||||
void subscribe_child_change(OrderKey) override;
|
||||
void unsubscribe_child_change(OrderKey) override;
|
||||
void subscribe_timer() override;
|
||||
void unsubscribe_timer() override;
|
||||
void subscribe_ticker(SecurityKey) override;
|
||||
void unsubscribe_ticker(SecurityKey) override;
|
||||
OrderKey launch_child(ChildTemplate &, Price, Quantity,
|
||||
bool subscribe = true) override;
|
||||
OrderKey launch_child(api::orderman::OrderDefinition const &, Price,
|
||||
@@ -78,13 +75,10 @@ class AlgoWrapperOrder : public Order,
|
||||
void transition(Status);
|
||||
void _transition(Status);
|
||||
void terminate(api::orderman::OrderStatus, std::string const &error = "");
|
||||
void shutdown(api::orderman::OrderStatus, std::string const &error = "");
|
||||
void try_complete_shutdown();
|
||||
void cleanup();
|
||||
void update_child_record(api::orderman::OrderState const &, ChildRecord *);
|
||||
OrderKey index_and_launch_child(ChildOrder &&, Price, Quantity, bool);
|
||||
bool is_working() const ;
|
||||
size_t working_children_count() const;
|
||||
log::CharPtrWrapper state_name(Status) ;
|
||||
ChildStatus map_child_status(api::orderman::OrderStatus) ;
|
||||
bool _is_attaching(api::orderman::OrderDefinition const &) const ;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#include "DynamicHelpers.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
template<>
|
||||
bool has_custom<api::orderman::Update>(const api::orderman::Update& orig)
|
||||
{
|
||||
return orig.has_custom();
|
||||
}
|
||||
|
||||
template<>
|
||||
const api::orderman::CustomOrderDefinition& custom<api::orderman::Update>(const api::orderman::Update& orig)
|
||||
{
|
||||
return orig.custom();
|
||||
}
|
||||
|
||||
template<>
|
||||
api::orderman::CustomOrderDefinition& mutable_custom<api::orderman::Update>(api::orderman::Update& orig)
|
||||
{
|
||||
return *orig.mutable_custom();
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
template<typename T>
|
||||
bool has_custom(const T& orig)
|
||||
{
|
||||
return orig.has_custom_def();
|
||||
}
|
||||
template<>
|
||||
bool has_custom(const api::orderman::Update& orig);
|
||||
|
||||
template<typename T>
|
||||
const api::orderman::CustomOrderDefinition& custom(const T& orig)
|
||||
{
|
||||
return orig.custom_def();
|
||||
}
|
||||
template<>
|
||||
const api::orderman::CustomOrderDefinition& custom<api::orderman::Update>(const api::orderman::Update& orig);
|
||||
|
||||
template<typename T>
|
||||
api::orderman::CustomOrderDefinition& mutable_custom(T& orig)
|
||||
{
|
||||
return *orig.mutable_custom_def();
|
||||
}
|
||||
template<>
|
||||
api::orderman::CustomOrderDefinition& mutable_custom<api::orderman::Update>(api::orderman::Update& orig);
|
||||
|
||||
|
||||
}}
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/Order.h"
|
||||
#include "lib/orderman/events/TickerUpdateEvent.h"
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
@@ -37,10 +36,7 @@ class IAlgoOrder {
|
||||
// The book for the specified security has changed
|
||||
virtual void on_book(SecurityKey, Book const &) {}
|
||||
|
||||
// Trades stream for the specified security
|
||||
virtual void on_ticker(SecurityKey, TradeVector const&) {};
|
||||
|
||||
// A periodic timer has fired
|
||||
// A period timer has run out
|
||||
virtual void on_timer() {}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
@@ -20,12 +20,6 @@ class IAlgoServices : public IBeforeLaunchAlgoServices {
|
||||
// Returns the filled quantity for this algo
|
||||
virtual Quantity total_fills() const = 0;
|
||||
|
||||
// Returns wheter the algo is in the process of shutting down (due
|
||||
// to a cancel, an error, or just having completed its work). When
|
||||
// algo is shutting down any requests to create, update or cancel
|
||||
// child orders will be ignored.
|
||||
virtual bool is_shutting_down() const = 0;
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Event Subscriptions
|
||||
// --------------------------------------------------------------
|
||||
@@ -44,10 +38,6 @@ class IAlgoServices : public IBeforeLaunchAlgoServices {
|
||||
virtual void subscribe_timer() = 0;
|
||||
virtual void unsubscribe_timer() = 0;
|
||||
|
||||
// Subscribe to trade ticker
|
||||
virtual void subscribe_ticker(SecurityKey) = 0;
|
||||
virtual void unsubscribe_ticker(SecurityKey) = 0;
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Child management
|
||||
// --------------------------------------------------------------
|
||||
@@ -96,7 +86,7 @@ class IAlgoServices : public IBeforeLaunchAlgoServices {
|
||||
|
||||
// Set wheter this algo instance should be displayed in the Spark UI ladder
|
||||
virtual void set_ladder_visibility(bool is_visible) = 0;
|
||||
|
||||
|
||||
// Update this algos's filled quantity and last price
|
||||
virtual void update_fill_state(Quantity total_fills, Price last_price) = 0;
|
||||
};
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
#include "lib/orderman/orders/algo/IBeforeLaunchAlgoServices.h"
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
TimeStamp IBeforeLaunchAlgoServices::get_time() const { return Timer::now(); }
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/domain_model/SecurityKey.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_common.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
@@ -43,8 +42,6 @@ class IBeforeLaunchAlgoServices {
|
||||
virtual ChildTemplate &
|
||||
make_child_template(api::orderman::OrderDefinition const &child_def,
|
||||
size_t reserve = 1) = 0;
|
||||
|
||||
virtual TimeStamp get_time() const;
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
|
||||
@@ -10,8 +10,18 @@ namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
enum class CoverAlgo::Status { WORKING, WAITING_FOR_HEDGES, DONE, CANCELING };
|
||||
|
||||
enum class CoverAlgo::Status { WORKING, WAITING_FOR_HEDGES, DONE };
|
||||
const char *CoverAlgo::status_name(Status st) {
|
||||
switch (st) {
|
||||
case Status::WORKING:
|
||||
return "WORKING";
|
||||
case Status::WAITING_FOR_HEDGES:
|
||||
return "WAITING_FOR_HEDGES";
|
||||
case Status::DONE:
|
||||
return "Done";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
struct CoverAlgo::PImpl {
|
||||
IAlgoServices *algo;
|
||||
OrderKey child_key = null_order_key();
|
||||
@@ -82,11 +92,8 @@ void CoverAlgo::do_launch_attached(IAlgoServices &algo, OrderKey k,
|
||||
on_child_change(k, st);
|
||||
}
|
||||
|
||||
void CoverAlgo::on_covered_change(ChildState const &st) {
|
||||
// First launch hedge if appropriate
|
||||
switch (me->status) {
|
||||
case Status::WORKING:
|
||||
case Status::CANCELING:
|
||||
void CoverAlgo::on_child_change(OrderKey k, ChildState const &st) {
|
||||
if (k == me->child_key) {
|
||||
if (st.total_fills.value() > me->hedged_fills.value()) {
|
||||
auto hedge_qty =
|
||||
Quantity(st.total_fills.value() - me->hedged_fills.value());
|
||||
@@ -109,15 +116,6 @@ void CoverAlgo::on_covered_change(ChildState const &st) {
|
||||
hedge_qty.value(), hedge_price.value());
|
||||
}
|
||||
algo().update_fill_state(st.total_fills, st.last_price);
|
||||
break;
|
||||
case Status::WAITING_FOR_HEDGES:
|
||||
case Status::DONE:
|
||||
break;
|
||||
}
|
||||
|
||||
// Now analyze child to figure out wheter we should terminate
|
||||
switch (me->status) {
|
||||
case Status::WORKING:
|
||||
switch (st.status) {
|
||||
case ChildStatus::WORKING:
|
||||
break;
|
||||
@@ -127,67 +125,36 @@ void CoverAlgo::on_covered_change(ChildState const &st) {
|
||||
algo().order_key().value());
|
||||
algo().set_ladder_visibility(false);
|
||||
me->status = Status::WAITING_FOR_HEDGES;
|
||||
terminate_if_hedges_complete();
|
||||
break;
|
||||
case ChildStatus::CANCELED:
|
||||
LOG(OrderMan, Info, "CoverAlgo {}. Child was unexpectadly canceled, terminating",
|
||||
LOG(OrderMan, Info, "CoverAlgo {}. Child was canceled, terminating",
|
||||
algo().order_key().value());
|
||||
on_done();
|
||||
me->status = Status::DONE;
|
||||
algo().terminate_error("Unexpected child cancel");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Status::CANCELING:
|
||||
} else {
|
||||
switch (st.status) {
|
||||
case ChildStatus::WORKING:
|
||||
break;
|
||||
case ChildStatus::CANCELED:
|
||||
case ChildStatus::COMPLETED:
|
||||
LOG(OrderMan, Info, "CoverAlgo {}. Shutdown complete",
|
||||
algo().order_key().value());
|
||||
on_done();
|
||||
algo().cancel();
|
||||
if (me->cancel_on_external_hedge_cancel) {
|
||||
LOG(OrderMan, Info, "CoverAlgo {}. Hedge was canceled, terminating",
|
||||
algo().order_key().value());
|
||||
me->status = Status::DONE;
|
||||
algo().terminate_error("Unexpected child cancel");
|
||||
} else
|
||||
me->hedges.erase(
|
||||
std::remove(me->hedges.begin(), me->hedges.end(), k),
|
||||
me->hedges.end());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Status::WAITING_FOR_HEDGES:
|
||||
case Status::DONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CoverAlgo::on_hedge_change(OrderKey k, ChildState const &st) {
|
||||
|
||||
// First adjust our hedge count or cancel if something went wrong
|
||||
switch (st.status) {
|
||||
case ChildStatus::WORKING:
|
||||
break;
|
||||
case ChildStatus::COMPLETED:
|
||||
me->hedges.erase(std::remove(me->hedges.begin(), me->hedges.end(), k),
|
||||
me->hedges.end());
|
||||
break;
|
||||
case ChildStatus::CANCELED:
|
||||
if (me->cancel_on_external_hedge_cancel) {
|
||||
LOG(OrderMan, Info, "CoverAlgo {}. Hedge was canceled, terminating",
|
||||
algo().order_key().value());
|
||||
on_done();
|
||||
algo().terminate_error("Unexpected child cancel");
|
||||
return;
|
||||
} else
|
||||
case ChildStatus::COMPLETED:
|
||||
me->hedges.erase(std::remove(me->hedges.begin(), me->hedges.end(), k),
|
||||
me->hedges.end());
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now if we are waiting for hedges to complete, bail
|
||||
terminate_if_hedges_complete();
|
||||
}
|
||||
|
||||
void CoverAlgo::on_child_change(OrderKey k, ChildState const &st) {
|
||||
if (k == me->child_key)
|
||||
on_covered_change(st);
|
||||
else
|
||||
on_hedge_change(k, st);
|
||||
check_terminate();
|
||||
}
|
||||
|
||||
void CoverAlgo::do_update(api::orderman::Update const &update) {
|
||||
@@ -244,7 +211,7 @@ void CoverAlgo::update_definition(
|
||||
cover.cancel_on_external_hedge_cancel();
|
||||
}
|
||||
|
||||
void CoverAlgo::terminate_if_hedges_complete() {
|
||||
void CoverAlgo::check_terminate() {
|
||||
LOG(OrderMan, Diag,
|
||||
"CoverAlgo {} check_terminate. State: {} Hedge count: {}",
|
||||
algo().order_key(), log::CharPtrWrapper{status_name(me->status)},
|
||||
@@ -253,61 +220,15 @@ void CoverAlgo::terminate_if_hedges_complete() {
|
||||
if (me->status == Status::WAITING_FOR_HEDGES && me->hedges.empty()) {
|
||||
LOG(OrderMan, Info, "CoverAlgo {}. Last hedge completed, terminating",
|
||||
algo().order_key().value());
|
||||
on_done();
|
||||
algo().complete();
|
||||
}
|
||||
}
|
||||
|
||||
IAlgoOrder::OnCancelResponse CoverAlgo::on_cancel() {
|
||||
switch (me->status) {
|
||||
case Status::WORKING: {
|
||||
algo().cancel_child(me->child_key);
|
||||
LOG(OrderMan, Info,
|
||||
"CoverAlgo {} received cancel. Canceling child and waiting for an "
|
||||
"ack",
|
||||
algo().order_key());
|
||||
me->status = Status::CANCELING;
|
||||
return IAlgoOrder::OnCancelResponse::IGNORE;
|
||||
}
|
||||
case Status::CANCELING:
|
||||
return IAlgoOrder::OnCancelResponse::IGNORE;
|
||||
case Status::WAITING_FOR_HEDGES:
|
||||
maybe_detach_hedges();
|
||||
return IAlgoOrder::OnCancelResponse::HONOR;
|
||||
case Status::DONE:
|
||||
bts_assert(false);
|
||||
return IAlgoOrder::OnCancelResponse::HONOR;
|
||||
}
|
||||
|
||||
bts_assert(false);
|
||||
return IAlgoOrder::OnCancelResponse::HONOR;
|
||||
}
|
||||
|
||||
void CoverAlgo::on_done()
|
||||
{
|
||||
me->status = Status::DONE;
|
||||
maybe_detach_hedges();
|
||||
}
|
||||
|
||||
|
||||
void CoverAlgo::maybe_detach_hedges() {
|
||||
if (!me->cancel_hedges_on_cancel)
|
||||
for (auto &k : me->hedges)
|
||||
algo().detach_child(k);
|
||||
}
|
||||
|
||||
const char *CoverAlgo::status_name(Status st) {
|
||||
switch (st) {
|
||||
case Status::WORKING:
|
||||
return "WORKING";
|
||||
case Status::WAITING_FOR_HEDGES:
|
||||
return "WAITING_FOR_HEDGES";
|
||||
case Status::DONE:
|
||||
return "DONE";
|
||||
case Status::CANCELING:
|
||||
return "CANCELING";
|
||||
}
|
||||
return nullptr;
|
||||
return IAlgoOrder::OnCancelResponse::HONOR;
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
|
||||
@@ -30,13 +30,8 @@ private:
|
||||
api::orderman::CoverOrderDefinition const &);
|
||||
void init_hedge_template(IBeforeLaunchAlgoServices &,
|
||||
api::orderman::CustomOrderDefinition const &);
|
||||
void terminate_if_hedges_complete();
|
||||
|
||||
void on_hedge_change(OrderKey hedge, ChildState const&) ;
|
||||
void on_covered_change(ChildState const&);
|
||||
void check_terminate();
|
||||
const char *status_name(Status st);
|
||||
void maybe_detach_hedges();
|
||||
void on_done();
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
#include "DormanAlgo.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_utils.h"
|
||||
#include "lib/orderman/orders/algo/utils/Accumulator.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace dorman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct FutureQty {
|
||||
Quantity size;
|
||||
TimeStamp when;
|
||||
};
|
||||
|
||||
struct DormanAlgo::Impl {
|
||||
IAlgoServices *algo = nullptr;
|
||||
AlgoParams params;
|
||||
api::Side side;
|
||||
Quantity exposed_qty = {};
|
||||
std::optional<FutureQty> next_size;
|
||||
|
||||
OrderDefinition splitter_definition;
|
||||
OrderKey child_id;
|
||||
ChildState child_state;
|
||||
|
||||
uint32_t fills ={};
|
||||
|
||||
std::vector<OrderKey> profit_orders;
|
||||
std::optional<Accumulator> trade_accumulator;
|
||||
|
||||
std::optional<TimeStamp> pause_until;
|
||||
};
|
||||
|
||||
DormanAlgo::DormanAlgo() :
|
||||
me(new Impl())
|
||||
{ }
|
||||
DormanAlgo::~DormanAlgo() { }
|
||||
|
||||
OrderOpResult DormanAlgo::do_before_launch(IBeforeLaunchAlgoServices& ibl) {
|
||||
const auto& def = ibl.definition();
|
||||
// Validation
|
||||
bts_assert(def.has_custom_def());
|
||||
bts_assert(def.custom_def().has_dynamic());
|
||||
|
||||
auto params = to_dorman_params(def.custom_def().dynamic());
|
||||
if (auto error = std::get_if<OrderOpResult>(¶ms)) {
|
||||
return *error;
|
||||
}
|
||||
me->params = std::get<AlgoParams>(params);
|
||||
me->side = def.side();
|
||||
if (me->side != api::Side::SIDE_BUY && me->side != api::Side::SIDE_SELL) {
|
||||
return OrderOpResult { ORDER_ERROR_INVALID_ORDER_DEFINITON, "Invalid side" };
|
||||
}
|
||||
|
||||
if (me->params.trade_accumulation) {
|
||||
auto& t = me->params.trade_accumulation.value();
|
||||
me->trade_accumulator = {Accumulator {t.contracts, t.seconds, ibl.get_time()}};
|
||||
}
|
||||
|
||||
CustomOrderDefinition default_order;
|
||||
default_order.mutable_limit();
|
||||
auto child_def =
|
||||
get_if<CustomOrderDefinition>(def.custom_def().dynamic().order_params(), "Child")
|
||||
.value_or(default_order);
|
||||
|
||||
update_child_template(ibl, child_def);
|
||||
|
||||
if (ibl.is_attaching()) {
|
||||
return OrderOpResult{ORDER_ERROR_OP_NOT_SUPPORTED,
|
||||
"This algo does not support attach."};
|
||||
}
|
||||
|
||||
return {/*ok*/};
|
||||
}
|
||||
|
||||
|
||||
void DormanAlgo::do_launch(IAlgoServices& algo) {
|
||||
me->algo = &algo;
|
||||
auto skey = SecurityKey(algo.security().id());
|
||||
algo.subscribe_book(skey);
|
||||
algo.subscribe_timer();
|
||||
if (me->trade_accumulator) {
|
||||
algo.subscribe_ticker(skey);
|
||||
}
|
||||
}
|
||||
|
||||
void DormanAlgo::do_launch_attached(IAlgoServices&, OrderKey, ChildState) {
|
||||
bts_assert(false);
|
||||
}
|
||||
void DormanAlgo::do_update(const api::orderman::Update& update) {
|
||||
LOG(OrderMan, Diag, "Dorman order {} receiving update: {}",
|
||||
algo().order_key().value(), utils::pb::to_string(update));
|
||||
|
||||
if (update.has_custom()) {
|
||||
if (!update.custom().has_dynamic()) {
|
||||
algo().cancel("Update cannot change order type");
|
||||
return;
|
||||
}
|
||||
auto params = to_dorman_params(update.custom().dynamic());
|
||||
|
||||
if (auto error = std::get_if<OrderOpResult>(¶ms)) {
|
||||
algo().cancel(error->error_msg);
|
||||
}
|
||||
|
||||
me->params = std::get<AlgoParams>(params);
|
||||
auto skey = SecurityKey(algo().security().id());
|
||||
if (me->params.trade_accumulation && !me->trade_accumulator) {
|
||||
algo().subscribe_ticker(skey);
|
||||
auto& t = me->params.trade_accumulation.value();
|
||||
me->trade_accumulator = {Accumulator {t.contracts, t.seconds, algo().get_time()}};
|
||||
}
|
||||
else if (!me->params.trade_accumulation && me->trade_accumulator) {
|
||||
algo().unsubscribe_ticker(skey);
|
||||
me->trade_accumulator = std::nullopt;
|
||||
}
|
||||
else if (me->params.trade_accumulation &&
|
||||
me->trade_accumulator &&
|
||||
(me->trade_accumulator.value().threshold != me->params.trade_accumulation.value().contracts ||
|
||||
me->trade_accumulator.value().bucket_interval != std::chrono::seconds(me->params.trade_accumulation.value().seconds))
|
||||
) {
|
||||
// accumulator params have changed
|
||||
auto& t = me->params.trade_accumulation.value();
|
||||
me->trade_accumulator = {Accumulator {t.contracts, t.seconds, algo().get_time()}};
|
||||
}
|
||||
|
||||
// Check for child
|
||||
auto child = get_if<CustomOrderDefinition>(update.custom().dynamic().order_params(), "Child");
|
||||
if (child) {
|
||||
update_child_template(algo(), child.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (me->fills >= update.quantity()) {
|
||||
complete();
|
||||
}
|
||||
else {
|
||||
// In case of price/qty change always update the child
|
||||
// NOTE: it might be useful to cache the available market data size
|
||||
// and update based on that if the update contains a size table change
|
||||
update_child(me->exposed_qty);
|
||||
}
|
||||
}
|
||||
|
||||
void DormanAlgo::on_child_change(OrderKey key, const ChildState& state) {
|
||||
if (key != me->child_id) return;
|
||||
|
||||
if (state.status == ChildStatus::CANCELED && !is_paused()) {
|
||||
algo().cancel("Unexpected child cancel");
|
||||
return;
|
||||
}
|
||||
|
||||
auto delta = state.total_fills.value() - me->child_state.total_fills.value();
|
||||
if (delta != 0) {
|
||||
me->fills += delta;
|
||||
algo().update_fill_state(Quantity {me->fills}, state.last_price);
|
||||
send_profit_order(state.last_price, Quantity {delta});
|
||||
}
|
||||
me->child_state = state;
|
||||
|
||||
if (me->params.scratch &&
|
||||
me->fills >= me->params.scratch.value().max_overfill) {
|
||||
do_scratch();
|
||||
}
|
||||
|
||||
if (me->fills >= algo().quantity().value() || state.status == ChildStatus::COMPLETED) {
|
||||
complete();
|
||||
}
|
||||
}
|
||||
|
||||
void DormanAlgo::on_book(SecurityKey key, const Book& book) {
|
||||
if (key.value() != me->algo->security().id()) { return; }
|
||||
auto price = me->algo->price();
|
||||
auto tick = me->algo->security().contract_spec().low_tick();
|
||||
|
||||
// Determine available size on the market
|
||||
auto& side = book.side(me->side);
|
||||
uint32_t available = 0;
|
||||
for (const auto& level : side) {
|
||||
if (securities::test_price_equality(level.price(), price.value(), tick)) {
|
||||
available += level.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the step_table
|
||||
auto target = Quantity {0};
|
||||
if (available > 0) {
|
||||
for (const auto& entry : me->params.steps.step_table) {
|
||||
target = Quantity { std::get<1>(entry) };
|
||||
if (std::get<0>(entry) > available) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
target = Quantity { std::min(target.value(), me->params.steps.maximum) };
|
||||
|
||||
// First on_book
|
||||
if (!child_active()) {
|
||||
update_child(target);
|
||||
}
|
||||
// See if we need to adjust
|
||||
else {
|
||||
if (target == me->exposed_qty) {
|
||||
me->next_size = std::nullopt;
|
||||
}
|
||||
// If target < size reduce immediately
|
||||
else if (target < me->exposed_qty) {
|
||||
update_child(target);
|
||||
me->next_size = std::nullopt;
|
||||
}
|
||||
// if target > size schedule increase
|
||||
else if (target > me->exposed_qty) {
|
||||
if (me->next_size) {
|
||||
// already planning to increase, update the target
|
||||
me->next_size.value().size = target;
|
||||
}
|
||||
else {
|
||||
// no existing scheduled increase, create one now
|
||||
me->next_size = { FutureQty { target, me->algo->get_time() + std::chrono::seconds(me->params.steps.trail_seconds) } };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void DormanAlgo::on_timer() {
|
||||
// Check for pending size changes
|
||||
auto now = algo().get_time();
|
||||
if (me->pause_until &&
|
||||
now >= me->pause_until.value()) {
|
||||
|
||||
resume();
|
||||
}
|
||||
if (me->next_size &&
|
||||
now >= me->next_size.value().when) {
|
||||
auto size = me->next_size.value().size;
|
||||
update_child(size);
|
||||
me->next_size = std::nullopt;
|
||||
}
|
||||
}
|
||||
void DormanAlgo::on_ticker(SecurityKey, const TradeVector& vec) {
|
||||
if (me->trade_accumulator) {
|
||||
size_t contracts = 0;
|
||||
for (const auto& t : vec) {
|
||||
contracts += t.quantity.value();
|
||||
}
|
||||
auto& acc = me->trade_accumulator.value();
|
||||
acc.push_back(algo().get_time(), contracts);
|
||||
if (acc.exceeded()) {
|
||||
auto delay = std::chrono::seconds(me->params.trade_accumulation.value().sleep_seconds);
|
||||
pause_for(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IAlgoOrder::OnCancelResponse DormanAlgo::on_cancel() {
|
||||
// detach any remaining profit orders so they keep running
|
||||
for(const auto& key : me->profit_orders) {
|
||||
algo().detach_child(key);
|
||||
}
|
||||
return OnCancelResponse::HONOR;
|
||||
}
|
||||
|
||||
IAlgoServices& DormanAlgo::algo() const { return *me->algo; }
|
||||
|
||||
//
|
||||
// private
|
||||
|
||||
void DormanAlgo::update_child(Quantity exposed) {
|
||||
auto& splitter = *me->splitter_definition.mutable_custom_def()->mutable_splitter();
|
||||
splitter.set_max_order_qty(me->params.clip_limit);
|
||||
me->exposed_qty = exposed;
|
||||
splitter.set_exposed_qty(is_paused() ? 0 : exposed.value());
|
||||
|
||||
if (!child_active()) {
|
||||
// launch child
|
||||
me->child_id = algo().launch_child(me->splitter_definition, algo().price(), algo().quantity());
|
||||
}
|
||||
else {
|
||||
Update update;
|
||||
update.set_price(algo().price().value());
|
||||
update.set_quantity(algo().quantity().value());
|
||||
*update.mutable_custom()->mutable_splitter() = splitter;
|
||||
algo().update_child(me->child_id, update);
|
||||
}
|
||||
}
|
||||
|
||||
void DormanAlgo::update_child_template(IBeforeLaunchAlgoServices& ibl, const CustomOrderDefinition& child) {
|
||||
auto child_def = ibl.definition();
|
||||
// Insert a spliter between this and the configured child definition.
|
||||
api::orderman::SplitterOrderDefinition& splitter_def = *child_def.mutable_custom_def()->mutable_splitter();
|
||||
*splitter_def.mutable_child() = child;
|
||||
me->splitter_definition = child_def;
|
||||
}
|
||||
|
||||
Quantity DormanAlgo::exposed_qty() const { return me->exposed_qty; }
|
||||
|
||||
bool DormanAlgo::child_active() const { return me->child_id.value() != 0; }
|
||||
|
||||
void DormanAlgo::do_scratch() {
|
||||
// When the # of fills exceeds the max overfill quantity
|
||||
// We enter fills opposite fills at the fill price and terminate
|
||||
// Use a splitter to bypass clip limit if necessary
|
||||
|
||||
OrderDefinition def = me->splitter_definition;
|
||||
def.set_side(securities::other_side(me->side));
|
||||
def.mutable_custom_def()->mutable_splitter()->set_exposed_qty(me->fills);
|
||||
|
||||
auto price = me->child_state.last_price;
|
||||
auto scratch_id = algo().launch_child(def, price, Quantity { me->fills }, false);
|
||||
|
||||
algo().detach_child(scratch_id);
|
||||
complete();
|
||||
}
|
||||
|
||||
void DormanAlgo::send_profit_order(Price p, Quantity q) {
|
||||
if (me->params.profit_orders) {
|
||||
auto side = securities::other_side(me->side);
|
||||
Price offset =
|
||||
Price { algo().security().contract_spec().low_tick() * me->params.profit_orders.value().ticks_away };
|
||||
auto price =
|
||||
securities::worsen_price(side, p, offset);
|
||||
|
||||
OrderDefinition def = me->splitter_definition;
|
||||
// BTS Simulator does not currently support GTC
|
||||
// Note: This could be made a configurable order type in the algo parameters
|
||||
def.set_time_in_force(TimeInForce::TIME_IN_FORCE_DAY);
|
||||
def.set_side(side);
|
||||
def.mutable_custom_def()->mutable_limit();
|
||||
me->profit_orders.push_back(
|
||||
algo().launch_child(def, price, q, false));
|
||||
}
|
||||
}
|
||||
|
||||
void DormanAlgo::complete() {
|
||||
// Allow profit orders to keep running
|
||||
if (me->params.profit_orders) {
|
||||
for(const auto& key : me->profit_orders) {
|
||||
algo().detach_child(key);
|
||||
}
|
||||
}
|
||||
algo().complete();
|
||||
}
|
||||
|
||||
|
||||
void DormanAlgo::pause_for(Duration d) {
|
||||
if (me->params.trade_accumulation) {
|
||||
if (is_paused()) {
|
||||
me->pause_until = std::max(me->pause_until.value(), { algo().get_time() + d });
|
||||
}
|
||||
else {
|
||||
me->pause_until = { algo().get_time() + d };
|
||||
// When paused child update will always expose 0
|
||||
update_child(me->exposed_qty);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool DormanAlgo::is_paused() const {
|
||||
return (bool)(me->pause_until);
|
||||
}
|
||||
void DormanAlgo::resume() {
|
||||
if (is_paused()) {
|
||||
me->pause_until = std::nullopt;
|
||||
update_child(me->exposed_qty);
|
||||
}
|
||||
}
|
||||
|
||||
}}} //namespace
|
||||
@@ -1,96 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include <map>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
namespace dorman {
|
||||
|
||||
//
|
||||
// Algo parameters
|
||||
//
|
||||
|
||||
// This defines the market quantity vs the order quantity
|
||||
struct StepParams {
|
||||
std::vector<std::tuple<uint32_t, uint32_t>> step_table;
|
||||
// This is the cap on the maximum quantity,
|
||||
// even though the step table can allow for higher quantities (depending on the
|
||||
// clip size this could be divided across multiple exchange orders)
|
||||
uint32_t maximum;
|
||||
// If the market increases don't follow until it has stayed high for trail_seconds;
|
||||
uint32_t trail_seconds;
|
||||
};
|
||||
|
||||
// If the number of accumulated fills (net of profit orders) touches or goes beyond
|
||||
// this, the fills will be scratched by entering opposite orders at the fill price
|
||||
struct ScratchParams {
|
||||
uint32_t max_overfill;
|
||||
};
|
||||
// If this is enabled, then as fills accumulate, profit GTC orders are
|
||||
// immediately sent N ticks away on the opposite side.
|
||||
struct ProfitOrderParams {
|
||||
uint32_t ticks_away;
|
||||
};
|
||||
// If N contracts trade in T seconds, then the algo is paused (ie child exchange order
|
||||
// is/are cancelled) for X seconds. Once the condition clears order is reentered.
|
||||
struct TradeAccumulationParams {
|
||||
uint32_t contracts;
|
||||
uint32_t seconds;
|
||||
uint32_t sleep_seconds;
|
||||
};
|
||||
struct AlgoParams {
|
||||
StepParams steps;
|
||||
uint32_t clip_limit;
|
||||
std::optional<ScratchParams> scratch;
|
||||
std::optional<ProfitOrderParams> profit_orders;
|
||||
std::optional<TradeAccumulationParams> trade_accumulation;
|
||||
};
|
||||
|
||||
|
||||
class DormanAlgo : public IAlgoOrder {
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> me;
|
||||
IAlgoServices& algo() const;
|
||||
|
||||
public:
|
||||
DormanAlgo();
|
||||
~DormanAlgo();
|
||||
|
||||
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
|
||||
void do_launch(IAlgoServices &) override;
|
||||
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
|
||||
void do_update(api::orderman::Update const &) override;
|
||||
void on_child_change(OrderKey, ChildState const &) override;
|
||||
void on_book(SecurityKey, const Book&) override;
|
||||
void on_timer() override;
|
||||
void on_ticker(SecurityKey, const TradeVector&) override;
|
||||
IAlgoOrder::OnCancelResponse on_cancel() override;
|
||||
|
||||
Quantity exposed_qty() const;
|
||||
|
||||
private:
|
||||
//
|
||||
// Child management
|
||||
void update_child_template(IBeforeLaunchAlgoServices&, const api::orderman::CustomOrderDefinition&);
|
||||
void update_child(Quantity exposed);
|
||||
bool child_active() const;
|
||||
|
||||
void do_scratch();
|
||||
void send_profit_order(Price, Quantity);
|
||||
void complete();
|
||||
|
||||
void pause_for(Duration);
|
||||
bool is_paused() const;
|
||||
void resume();
|
||||
|
||||
};
|
||||
|
||||
template <class OK,class ERR>
|
||||
using Result = std::variant<OK,ERR>;
|
||||
Result<AlgoParams,OrderOpResult> to_dorman_params(const api::orderman::DynamicOrderDefinition&);
|
||||
|
||||
}}} //namespace
|
||||
@@ -145,7 +145,7 @@ void IcebergAlgo::on_child_change(OrderKey k, ChildState const &st) {
|
||||
on_timer();
|
||||
return;
|
||||
case ChildStatus::CANCELED:
|
||||
algo().cancel("Unexpected child cancel");
|
||||
algo().terminate_error("Unexpected child cancel");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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