Compare commits
64
Commits
ls
...
excel_algos
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0abb668a0 | ||
|
|
6a0f45ed72 | ||
|
|
9baa81b14f | ||
|
|
a43e649c06 | ||
|
|
6bf39b43f8 | ||
|
|
851a34449a | ||
|
|
5609b8d80d | ||
|
|
dffb9018da | ||
|
|
aec1d1ee6e | ||
|
|
fa4b4fc934 | ||
|
|
49c584047a | ||
|
|
773ea38ffa | ||
|
|
81b7770151 | ||
|
|
e480b8951f | ||
|
|
781cf3ea6d | ||
|
|
93f86ecb94 | ||
|
|
f360b79d27 | ||
|
|
7bf3c51380 | ||
|
|
1192182bc8 | ||
|
|
419cac3e3d | ||
|
|
2e8dbd8478 | ||
|
|
938db6b616 | ||
|
|
d6bc9298cc | ||
|
|
d7ea71de84 | ||
|
|
2881e5ea32 | ||
|
|
4fd8530bc7 | ||
|
|
ffd5915c52 | ||
|
|
70f1552ec6 | ||
|
|
f3114d1664 | ||
|
|
88494eb0fc | ||
|
|
d5427f6c24 | ||
|
|
106788c8c8 | ||
|
|
6968312a04 | ||
|
|
4c9dc9d83b | ||
|
|
ff193b13f0 | ||
|
|
fc9582850b | ||
|
|
f1ae9030d7 | ||
|
|
fc5439e92e | ||
|
|
908b50a92a | ||
|
|
eb7aa17f2e | ||
|
|
f92722c849 | ||
|
|
ebedd17129 | ||
|
|
0f8ed5dacb | ||
|
|
14f4452599 | ||
|
|
22002c43cf | ||
|
|
7551e23877 | ||
|
|
22c92c3e17 | ||
|
|
613823e7c5 | ||
|
|
f0a9a2415d | ||
|
|
708e63e36e | ||
|
|
53990297e8 | ||
|
|
9570be8bfa | ||
|
|
0b15570d00 | ||
|
|
5fbc1397b5 | ||
|
|
38bd2ef321 | ||
|
|
59c00203a9 | ||
|
|
706e5bc73b | ||
|
|
6cf395199e | ||
|
|
1707c3268f | ||
|
|
9b73ad2486 | ||
|
|
205eebb7da | ||
|
|
ef4dc1fc65 | ||
|
|
474401cddc | ||
|
|
3ebcf9a270 |
No files matched your search
Vendored
+2
-1
@@ -44,6 +44,7 @@
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/build/ninja_debug/sdk/examples" : "${workspaceRoot}/src/sdk/examples",
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
@@ -104,7 +105,7 @@
|
||||
"request": "launch",
|
||||
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
|
||||
"args": [
|
||||
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
|
||||
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64.xll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
|
||||
@@ -59,6 +59,7 @@ 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)
|
||||
|
||||
|
||||
@@ -106,11 +106,19 @@ 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 = v,
|
||||
v => pvm.value = set_value_not_null_string(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("spark_core")]
|
||||
[assembly: InternalsVisibleTo("sparkcmd")]
|
||||
[assembly: InternalsVisibleTo("spark_test")]
|
||||
[assembly: InternalsVisibleTo("spark_excel")]
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace bts.md
|
||||
{
|
||||
if (!timeout.HasValue)
|
||||
{
|
||||
timeout = TimeSpan.FromMilliseconds(10000);
|
||||
timeout = TimeSpan.FromHours(24);
|
||||
}
|
||||
CancellationTokenSource src = new CancellationTokenSource(timeout.Value);
|
||||
SecurityImmutable? security = await Task.Run<SecurityImmutable?>(async () =>
|
||||
@@ -234,6 +234,8 @@ 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.
@@ -0,0 +1,300 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using bts.excel.extensions;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a reference to some Cell instance, whether it exists or not.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Static methods are provided for conversion of column names etc.
|
||||
/// </remarks>
|
||||
public sealed class CellRef : IComparable, IComparable<CellRef>
|
||||
{
|
||||
public CellRef(ExcelDna.Integration.ExcelReference rref) : this(rref.GetSheetName(),
|
||||
(rref.ColumnFirst + 1, rref.RowFirst + 1) )
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for the class: CellRef
|
||||
/// </summary>
|
||||
/// <param name="sheetName">Name of the sheet where the cell belongs.</param>
|
||||
/// <param name="coordinate">Coordinate (column, row) for the cell in the sheet.</param>
|
||||
public CellRef(string sheetName, (int col1, int row1) coordinate)
|
||||
{
|
||||
SheetName = sheetName;
|
||||
SimpleSheetName = simple_sheet_name(SheetName);
|
||||
Coordinate = coordinate;
|
||||
LocalAddress = ConvertCoordToAddress(coordinate);
|
||||
}
|
||||
|
||||
static string simple_sheet_name(string sn)
|
||||
{
|
||||
int open = sn.IndexOf('[');
|
||||
int close = sn.IndexOf(']', open + 1);
|
||||
var ssn = sn.Substring(0, open) + sn.Substring(close + 1);
|
||||
ssn = 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;}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of rows, to e compatible with Excel.
|
||||
/// </summary>
|
||||
private const int MaxRows = 1048576;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of columns, to be compatible with Excel.
|
||||
/// </summary>
|
||||
private const int MaxColumns = 16384;
|
||||
|
||||
// NOTE: Worksheet and workbook specifications and limits
|
||||
// http://office.microsoft.com/en-gb/excel-help/excel-specifications-and-limits-HP010342495.aspx
|
||||
//
|
||||
// Worksheet size: 1,048,576 rows by 16,384 columns.
|
||||
// * 1048575 is the highest number representable in 20 bits.
|
||||
// * 16383 is the highest number representable in 14 bits. That leaves 2 spare bits in a 16-bit word.
|
||||
// Column width: 255 characters (I'm not sure what the real significance of this is).
|
||||
// Total number of characters that a cell can contain: 32,767 characters
|
||||
// * 32766 is the highest number representable in 15 bits. That leaves 1 spare bit in a 16-bit word.
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Name of the sheet where the cell belongs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All cells belong to some Sheet instance, so this name must never be null or empty.
|
||||
/// </remarks>
|
||||
public string SheetName { get; }
|
||||
public string SimpleSheetName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Coordinate (column, row) for the cell in the sheet.
|
||||
/// </summary>
|
||||
public (int col1, int row1) Coordinate { get; }
|
||||
|
||||
public CellRef Offset(int columnOffset, int rowOffset)
|
||||
{
|
||||
return new CellRef(SheetName, (Column + columnOffset, Row + rowOffset));
|
||||
}
|
||||
|
||||
public int Column => Coordinate.col1;
|
||||
|
||||
public int Row => Coordinate.row1;
|
||||
|
||||
public string LocalAddress { get; internal set; }
|
||||
|
||||
#region Address conversion
|
||||
|
||||
/// <summary>
|
||||
/// Converts a column identifier, using letters, to a column number.
|
||||
/// </summary>
|
||||
/// <param name="columnName">Column identifier using letters.</param>
|
||||
/// <returns>Column number.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">When column number exceeds maximum.</exception>
|
||||
public static int ConvertToColumnNumber(string columnName)
|
||||
{
|
||||
var number = 0;
|
||||
var pow = 1;
|
||||
for (var i = columnName.Length - 1; i >= 0; i--)
|
||||
{
|
||||
number += (columnName[i] - 'A' + 1) * pow;
|
||||
pow *= 26;
|
||||
}
|
||||
|
||||
if (number > MaxColumns)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(columnName),
|
||||
"Column number from columnName exceeds maximum");
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a column number to a column identifier using letters.
|
||||
/// </summary>
|
||||
/// <param name="columnNumber">Column number.</param>
|
||||
/// <returns>Column identifier using letters.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">When column number is invalid.</exception>
|
||||
public static string ConvertToColumnName(int columnNumber)
|
||||
{
|
||||
if (columnNumber < 1 || columnNumber > MaxColumns)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(columnNumber),
|
||||
"Invalid column number");
|
||||
}
|
||||
|
||||
var dividend = columnNumber;
|
||||
var columnName = string.Empty;
|
||||
while (dividend > 0)
|
||||
{
|
||||
var modulo = (dividend - 1) % 26;
|
||||
columnName = Convert.ToChar(65 + modulo) + columnName;
|
||||
dividend = (dividend - modulo) / 26;
|
||||
}
|
||||
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an address, like "A1", to a cell coordinate.
|
||||
/// </summary>
|
||||
/// <param name="address">Cell address, like "A1".</param>
|
||||
/// <returns>Coordinate of cell on sheet.</returns>
|
||||
/// <exception cref="ArgumentNullException">When address argument is null.</exception>
|
||||
/// <exception cref="ArgumentException">When address is invalid.</exception>
|
||||
public static (int col1, int row1) ConvertAddressToCoord(string address)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
}
|
||||
|
||||
address = address.Replace("$", string.Empty);
|
||||
address = address.ToUpper();
|
||||
|
||||
var letters = new StringBuilder();
|
||||
foreach (var c in address)
|
||||
{
|
||||
if (c < 'A' || c > 'Z')
|
||||
{
|
||||
break;
|
||||
}
|
||||
letters.Append(c);
|
||||
}
|
||||
|
||||
if (letters.Length == 0 || letters.Length > 3)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Column name requires 1 to 3 letters",
|
||||
nameof(address));
|
||||
}
|
||||
|
||||
var numbers = address.Substring(letters.Length);
|
||||
if (numbers.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Row number is required", nameof(address));
|
||||
}
|
||||
|
||||
Debug.Assert(letters.Length + numbers.Length == address.Length);
|
||||
|
||||
var row1 = int.Parse(numbers);
|
||||
var col1 = ConvertToColumnNumber(letters.ToString());
|
||||
|
||||
return (col1, row1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert cell coordinate to cell address, like "A1".
|
||||
/// </summary>
|
||||
/// <param name="coordinate">Coordinate of cell on sheet.</param>
|
||||
/// <param name="absolute">Provide absolute address, like "$A$1".</param>
|
||||
/// <returns>Cell address, like "A1", or optionally like "$A$1".</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static string ConvertCoordToAddress((int col1, int row1) coordinate, bool absolute = false)
|
||||
{
|
||||
if (coordinate.row1 < 1 || coordinate.row1 > MaxRows ||
|
||||
coordinate.col1 < 1 || coordinate.col1 > MaxColumns)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(coordinate),
|
||||
"Invalid coordinate");
|
||||
}
|
||||
|
||||
var (col1, row1) = coordinate;
|
||||
return absolute
|
||||
? $"${ConvertToColumnName(col1)}${row1}"
|
||||
: $"{ConvertToColumnName(col1)}{row1}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IComparable
|
||||
|
||||
public int CompareTo(CellRef other)
|
||||
{
|
||||
if (other == null) return -1;
|
||||
return string.Compare(ToString(), other.ToString(),
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj.GetType() != GetType()) return -1;
|
||||
return CompareTo((CellRef)obj);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HashCode & Equals
|
||||
|
||||
/// <returns>Unique key</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return to_name();
|
||||
}
|
||||
|
||||
public string to_address()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SheetName))
|
||||
return $"'{SheetName}'!{LocalAddress}";
|
||||
else
|
||||
return $"{LocalAddress}";
|
||||
}
|
||||
|
||||
public string to_name()
|
||||
{
|
||||
if(!string.IsNullOrEmpty(named_range))
|
||||
return named_range;
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SheetName))
|
||||
{
|
||||
return $"{SimpleSheetName}_{LocalAddress}".ToLowerInvariant();
|
||||
}
|
||||
else
|
||||
return $"{LocalAddress}".ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
public string r1_c1_include_sheet()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SheetName))
|
||||
return $"'{SimpleSheetName}'!R{this.Row}C{this.Column}";
|
||||
else
|
||||
return $"R{this.Row}C{this.Column}";
|
||||
}
|
||||
|
||||
public string r1_c1()
|
||||
{
|
||||
return $"R{this.Row}C{this.Column}";
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ToString().GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
|
||||
if (obj != null && !(obj is CellRef))
|
||||
throw new ArgumentException("Object must be of type CellRef.");
|
||||
|
||||
return GetHashCode().Equals(obj.GetHashCode());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public class ErrorValue : IComparable, IComparable<ErrorValue>
|
||||
{
|
||||
private enum ErrorType
|
||||
{
|
||||
Null,
|
||||
Div0,
|
||||
Value,
|
||||
Ref,
|
||||
Name,
|
||||
Num,
|
||||
NA
|
||||
}
|
||||
|
||||
public static ErrorValue Null => new ErrorValue(ErrorType.Null);
|
||||
public static ErrorValue Div0 => new ErrorValue(ErrorType.Div0);
|
||||
public static ErrorValue Value => new ErrorValue(ErrorType.Value);
|
||||
public static ErrorValue Ref => new ErrorValue(ErrorType.Ref);
|
||||
public static ErrorValue Name => new ErrorValue(ErrorType.Name);
|
||||
public static ErrorValue Num => new ErrorValue(ErrorType.Num);
|
||||
public static ErrorValue NA => new ErrorValue(ErrorType.NA);
|
||||
|
||||
private const string HashNull = "#NULL!";
|
||||
private const string HashDiv0 = "#DIV/0!";
|
||||
private const string HashValue = "#VALUE!";
|
||||
private const string HashRef = "#REF!";
|
||||
private const string HashName = "#NAME?";
|
||||
private const string HashNum = "#NUM!";
|
||||
private const string HashNA = "#N/A";
|
||||
|
||||
private readonly ErrorType _errorType;
|
||||
|
||||
public ErrorValue(string token)
|
||||
: this(GetErrorType(token))
|
||||
{ }
|
||||
|
||||
private ErrorValue(ErrorType errorType)
|
||||
{
|
||||
_errorType = errorType;
|
||||
}
|
||||
|
||||
private static ErrorType GetErrorType(string token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case HashNull: return ErrorType.Null;
|
||||
case HashDiv0: return ErrorType.Div0;
|
||||
case HashValue: return ErrorType.Value;
|
||||
case HashRef: return ErrorType.Ref;
|
||||
case HashName: return ErrorType.Name;
|
||||
case HashNum: return ErrorType.Num;
|
||||
case HashNA: return ErrorType.NA;
|
||||
default: throw new Exception("Unrecongised error token");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsErrorToken(string token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case HashNull:
|
||||
case HashDiv0:
|
||||
case HashValue:
|
||||
case HashRef:
|
||||
case HashName:
|
||||
case HashNum:
|
||||
case HashNA:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (_errorType)
|
||||
{
|
||||
case ErrorType.Null: return HashNull;
|
||||
case ErrorType.Div0: return HashDiv0;
|
||||
case ErrorType.Value: return HashValue;
|
||||
case ErrorType.Ref: return HashRef;
|
||||
case ErrorType.Name: return HashName;
|
||||
case ErrorType.Num: return HashNum;
|
||||
case ErrorType.NA: return HashNA;
|
||||
default: throw new Exception("Unexpected condition");
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(ErrorValue other)
|
||||
{
|
||||
if (other == null) return -1;
|
||||
return _errorType.CompareTo(other._errorType);
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj.GetType() != GetType()) return -1;
|
||||
return CompareTo(obj as ErrorValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
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,26 @@
|
||||
|
||||
using spark_excel.grammar;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public interface IExpression
|
||||
{
|
||||
object evaluate();
|
||||
}
|
||||
internal class Expression<T> : IExpression
|
||||
{
|
||||
private readonly ExcelFormulaParser.ExprContext _context;
|
||||
private readonly IExcelFormulaVisitor<T> _visitor;
|
||||
|
||||
public Expression(ExcelFormulaParser.ExprContext context, IExcelFormulaVisitor<T> visitor)
|
||||
{
|
||||
_context = context;
|
||||
_visitor = visitor;
|
||||
}
|
||||
|
||||
public object evaluate()
|
||||
{
|
||||
return _context.Accept(_visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
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 class GeneratorVisitor : ExcelFormulaBaseVisitor<object>
|
||||
{
|
||||
ExcelReference _orig_ref, _current_ref;
|
||||
functions.lib.FunctionLibrary _library;
|
||||
ExcelFormulaErrorListener _error_listener = new ExcelFormulaErrorListener();
|
||||
IDefinitionVisitor _defs;
|
||||
IParameters _params;
|
||||
string _document_name;
|
||||
public GeneratorVisitor(ExcelReference rref, IDefinitionVisitor defs, IParameters parameters, bool include_setups = true)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
|
||||
public override object VisitName(ExcelFormulaParser.NameContext context)
|
||||
{
|
||||
var name = context.GetText();
|
||||
|
||||
var lookup = name;
|
||||
if(!lookup.Contains("!"))
|
||||
lookup = "!" + lookup;
|
||||
object value = XlCall.Excel(XlCall.xlfGetName, lookup);
|
||||
if(value != null && !(value is ExcelError))
|
||||
{
|
||||
string[] par = value.ToString().Split(new[] {'=', 'R', 'C'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
int r = int.Parse(par[0]) - 1;
|
||||
int c = int.Parse(par[1]) - 1;
|
||||
var rref = new ExcelReference(r, r, c, c, _orig_ref.GetSheetName());
|
||||
process_cell(rref, out _);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public override object VisitMain(ExcelFormulaParser.MainContext context)
|
||||
{
|
||||
return context.expr().Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitMulDiv(ExcelFormulaParser.MulDivContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr[0].Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = expr[1].Accept(this);
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
string code = $"{arg1} {context.op.Text} {arg2}";
|
||||
return code;
|
||||
}
|
||||
|
||||
public override object VisitAddSub(ExcelFormulaParser.AddSubContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr[0].Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = expr[1].Accept(this);
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
string code = $"{arg1} {context.op.Text} {arg2}";
|
||||
return code;
|
||||
}
|
||||
|
||||
public override object VisitPercentage(ExcelFormulaParser.PercentageContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr.Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
return $"( {arg1} / 100.0 )";
|
||||
}
|
||||
|
||||
public override object VisitRelational(ExcelFormulaParser.RelationalContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr[0].Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = expr[1].Accept(this);
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
return $"{arg1} {context.op.Text} {arg2}";
|
||||
}
|
||||
|
||||
|
||||
public override object VisitError(ExcelFormulaParser.ErrorContext context)
|
||||
{
|
||||
return new ErrorValue(context.GetText());
|
||||
}
|
||||
|
||||
public override object VisitString(ExcelFormulaParser.StringContext context)
|
||||
{
|
||||
var str = context.GetText();
|
||||
return $"std::string({str})";
|
||||
}
|
||||
|
||||
public override object VisitUnary(ExcelFormulaParser.UnaryContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
if(context.sign.Text == "-")
|
||||
return "-" + expr.Accept(this);
|
||||
else
|
||||
return expr.Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitFunction(ExcelFormulaParser.FunctionContext context)
|
||||
{
|
||||
var name = context.name.Text;
|
||||
|
||||
ExcelReference current = _orig_ref;
|
||||
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);
|
||||
}
|
||||
|
||||
public override object VisitParenthesis(ExcelFormulaParser.ParenthesisContext context)
|
||||
{
|
||||
return context.expr().Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitNumber(ExcelFormulaParser.NumberContext context)
|
||||
{
|
||||
var num = context.GetText();
|
||||
if(double.TryParse(num, out double res))
|
||||
return res.ToString("0.0############");
|
||||
return num;
|
||||
}
|
||||
|
||||
public override object VisitConcatenate(ExcelFormulaParser.ConcatenateContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = "std::to_string(" + expr[0].Accept(this) + ")";
|
||||
var arg2 = "std::to_string(" + expr[1].Accept(this) + ")";
|
||||
return $"{arg1}+{arg2}";
|
||||
}
|
||||
|
||||
public override object VisitRange(ExcelFormulaParser.RangeContext context)
|
||||
{
|
||||
var rng = context.rng();
|
||||
return rng.Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitRangeList(ExcelFormulaParser.RangeListContext context)
|
||||
{
|
||||
var result = new List<RangeRef>();
|
||||
|
||||
foreach (var range in context.rng())
|
||||
{
|
||||
var obj = range.Accept(this);
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case ErrorValue err:
|
||||
result.Add(null);
|
||||
break;
|
||||
case RangeRef rangeRef:
|
||||
result.Add(rangeRef);
|
||||
break;
|
||||
case null:
|
||||
result.Add(null);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object VisitRangeRef(ExcelFormulaParser.RangeRefContext context)
|
||||
{
|
||||
var obj = context.@ref().Accept(this);
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case ErrorValue _:
|
||||
return obj;
|
||||
case CellRef start:
|
||||
var (column, row) = CellRef.ConvertAddressToCoord(context.end.Text);
|
||||
var rng = new RangeRef(start, column, row);
|
||||
|
||||
if(rng.IsOneCell())
|
||||
{
|
||||
var rref = new ExcelReference(row - 1, row - 1, column - 1, column - 1, rng.SheetName);
|
||||
process_cell(rref, out _);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rng;
|
||||
case null:
|
||||
return null;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
public override object VisitCell(ExcelFormulaParser.CellContext context)
|
||||
{
|
||||
return context.@ref().Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitCellList(ExcelFormulaParser.CellListContext context)
|
||||
{
|
||||
var result = new List<CellRef>();
|
||||
foreach (var @ref in context.@ref())
|
||||
{
|
||||
var obj = @ref.Accept(this);
|
||||
switch (obj)
|
||||
{
|
||||
case ErrorValue _:
|
||||
// TODO: Warn that error has been replaced with null here?
|
||||
result.Add(null);
|
||||
break;
|
||||
case CellRef cellRef:
|
||||
result.Add(cellRef);
|
||||
break;
|
||||
case null:
|
||||
result.Add(null);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object VisitCellRef(ExcelFormulaParser.CellRefContext context)
|
||||
{
|
||||
var colRow = context.colRow.Text;
|
||||
var (col1, row1) = CellRef.ConvertAddressToCoord(colRow);
|
||||
var sheetname = context.sheet?.Text ?? _orig_ref.GetSheetName();
|
||||
|
||||
var rref = new ExcelReference(row1 - 1, row1 - 1, col1 - 1, col1 - 1, sheetname);
|
||||
return process_cell(rref, out _);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public class RangeRef
|
||||
{
|
||||
public RangeRef(CellRef start, CellRef end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public string named_range { get; set;}
|
||||
|
||||
public RangeRef(CellRef start, int endColumn, int endRow)
|
||||
: this(start, new CellRef(start.SheetName, (endColumn, endRow)))
|
||||
{ }
|
||||
|
||||
public RangeRef Offset(int columnOffset, int rowOffset)
|
||||
{
|
||||
var start = Start.Offset(columnOffset, rowOffset);
|
||||
var end = End.Offset(columnOffset, rowOffset);
|
||||
return new RangeRef(start, end);
|
||||
}
|
||||
|
||||
public (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; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return to_name();
|
||||
}
|
||||
|
||||
public string SheetName => Start.SheetName;
|
||||
public string SimpleSheetName => Start.SimpleSheetName;
|
||||
|
||||
public string to_address()
|
||||
{
|
||||
return string.Format("'{0}'!{1}:{2}",
|
||||
SheetName,
|
||||
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
|
||||
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
|
||||
}
|
||||
|
||||
public string to_name()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(named_range))
|
||||
return named_range;
|
||||
else
|
||||
{
|
||||
return string.Format("{0}_{1}_{2}",
|
||||
Start.SimpleSheetName,
|
||||
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
|
||||
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
|
||||
}
|
||||
}
|
||||
|
||||
public string r1_c1()
|
||||
{
|
||||
return $"{Start.r1_c1()}:{End.r1_c1()}";
|
||||
}
|
||||
|
||||
public bool IsVector()
|
||||
{
|
||||
return IsSingleRow() || IsSingleColumn();
|
||||
}
|
||||
|
||||
public bool IsOneCell()
|
||||
{
|
||||
return IsSingleRow() && IsSingleColumn();
|
||||
}
|
||||
|
||||
internal List<CellRef> get_all_cells()
|
||||
{
|
||||
var result = new List<CellRef>();
|
||||
var sheetName = Start.SheetName;
|
||||
var numRows = NumRows();
|
||||
var lastRow = Start.Row + numRows - 1;
|
||||
|
||||
for (var row = Start.Row; row <= lastRow; row++)
|
||||
{
|
||||
var numCols = NumCols();
|
||||
var lastCol = Start.Column + numCols - 1;
|
||||
for (var col = Start.Column; col <= lastCol; col++)
|
||||
{
|
||||
result.Add(new CellRef(sheetName, (col, row)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal 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()
|
||||
{
|
||||
Debug.Assert(IsVector());
|
||||
|
||||
var result = new List<CellRef>();
|
||||
|
||||
if (IsSingleColumn())
|
||||
{
|
||||
var col = Start.Column;
|
||||
var sheetName = Start.SheetName;
|
||||
var numRows = NumRows();
|
||||
var lastRow = Start.Row + numRows - 1;
|
||||
for (var row = Start.Row; row <= lastRow; row++)
|
||||
{
|
||||
result.Add(new CellRef(sheetName, (col, row)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(IsSingleRow());
|
||||
|
||||
var row = Start.Row;
|
||||
var sheetName = Start.SheetName;
|
||||
var numCols = NumCols();
|
||||
var lastCol = Start.Column + numCols - 1;
|
||||
for (var col = Start.Column; col <= lastCol; col++)
|
||||
{
|
||||
result.Add(new CellRef(sheetName, (col, row)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsSingleColumn()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return Start.Column == End.Column;
|
||||
}
|
||||
|
||||
public bool IsSingleRow()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return Start.Row == End.Row;
|
||||
}
|
||||
|
||||
public int NumRows()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return End.Row - Start.Row + 1;
|
||||
}
|
||||
|
||||
public int NumCols()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return End.Column - Start.Column + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
public FunctionLibrary(bool include_setups)
|
||||
{
|
||||
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)
|
||||
{
|
||||
foreach(var att in xl.CustomAttributes)
|
||||
{
|
||||
if(att is SparkAlgoCodeAttribute ac)
|
||||
_dnafunctions[xl.FunctionAttribute.Name] = ac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_defined(string name) => _functions.ContainsKey(name) || _dnafunctions.ContainsKey(name);
|
||||
|
||||
public void add_function(string name, IFunction function)
|
||||
{
|
||||
_functions.Add(name, function);
|
||||
}
|
||||
|
||||
public void add_functions(Dictionary<string, IFunction> delegates)
|
||||
{
|
||||
foreach (var f in delegates)
|
||||
_functions.Add(f.Key, f.Value);
|
||||
}
|
||||
|
||||
public object call_function(string name, IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
object bts_function_or_throw(string name, IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell)
|
||||
{
|
||||
SparkAlgoCodeAttribute att = null;
|
||||
if (_dnafunctions.TryGetValue(name, out att))
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
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))
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public static class Logical
|
||||
{
|
||||
public static Dictionary<string, IFunction> functions { get; }
|
||||
|
||||
static Logical()
|
||||
{
|
||||
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"AND", new And()},
|
||||
{"IF", new If()},
|
||||
{"IFERROR", new IfError()},
|
||||
{"OR", new Or()},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class Mathematics
|
||||
{
|
||||
public static Dictionary<string, IFunction> functions { get; }
|
||||
|
||||
static Mathematics()
|
||||
{
|
||||
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"LN", new Ln()},
|
||||
{"LOG", new Log()},
|
||||
{"POWER", new Power()},
|
||||
{"PI", new Pi()},
|
||||
{"SQRT", new Sqrt()},
|
||||
{"SUM", new Sum()},
|
||||
{"SIN", new Sin()},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class Stat
|
||||
{
|
||||
public static Dictionary<string, IFunction> functions { get; }
|
||||
|
||||
static Stat()
|
||||
{
|
||||
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"AVERAGE", new Average()},
|
||||
{"MAX", new Max()},
|
||||
{"MIN", new Min()},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class And : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: AND(logical1, [logical2], ...)
|
||||
// https://support.office.com/en-gb/article/AND-function-5f19b2e8-e1df-4408-897a-ce285a19e9d9
|
||||
|
||||
if (args.Length < 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"AND function requires at least 2 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string code = "(";
|
||||
foreach (var fx in args)
|
||||
{
|
||||
var arg = fx.evaluate();
|
||||
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
code += arg + " && ";
|
||||
}
|
||||
code += "true)";
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class If : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: IF(logical_test, value_if_true, [value_if_false])
|
||||
// https://support.office.com/en-gb/article/IF-function-69aed7c9-4e8a-4755-a9bc-aa8bbff73be2
|
||||
|
||||
if (args.Length != 2 && args.Length != 3)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"IF function takes 2 or 3 arguments",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
string code = string.Empty;
|
||||
if (args.Length == 3)
|
||||
{
|
||||
code += $"( {arg} ? ({ args[1].evaluate() }) : ({args[2].evaluate()}) ) ";
|
||||
}
|
||||
else
|
||||
{
|
||||
code += $"( {arg} ? ({ args[1].evaluate() }) : false) ";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class IfError : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: IFERROR(value, value_if_error)
|
||||
// https://support.office.com/en-gb/article/IFERROR-function-c526fd07-caeb-47b8-8bb6-63f3e417f611
|
||||
|
||||
if (args.Length != 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"IFERROR function takes exactly 2 arguments",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
return arg;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return args[1].evaluate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Ln : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: LN(number)
|
||||
// https://support.office.com/en-gb/article/LN-function-81fe1ed7-dac9-4acd-ba1d-07a142c6118f
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"LN function takes only 1 argument, got {args.Length}");
|
||||
}
|
||||
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
string code = $"log({arg})";
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Log : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: LOG(number, [base])
|
||||
// https://support.office.com/en-gb/article/LOG-function-4e82f196-1ca9-4747-8fb0-6c4a3abb3280
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
case 1:
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
string code = $"log10({arg})";
|
||||
return code;
|
||||
|
||||
case 2:
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = args[1].evaluate();
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
string code2 = $"log({arg1}, {arg2})";
|
||||
return code2;
|
||||
|
||||
default:
|
||||
throw new ArgumentException(
|
||||
$"LOG function takes only 1 or 2 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Max : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: MAX(number1, [number2], ...)
|
||||
// https://support.office.com/en-GB/article/MAX-function-e0012414-9ac8-4b34-9a47-73e662c08098
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"MAX function takes at least 1 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string code = $"std::max(\n";
|
||||
foreach (var arg in args)
|
||||
{
|
||||
var argv = arg.evaluate();
|
||||
if (argv is ErrorValue)
|
||||
return argv;
|
||||
|
||||
code += $"{argv},";
|
||||
}
|
||||
code += $"{ args[args.Length-1].evaluate()} );\n";
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Min : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: MIN(number1, [number2], ...)
|
||||
// https://support.office.com/en-GB/article/MIN-function-61635d12-920f-4ce2-a70f-96f202dcc152
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"MIN function takes at least 1 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string code = $"std::min(\n";
|
||||
foreach (var arg in args)
|
||||
{
|
||||
var argv = arg.evaluate();
|
||||
if (argv is ErrorValue)
|
||||
return argv;
|
||||
|
||||
code += $"{argv},";
|
||||
}
|
||||
code += $"{ args[args.Length - 1].evaluate()} );\n";
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Or : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: OR(logical1, [logical2], ...)
|
||||
// https://support.office.com/en-gb/article/OR-function-7d17ad14-8700-4281-b308-00b131e22af0
|
||||
|
||||
string code = "(";
|
||||
foreach (var fx in args)
|
||||
{
|
||||
var arg = fx.evaluate();
|
||||
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
code += arg + " || ";
|
||||
}
|
||||
code += "false)";
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Pi : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: PI()
|
||||
// https://support.office.com/en-gb/article/PI-function-264199d0-a3ba-46b8-975a-c4a04608989b
|
||||
|
||||
if (args.Length != 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"PI function takes no arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
return Math.PI.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Power : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: POWER(number, power)
|
||||
// https://support.office.com/en-gb/article/POWER-function-d3f2908b-56f4-4c3f-895a-07fb519c362a
|
||||
|
||||
if (args.Length != 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"POWER function takes 2 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = args[1].evaluate();
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
return $"pow({arg1}, {arg2})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Sin : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: SIN(number)
|
||||
// https://support.office.com/en-gb/article/SIN-function-654975c2-05c4-4831-9a24-2c65e4040fdf
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Sin function takes only 1 argument, got {args.Length}");
|
||||
}
|
||||
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
return $"sin({arg1})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Sqrt : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
|
||||
{
|
||||
// Syntax: SQRT(number)
|
||||
// https://support.office.com/en-gb/article/SQRT-function-654975c2-05c4-4831-9a24-2c65e4040fdf
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"SQRT function takes only 1 argument, got {args.Length}");
|
||||
}
|
||||
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
return $"sqrt({arg1})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using ExcelDna.Integration;
|
||||
|
||||
namespace bts.excel.extensions
|
||||
{
|
||||
public static class ExcelDNAExtensions
|
||||
{
|
||||
public static string GetSheetName(this ExcelReference rref)
|
||||
{
|
||||
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, rref);
|
||||
return sheetname;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -11,14 +12,26 @@ using bts.excel;
|
||||
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
|
||||
{
|
||||
public Create create;
|
||||
public string order_type;
|
||||
public core.Result<Response>? response;
|
||||
}
|
||||
static ConcurrentDictionary<long, OrderDef> _orders = new ConcurrentDictionary<long, OrderDef>();
|
||||
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Get All Accessible Accounts")]
|
||||
async public static void spark_get_accounts(string login_token, ExcelAsyncHandle handle)
|
||||
@@ -30,7 +43,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());
|
||||
@@ -42,96 +55,148 @@ namespace bts.api.orderman
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID", IsMacroType = true)]
|
||||
public static object spark_orderservice_cancel_order(long order_id)
|
||||
static Task<core.Result<Response>> cancel_order(long order_id)
|
||||
{
|
||||
var resp = _service.Value.cancel_order(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
|
||||
return parse_response(resp);
|
||||
var resp = _service.Value.cancel_order_async(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
|
||||
return resp;
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Update an Order by Order ID", IsMacroType = true)]
|
||||
public static object spark_orderservice_update_order(long order_id, double price, long quantity)
|
||||
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID")]
|
||||
public static async void spark_orderservice_cancel_order(long order_id, ExcelAsyncHandle handle)
|
||||
{
|
||||
var resp = _service.Value.update_order(new Update() { OrderKey = Convert.ToUInt32(order_id), Price = price, Quantity = Convert.ToUInt32(quantity) });
|
||||
return parse_response(resp);
|
||||
}
|
||||
try
|
||||
{
|
||||
var resp = await cancel_order(order_id);
|
||||
handle.SetResult(parse_response(resp));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
handle.SetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key", IsMacroType = true)]
|
||||
public static object spark_orderservice_send_order_key(
|
||||
[ExcelArgument("Required. Only send the order if > last")] long local_order_id,
|
||||
[ExcelArgument("Required. Security key")] long security_key,
|
||||
[ExcelArgument("Required. Side BUY or SELL")] string side,
|
||||
[ExcelArgument("Required. User")] string user,
|
||||
[ExcelArgument("Required. Account")] string account,
|
||||
[ExcelArgument("Required. Price")] double price,
|
||||
[ExcelArgument("Required. Quantity")] long quantity,
|
||||
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params = null,
|
||||
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
|
||||
[ExcelArgument("OPTIONAL. (YES,NO) - Default YES")] string manual_order_indicator = null,
|
||||
[ExcelArgument("OPTIONAL. (1,2,3,4) - Default 1")] string cti_code = null,
|
||||
[ExcelArgument("OPTIONAL. (CUSTOMER,FIRM) - Default FIRM")] string customer_or_firm = null)
|
||||
static Task<core.Result<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),
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (local_order_id <= _local_order_id)
|
||||
return "local_order_id must be greater than " + _local_order_id;
|
||||
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 = resolve_user(user);
|
||||
if(btsuser == null)
|
||||
return "Could not resolve user: " + user;
|
||||
cr.Quantity = Convert.ToUInt32(quantity);
|
||||
|
||||
var btsuser = await resolve_user(user);
|
||||
if (btsuser == null)
|
||||
throw new ArgumentException("Could not resolve user: " + user);
|
||||
cr.NewOrder.User = btsuser.Name;
|
||||
cr.NewOrder.UserId = btsuser.Id;
|
||||
var btaccount = resolve_account(account);
|
||||
if(btaccount == null)
|
||||
return "Could not resolve account: " + account;
|
||||
var btaccount = await resolve_account(account);
|
||||
if (btaccount == null)
|
||||
throw new ArgumentException("Could not resolve account: " + account);
|
||||
cr.NewOrder.Account = btaccount.Name;
|
||||
cr.NewOrder.AccountId = btaccount.Id;
|
||||
if (string.IsNullOrEmpty(order_type))
|
||||
order_type = "Limit";
|
||||
cr.NewOrder.CustomDef = custom_def(order_type, order_params);
|
||||
if(cr.NewOrder.CustomDef == null || cr.NewOrder.CustomDef.CustomCase == CustomOrderDefinition.CustomOneofCase.None)
|
||||
return "Could not parse order_type and/or order_params info";
|
||||
|
||||
var resp = _service.Value.create_order(cr);
|
||||
_local_order_id = local_order_id;
|
||||
return parse_response(resp, true);
|
||||
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;
|
||||
}
|
||||
|
||||
private static object parse_response(Response resp, bool show_vertically = false)
|
||||
[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)
|
||||
{
|
||||
object[,] response_cells = new JsonFormatter(resp).format(false);
|
||||
if(show_vertically)
|
||||
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)
|
||||
response_cells = bts.excel.Utilities.transpose_keep_dimension(response_cells);
|
||||
return response_cells;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
return user;
|
||||
}
|
||||
|
||||
static BTSAccount resolve_account(string acctname)
|
||||
async static Task<BTSAccount> resolve_account(string acctname)
|
||||
{
|
||||
var acc = _admin_data.Value.Accounts.FirstOrDefault(x => x.Name.ToLowerInvariant() == acctname.ToLowerInvariant());
|
||||
var admin = await _admin_data.Value;
|
||||
if(!admin.success)
|
||||
return null;
|
||||
var acc = admin.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();
|
||||
@@ -144,42 +209,205 @@ namespace bts.api.orderman
|
||||
return null;
|
||||
}
|
||||
|
||||
static long _local_order_id = 0;
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key", IsMacroType = true)]
|
||||
public static object spark_orderservice_send_order(
|
||||
[ExcelArgument("Required. Only send the order if > last")] long local_order_id,
|
||||
[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,
|
||||
[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,
|
||||
[ExcelArgument("Required. Price")] double price,
|
||||
[ExcelArgument("Required. Quantity")] long quantity,
|
||||
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params = null,
|
||||
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
|
||||
[ExcelArgument("OPTIONAL. (YES,NO) - Default YES")] string manual_order_indicator = null,
|
||||
[ExcelArgument("OPTIONAL. (1,2,3,4) - Default 1")] string cti_code = null,
|
||||
[ExcelArgument("OPTIONAL. (CUSTOMER,FIRM) - Default FIRM")] string customer_or_firm = null)
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_param_update,
|
||||
ExcelAsyncHandle handle)
|
||||
{
|
||||
if (local_order_id <= _local_order_id)
|
||||
return "local_order_id must be greater than " + _local_order_id;
|
||||
Venue ven;
|
||||
if (!Utilities.parse_venue(exchange, out ven))
|
||||
return "Can't Parse Exchange: " + (exchange ?? string.Empty);
|
||||
|
||||
uint key;
|
||||
if (MDSubscribe.market_data(ven).lookup_security_key_from_desc(security_desc, out key))
|
||||
try
|
||||
{
|
||||
return spark_orderservice_send_order_key(local_order_id, (long)key, side, user, account, price, quantity, order_type, order_params,
|
||||
time_in_force, manual_order_indicator, cti_code, customer_or_firm);
|
||||
if(should_process_inputs <= 0)
|
||||
{
|
||||
handle.SetResult(-1);
|
||||
return;
|
||||
}
|
||||
if (_orders.TryGetValue(local_order_id, out OrderDef def))
|
||||
{
|
||||
if(!def.response.HasValue) //is new
|
||||
{
|
||||
def.create.Price = price;
|
||||
def.create.Quantity = Convert.ToUInt32(quantity);
|
||||
var resp = await send_order(def.create).Item2;
|
||||
if(resp.success)
|
||||
{
|
||||
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) ||
|
||||
(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)
|
||||
{
|
||||
def.create.Price = price;
|
||||
def.create.Quantity = Convert.ToUInt32(quantity);
|
||||
}
|
||||
handle.SetResult(resp.value.OrderKey);
|
||||
}
|
||||
else
|
||||
handle.SetResult(def.response.Value.value.OrderKey);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
handle.SetResult(-1);
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return "Can't find security desc " + security_desc;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,723 @@
|
||||
/*
|
||||
[The "BSD licence"]
|
||||
Copyright (c) 2013 Sam Harwell
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use
|
||||
in source and binary forms, with or without
|
||||
modification, are permitted provided that the
|
||||
following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form
|
||||
must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the
|
||||
author may not be used to endorse or promote products
|
||||
derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** C 2011 grammar built from the C11 Spec */
|
||||
grammar C;
|
||||
|
||||
primaryExpression:
|
||||
Identifier
|
||||
| Constant
|
||||
| StringLiteral+
|
||||
| '(' expression ')'
|
||||
| genericSelection
|
||||
| '__extension__'? '(' compoundStatement ')' // Blocks (GCC extension)
|
||||
| '__builtin_va_arg' '(' unaryExpression ',' typeName ')'
|
||||
| '__builtin_offsetof' '(' typeName ',' unaryExpression ')';
|
||||
|
||||
genericSelection:
|
||||
'_Generic' '(' assignmentExpression ',' genericAssocList ')';
|
||||
|
||||
genericAssocList:
|
||||
genericAssociation
|
||||
| genericAssocList ',' genericAssociation;
|
||||
|
||||
genericAssociation:
|
||||
typeName ':' assignmentExpression
|
||||
| 'default' ':' assignmentExpression;
|
||||
|
||||
postfixExpression:
|
||||
primaryExpression
|
||||
| postfixExpression '[' expression ']'
|
||||
| postfixExpression '(' argumentExpressionList? ')'
|
||||
| postfixExpression '.' Identifier
|
||||
| postfixExpression '->' Identifier
|
||||
| postfixExpression '++'
|
||||
| postfixExpression '--'
|
||||
| '(' typeName ')' '{' initializerList '}'
|
||||
| '(' typeName ')' '{' initializerList ',' '}'
|
||||
| '__extension__' '(' typeName ')' '{' initializerList '}'
|
||||
| '__extension__' '(' typeName ')' '{' initializerList ',' '}';
|
||||
|
||||
argumentExpressionList:
|
||||
assignmentExpression
|
||||
| argumentExpressionList ',' assignmentExpression;
|
||||
|
||||
unaryExpression:
|
||||
postfixExpression
|
||||
| '++' unaryExpression
|
||||
| '--' unaryExpression
|
||||
| unaryOperator castExpression
|
||||
| 'sizeof' unaryExpression
|
||||
| 'sizeof' '(' typeName ')'
|
||||
| '_Alignof' '(' typeName ')'
|
||||
| '&&' Identifier ; // GCC extension address of label
|
||||
|
||||
unaryOperator: '&' | '*' | '+' | '-' | '~' | '!';
|
||||
|
||||
castExpression:
|
||||
'(' typeName ')' castExpression
|
||||
| '__extension__' '(' typeName ')' castExpression
|
||||
| unaryExpression
|
||||
| DigitSequence ; // for
|
||||
|
||||
multiplicativeExpression:
|
||||
castExpression
|
||||
| multiplicativeExpression '*' castExpression
|
||||
| multiplicativeExpression '/' castExpression
|
||||
| multiplicativeExpression '%' castExpression;
|
||||
|
||||
additiveExpression:
|
||||
multiplicativeExpression
|
||||
| additiveExpression '+' multiplicativeExpression
|
||||
| additiveExpression '-' multiplicativeExpression;
|
||||
|
||||
shiftExpression:
|
||||
additiveExpression
|
||||
| shiftExpression '<<' additiveExpression
|
||||
| shiftExpression '>>' additiveExpression;
|
||||
|
||||
relationalExpression:
|
||||
shiftExpression
|
||||
| relationalExpression '<' shiftExpression
|
||||
| relationalExpression '>' shiftExpression
|
||||
| relationalExpression '<=' shiftExpression
|
||||
| relationalExpression '>=' shiftExpression;
|
||||
|
||||
equalityExpression:
|
||||
relationalExpression
|
||||
| equalityExpression '==' relationalExpression
|
||||
| equalityExpression '!=' relationalExpression;
|
||||
|
||||
andExpression:
|
||||
equalityExpression
|
||||
| andExpression '&' equalityExpression;
|
||||
|
||||
exclusiveOrExpression:
|
||||
andExpression
|
||||
| exclusiveOrExpression '^' andExpression;
|
||||
|
||||
inclusiveOrExpression:
|
||||
exclusiveOrExpression
|
||||
| inclusiveOrExpression '|' exclusiveOrExpression;
|
||||
|
||||
logicalAndExpression:
|
||||
inclusiveOrExpression
|
||||
| logicalAndExpression '&&' inclusiveOrExpression;
|
||||
|
||||
logicalOrExpression:
|
||||
logicalAndExpression
|
||||
| logicalOrExpression '||' logicalAndExpression;
|
||||
|
||||
conditionalExpression:
|
||||
logicalOrExpression (
|
||||
'?' expression ':' conditionalExpression
|
||||
)?;
|
||||
|
||||
assignmentExpression:
|
||||
conditionalExpression
|
||||
| unaryExpression assignmentOperator assignmentExpression
|
||||
| DigitSequence ; // for
|
||||
|
||||
assignmentOperator:
|
||||
'='
|
||||
| '*='
|
||||
| '/='
|
||||
| '%='
|
||||
| '+='
|
||||
| '-='
|
||||
| '<<='
|
||||
| '>>='
|
||||
| '&='
|
||||
| '^='
|
||||
| '|=';
|
||||
|
||||
expression:
|
||||
assignmentExpression
|
||||
| expression ',' assignmentExpression;
|
||||
|
||||
constantExpression: conditionalExpression;
|
||||
|
||||
declaration:
|
||||
declarationSpecifiers initDeclaratorList ';'
|
||||
| declarationSpecifiers ';'
|
||||
| staticAssertDeclaration;
|
||||
|
||||
declarationSpecifiers: declarationSpecifier+;
|
||||
|
||||
declarationSpecifiers2: declarationSpecifier+;
|
||||
|
||||
declarationSpecifier:
|
||||
storageClassSpecifier
|
||||
| typeSpecifier
|
||||
| typeQualifier
|
||||
| functionSpecifier
|
||||
| alignmentSpecifier;
|
||||
|
||||
initDeclaratorList:
|
||||
initDeclarator
|
||||
| initDeclaratorList ',' initDeclarator;
|
||||
|
||||
initDeclarator: declarator | declarator '=' initializer;
|
||||
|
||||
storageClassSpecifier:
|
||||
'typedef'
|
||||
| 'extern'
|
||||
| 'static'
|
||||
| '_Thread_local'
|
||||
| 'auto'
|
||||
| 'register';
|
||||
|
||||
typeSpecifier: (
|
||||
'void'
|
||||
| 'char'
|
||||
| 'short'
|
||||
| 'int'
|
||||
| 'long'
|
||||
| 'float'
|
||||
| 'double'
|
||||
| 'signed'
|
||||
| 'unsigned'
|
||||
| '_Bool'
|
||||
| '_Complex'
|
||||
| '__m128'
|
||||
| '__m128d'
|
||||
| '__m128i'
|
||||
)
|
||||
| '__extension__' '(' ('__m128' | '__m128d' | '__m128i') ')'
|
||||
| atomicTypeSpecifier
|
||||
| structOrUnionSpecifier
|
||||
| enumSpecifier
|
||||
| typedefName
|
||||
| '__typeof__' '(' constantExpression ')' // GCC extension
|
||||
| typeSpecifier pointer;
|
||||
|
||||
structOrUnionSpecifier:
|
||||
structOrUnion Identifier? '{' structDeclarationList '}'
|
||||
| structOrUnion Identifier;
|
||||
|
||||
structOrUnion: 'struct' | 'union';
|
||||
|
||||
structDeclarationList:
|
||||
structDeclaration
|
||||
| structDeclarationList structDeclaration;
|
||||
|
||||
structDeclaration:
|
||||
specifierQualifierList structDeclaratorList? ';'
|
||||
| staticAssertDeclaration;
|
||||
|
||||
specifierQualifierList:
|
||||
typeSpecifier specifierQualifierList?
|
||||
| typeQualifier specifierQualifierList?;
|
||||
|
||||
structDeclaratorList:
|
||||
structDeclarator
|
||||
| structDeclaratorList ',' structDeclarator;
|
||||
|
||||
structDeclarator:
|
||||
declarator
|
||||
| declarator? ':' constantExpression;
|
||||
|
||||
enumSpecifier:
|
||||
'enum' Identifier? '{' enumeratorList '}'
|
||||
| 'enum' Identifier? '{' enumeratorList ',' '}'
|
||||
| 'enum' Identifier;
|
||||
|
||||
enumeratorList: enumerator | enumeratorList ',' enumerator;
|
||||
|
||||
enumerator:
|
||||
enumerationConstant
|
||||
| enumerationConstant '=' constantExpression;
|
||||
|
||||
enumerationConstant: Identifier;
|
||||
|
||||
atomicTypeSpecifier: '_Atomic' '(' typeName ')';
|
||||
|
||||
typeQualifier: 'const' | 'restrict' | 'volatile' | '_Atomic';
|
||||
|
||||
functionSpecifier: (
|
||||
'inline'
|
||||
| '_Noreturn'
|
||||
| '__inline__' // GCC extension
|
||||
| '__stdcall'
|
||||
)
|
||||
| gccAttributeSpecifier
|
||||
| '__declspec' '(' Identifier ')';
|
||||
|
||||
alignmentSpecifier:
|
||||
'_Alignas' '(' typeName ')'
|
||||
| '_Alignas' '(' constantExpression ')';
|
||||
|
||||
declarator: pointer? directDeclarator gccDeclaratorExtension*;
|
||||
|
||||
directDeclarator:
|
||||
Identifier
|
||||
| '(' declarator ')'
|
||||
| directDeclarator '[' typeQualifierList? assignmentExpression? ']'
|
||||
| directDeclarator '[' 'static' typeQualifierList? assignmentExpression ']'
|
||||
| directDeclarator '[' typeQualifierList 'static' assignmentExpression ']'
|
||||
| directDeclarator '[' typeQualifierList? '*' ']'
|
||||
| directDeclarator '(' parameterTypeList ')'
|
||||
| directDeclarator '(' identifierList? ')'
|
||||
| Identifier ':' DigitSequence // bit field
|
||||
| '(' typeSpecifier? pointer directDeclarator ')' ; // function pointer like: (__cdecl *f)
|
||||
|
||||
gccDeclaratorExtension:
|
||||
'__asm' '(' StringLiteral+ ')'
|
||||
| gccAttributeSpecifier;
|
||||
|
||||
gccAttributeSpecifier:
|
||||
'__attribute__' '(' '(' gccAttributeList ')' ')';
|
||||
|
||||
gccAttributeList:
|
||||
gccAttribute (',' gccAttribute)*
|
||||
| ; // empty
|
||||
|
||||
gccAttribute:
|
||||
~(
|
||||
','
|
||||
| '('
|
||||
| ')'
|
||||
) // relaxed def for "identifier or reserved word"
|
||||
('(' argumentExpressionList? ')')?
|
||||
| ; // empty
|
||||
|
||||
nestedParenthesesBlock: (
|
||||
~('(' | ')')
|
||||
| '(' nestedParenthesesBlock ')'
|
||||
)*;
|
||||
|
||||
pointer:
|
||||
'*' typeQualifierList?
|
||||
| '*' typeQualifierList? pointer
|
||||
| '^' typeQualifierList? // Blocks language extension
|
||||
| '^' typeQualifierList? pointer ; // Blocks language extension
|
||||
|
||||
typeQualifierList:
|
||||
typeQualifier
|
||||
| typeQualifierList typeQualifier;
|
||||
|
||||
parameterTypeList: parameterList | parameterList ',' '...';
|
||||
|
||||
parameterList:
|
||||
parameterDeclaration
|
||||
| parameterList ',' parameterDeclaration;
|
||||
|
||||
parameterDeclaration:
|
||||
declarationSpecifiers declarator
|
||||
| declarationSpecifiers2 abstractDeclarator?;
|
||||
|
||||
identifierList: Identifier | identifierList ',' Identifier;
|
||||
|
||||
typeName: specifierQualifierList abstractDeclarator?;
|
||||
|
||||
abstractDeclarator:
|
||||
pointer
|
||||
| pointer? directAbstractDeclarator gccDeclaratorExtension*;
|
||||
|
||||
directAbstractDeclarator:
|
||||
'(' abstractDeclarator ')' gccDeclaratorExtension*
|
||||
| '[' typeQualifierList? assignmentExpression? ']'
|
||||
| '[' 'static' typeQualifierList? assignmentExpression ']'
|
||||
| '[' typeQualifierList 'static' assignmentExpression ']'
|
||||
| '[' '*' ']'
|
||||
| '(' parameterTypeList? ')' gccDeclaratorExtension*
|
||||
| directAbstractDeclarator '[' typeQualifierList? assignmentExpression? ']'
|
||||
| directAbstractDeclarator '[' 'static' typeQualifierList? assignmentExpression ']'
|
||||
| directAbstractDeclarator '[' typeQualifierList 'static' assignmentExpression ']'
|
||||
| directAbstractDeclarator '[' '*' ']'
|
||||
| directAbstractDeclarator '(' parameterTypeList? ')' gccDeclaratorExtension*;
|
||||
|
||||
typedefName: Identifier;
|
||||
|
||||
initializer:
|
||||
assignmentExpression
|
||||
| '{' initializerList '}'
|
||||
| '{' initializerList ',' '}';
|
||||
|
||||
initializerList:
|
||||
designation? initializer
|
||||
| initializerList ',' designation? initializer;
|
||||
|
||||
designation: designatorList '=';
|
||||
|
||||
designatorList: designator | designatorList designator;
|
||||
|
||||
designator: '[' constantExpression ']' | '.' Identifier;
|
||||
|
||||
staticAssertDeclaration:
|
||||
'_Static_assert' '(' constantExpression ',' StringLiteral+ ')' ';';
|
||||
|
||||
statement:
|
||||
labeledStatement
|
||||
| compoundStatement
|
||||
| expressionStatement
|
||||
| selectionStatement
|
||||
| iterationStatement
|
||||
| jumpStatement
|
||||
| ('__asm' | '__asm__') ('volatile' | '__volatile__') '(' (
|
||||
logicalOrExpression (',' logicalOrExpression)*
|
||||
)? (':' (logicalOrExpression (',' logicalOrExpression)*)?)* ')' ';';
|
||||
|
||||
labeledStatement:
|
||||
Identifier ':' statement
|
||||
| 'case' constantExpression ':' statement
|
||||
| 'default' ':' statement;
|
||||
|
||||
compoundStatement: '{' blockItemList? '}';
|
||||
|
||||
blockItemList: blockItem | blockItemList blockItem;
|
||||
|
||||
blockItem: statement | declaration;
|
||||
|
||||
expressionStatement: expression? ';';
|
||||
|
||||
selectionStatement:
|
||||
'if' '(' expression ')' statement ('else' statement)?
|
||||
| 'switch' '(' expression ')' statement;
|
||||
|
||||
iterationStatement:
|
||||
While '(' expression ')' statement
|
||||
| Do statement While '(' expression ')' ';'
|
||||
| For '(' forCondition ')' statement;
|
||||
|
||||
// | 'for' '(' expression? ';' expression? ';' forUpdate? ')' statement | For '(' declaration
|
||||
// expression? ';' expression? ')' statement
|
||||
|
||||
forCondition:
|
||||
forDeclaration ';' forExpression? ';' forExpression?
|
||||
| expression? ';' forExpression? ';' forExpression?;
|
||||
|
||||
forDeclaration:
|
||||
declarationSpecifiers initDeclaratorList
|
||||
| declarationSpecifiers;
|
||||
|
||||
forExpression:
|
||||
assignmentExpression
|
||||
| forExpression ',' assignmentExpression;
|
||||
|
||||
jumpStatement:
|
||||
'goto' Identifier ';'
|
||||
| 'continue' ';'
|
||||
| 'break' ';'
|
||||
| 'return' expression? ';'
|
||||
| 'goto' unaryExpression ';' ; // GCC extension
|
||||
|
||||
compilationUnit: translationUnit? EOF;
|
||||
|
||||
translationUnit:
|
||||
externalDeclaration
|
||||
| translationUnit externalDeclaration;
|
||||
|
||||
externalDeclaration:
|
||||
functionDefinition
|
||||
| declaration
|
||||
| ';' ; // stray ;
|
||||
|
||||
functionDefinition:
|
||||
declarationSpecifiers? declarator declarationList? compoundStatement;
|
||||
|
||||
declarationList: declaration | declarationList declaration;
|
||||
|
||||
Auto: 'auto';
|
||||
Break: 'break';
|
||||
Case: 'case';
|
||||
Char: 'char';
|
||||
Const: 'const';
|
||||
Continue: 'continue';
|
||||
Default: 'default';
|
||||
Do: 'do';
|
||||
Double: 'double';
|
||||
Else: 'else';
|
||||
Enum: 'enum';
|
||||
Extern: 'extern';
|
||||
Float: 'float';
|
||||
For: 'for';
|
||||
Goto: 'goto';
|
||||
If: 'if';
|
||||
Inline: 'inline';
|
||||
Int: 'int';
|
||||
Long: 'long';
|
||||
Register: 'register';
|
||||
Restrict: 'restrict';
|
||||
Return: 'return';
|
||||
Short: 'short';
|
||||
Signed: 'signed';
|
||||
Sizeof: 'sizeof';
|
||||
Static: 'static';
|
||||
Struct: 'struct';
|
||||
Switch: 'switch';
|
||||
Typedef: 'typedef';
|
||||
Union: 'union';
|
||||
Unsigned: 'unsigned';
|
||||
Void: 'void';
|
||||
Volatile: 'volatile';
|
||||
While: 'while';
|
||||
|
||||
Alignas: '_Alignas';
|
||||
Alignof: '_Alignof';
|
||||
Atomic: '_Atomic';
|
||||
Bool: '_Bool';
|
||||
Complex: '_Complex';
|
||||
Generic: '_Generic';
|
||||
Imaginary: '_Imaginary';
|
||||
Noreturn: '_Noreturn';
|
||||
StaticAssert: '_Static_assert';
|
||||
ThreadLocal: '_Thread_local';
|
||||
|
||||
LeftParen: '(';
|
||||
RightParen: ')';
|
||||
LeftBracket: '[';
|
||||
RightBracket: ']';
|
||||
LeftBrace: '{';
|
||||
RightBrace: '}';
|
||||
|
||||
Less: '<';
|
||||
LessEqual: '<=';
|
||||
Greater: '>';
|
||||
GreaterEqual: '>=';
|
||||
LeftShift: '<<';
|
||||
RightShift: '>>';
|
||||
|
||||
Plus: '+';
|
||||
PlusPlus: '++';
|
||||
Minus: '-';
|
||||
MinusMinus: '--';
|
||||
Star: '*';
|
||||
Div: '/';
|
||||
Mod: '%';
|
||||
|
||||
And: '&';
|
||||
Or: '|';
|
||||
AndAnd: '&&';
|
||||
OrOr: '||';
|
||||
Caret: '^';
|
||||
Not: '!';
|
||||
Tilde: '~';
|
||||
|
||||
Question: '?';
|
||||
Colon: ':';
|
||||
Semi: ';';
|
||||
Comma: ',';
|
||||
|
||||
Assign: '=';
|
||||
// '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '&=' | '^=' | '|='
|
||||
StarAssign: '*=';
|
||||
DivAssign: '/=';
|
||||
ModAssign: '%=';
|
||||
PlusAssign: '+=';
|
||||
MinusAssign: '-=';
|
||||
LeftShiftAssign: '<<=';
|
||||
RightShiftAssign: '>>=';
|
||||
AndAssign: '&=';
|
||||
XorAssign: '^=';
|
||||
OrAssign: '|=';
|
||||
|
||||
Equal: '==';
|
||||
NotEqual: '!=';
|
||||
|
||||
Arrow: '->';
|
||||
Dot: '.';
|
||||
Ellipsis: '...';
|
||||
|
||||
Identifier: IdentifierNondigit ( IdentifierNondigit | Digit)*;
|
||||
|
||||
fragment IdentifierNondigit:
|
||||
Nondigit
|
||||
| UniversalCharacterName
|
||||
; //| // other implementation-defined characters...
|
||||
|
||||
fragment Nondigit: [a-zA-Z_];
|
||||
|
||||
fragment Digit: [0-9];
|
||||
|
||||
fragment UniversalCharacterName:
|
||||
'\\u' HexQuad
|
||||
| '\\U' HexQuad HexQuad;
|
||||
|
||||
fragment HexQuad:
|
||||
HexadecimalDigit HexadecimalDigit HexadecimalDigit HexadecimalDigit;
|
||||
|
||||
Constant:
|
||||
IntegerConstant
|
||||
| FloatingConstant
|
||||
//| EnumerationConstant
|
||||
| CharacterConstant;
|
||||
|
||||
fragment IntegerConstant:
|
||||
DecimalConstant IntegerSuffix?
|
||||
| OctalConstant IntegerSuffix?
|
||||
| HexadecimalConstant IntegerSuffix?
|
||||
| BinaryConstant;
|
||||
|
||||
fragment BinaryConstant: '0' [bB] [0-1]+;
|
||||
|
||||
fragment DecimalConstant: NonzeroDigit Digit*;
|
||||
|
||||
fragment OctalConstant: '0' OctalDigit*;
|
||||
|
||||
fragment HexadecimalConstant:
|
||||
HexadecimalPrefix HexadecimalDigit+;
|
||||
|
||||
fragment HexadecimalPrefix: '0' [xX];
|
||||
|
||||
fragment NonzeroDigit: [1-9];
|
||||
|
||||
fragment OctalDigit: [0-7];
|
||||
|
||||
fragment HexadecimalDigit: [0-9a-fA-F];
|
||||
|
||||
fragment IntegerSuffix:
|
||||
UnsignedSuffix LongSuffix?
|
||||
| UnsignedSuffix LongLongSuffix
|
||||
| LongSuffix UnsignedSuffix?
|
||||
| LongLongSuffix UnsignedSuffix?;
|
||||
|
||||
fragment UnsignedSuffix: [uU];
|
||||
|
||||
fragment LongSuffix: [lL];
|
||||
|
||||
fragment LongLongSuffix: 'll' | 'LL';
|
||||
|
||||
fragment FloatingConstant:
|
||||
DecimalFloatingConstant
|
||||
| HexadecimalFloatingConstant;
|
||||
|
||||
fragment DecimalFloatingConstant:
|
||||
FractionalConstant ExponentPart? FloatingSuffix?
|
||||
| DigitSequence ExponentPart FloatingSuffix?;
|
||||
|
||||
fragment HexadecimalFloatingConstant:
|
||||
HexadecimalPrefix HexadecimalFractionalConstant BinaryExponentPart FloatingSuffix?
|
||||
| HexadecimalPrefix HexadecimalDigitSequence BinaryExponentPart FloatingSuffix?;
|
||||
|
||||
fragment FractionalConstant:
|
||||
DigitSequence? '.' DigitSequence
|
||||
| DigitSequence '.';
|
||||
|
||||
fragment ExponentPart:
|
||||
'e' Sign? DigitSequence
|
||||
| 'E' Sign? DigitSequence;
|
||||
|
||||
fragment Sign: '+' | '-';
|
||||
|
||||
DigitSequence: Digit+;
|
||||
|
||||
fragment HexadecimalFractionalConstant:
|
||||
HexadecimalDigitSequence? '.' HexadecimalDigitSequence
|
||||
| HexadecimalDigitSequence '.';
|
||||
|
||||
fragment BinaryExponentPart:
|
||||
'p' Sign? DigitSequence
|
||||
| 'P' Sign? DigitSequence;
|
||||
|
||||
fragment HexadecimalDigitSequence: HexadecimalDigit+;
|
||||
|
||||
fragment FloatingSuffix: 'f' | 'l' | 'F' | 'L';
|
||||
|
||||
fragment CharacterConstant:
|
||||
'\'' CCharSequence '\''
|
||||
| 'L\'' CCharSequence '\''
|
||||
| 'u\'' CCharSequence '\''
|
||||
| 'U\'' CCharSequence '\'';
|
||||
|
||||
fragment CCharSequence: CChar+;
|
||||
|
||||
fragment CChar: ~['\\\r\n] | EscapeSequence;
|
||||
|
||||
fragment EscapeSequence:
|
||||
SimpleEscapeSequence
|
||||
| OctalEscapeSequence
|
||||
| HexadecimalEscapeSequence
|
||||
| UniversalCharacterName;
|
||||
|
||||
fragment SimpleEscapeSequence: '\\' ['"?abfnrtv\\];
|
||||
|
||||
fragment OctalEscapeSequence:
|
||||
'\\' OctalDigit
|
||||
| '\\' OctalDigit OctalDigit
|
||||
| '\\' OctalDigit OctalDigit OctalDigit;
|
||||
|
||||
fragment HexadecimalEscapeSequence: '\\x' HexadecimalDigit+;
|
||||
|
||||
StringLiteral: EncodingPrefix? '"' SCharSequence? '"';
|
||||
|
||||
fragment EncodingPrefix: 'u8' | 'u' | 'U' | 'L';
|
||||
|
||||
fragment SCharSequence: SChar+;
|
||||
|
||||
fragment SChar:
|
||||
~["\\\r\n]
|
||||
| EscapeSequence
|
||||
| '\\\n' // Added line
|
||||
| '\\\r\n' ; // Added line
|
||||
|
||||
ComplexDefine: '#' Whitespace? 'define' ~[#]* -> skip;
|
||||
|
||||
IncludeDirective:
|
||||
'#' Whitespace? 'include' Whitespace? (
|
||||
('"' ~[\r\n]* '"')
|
||||
| ('<' ~[\r\n]* '>')
|
||||
) Whitespace? Newline -> skip;
|
||||
|
||||
// ignore the following asm blocks:
|
||||
/*
|
||||
asm
|
||||
{
|
||||
mfspr x, 286;
|
||||
}
|
||||
*/
|
||||
AsmBlock: 'asm' ~'{'* '{' ~'}'* '}' -> skip;
|
||||
|
||||
// ignore the lines generated by c preprocessor sample line : '#line 1 "/home/dm/files/dk1.h" 1'
|
||||
LineAfterPreprocessing: '#line' Whitespace* ~[\r\n]* -> skip;
|
||||
|
||||
LineDirective:
|
||||
'#' Whitespace? DecimalConstant Whitespace? StringLiteral ~[\r\n]* -> skip;
|
||||
|
||||
PragmaDirective:
|
||||
'#' Whitespace? 'pragma' Whitespace ~[\r\n]* -> skip;
|
||||
|
||||
Whitespace: [ \t]+ -> skip;
|
||||
|
||||
Newline: ( '\r' '\n'? | '\n') -> skip;
|
||||
|
||||
BlockComment: '/*' .*? '*/' -> skip;
|
||||
|
||||
LineComment: '//' ~[\r\n]* -> skip;
|
||||
@@ -0,0 +1,93 @@
|
||||
grammar ExcelFormula;
|
||||
|
||||
/* Parser rules */
|
||||
main: '=' expr EOF;
|
||||
expr:
|
||||
sign = ('+' | '-') expr # Unary
|
||||
| expr '%' # Percentage
|
||||
| <assoc=right> expr '^' expr # Pow
|
||||
| expr op = ('*' | '/') expr # MulDiv
|
||||
| expr op = ('+' | '-') expr # AddSub
|
||||
| expr '&' expr # Concatenate
|
||||
| expr op = ('=' | '<>') expr # Equality
|
||||
| expr op = ('<' | '>' | '<=' | '>=') expr # Relational
|
||||
| '(' expr ')' # Parenthesis
|
||||
| name = NAME '(' (expr (',' expr)*)? ')' # Function
|
||||
| rng # Range
|
||||
| rng (',' rng)+ # RangeList
|
||||
| ref # Cell
|
||||
| ref (',' ref)+ # CellList
|
||||
| BOOLEAN # Boolean
|
||||
| NUMBER # Number
|
||||
| NAME # Name
|
||||
| STRING # String
|
||||
| ERROR # Error;
|
||||
ref: sheet = SHEET? colRow = COL_ROW # CellRef;
|
||||
rng: start = ref ':' end = COL_ROW # RangeRef;
|
||||
|
||||
/* Lexer rules */
|
||||
COL_ROW: '$'? COL '$'? DIGIT+;
|
||||
STRING: '"' ~["]* '"';
|
||||
BOOLEAN: T R U E | F A L S E;
|
||||
NUMBER: FLOAT | DIGIT+;
|
||||
NAME: [a-zA-Z_\\] ([0-9a-zA-Z_.\\])*;
|
||||
ERROR:
|
||||
E_NULL
|
||||
| E_DIV0
|
||||
| E_VALUE
|
||||
| E_NAME
|
||||
| E_NUM
|
||||
| E_NA
|
||||
| E_REF;
|
||||
E_NULL: '#' N U L L '!';
|
||||
E_DIV0: '#' D I V '/0';
|
||||
E_VALUE: '#' V A L U E '!';
|
||||
E_NAME: '#' N A M E '?';
|
||||
E_NUM: '#' N U M '!';
|
||||
E_NA: '#' N '/' A;
|
||||
E_REF: '#' R E F '!';
|
||||
SHEET:
|
||||
SHEET_C+ '!'
|
||||
| SHEET_C+ ':'+ SHEET_C+ '!'
|
||||
| '\'' Q_SHEET_C+ '\'!'
|
||||
| '\'' Q_SHEET_C+ ':' Q_SHEET_C+ '\'!';
|
||||
SPACE: (' ' | '\t' | '\r' | '\n' | '\u000C') -> skip;
|
||||
INVALID: .;
|
||||
|
||||
/* Lexer fragments */
|
||||
fragment COL: [a-zA-Z]? [a-zA-Z]? [a-zA-Z]; // Max col is XFD
|
||||
fragment DIGIT: [0-9];
|
||||
fragment FLOAT: DIGIT+ '.' DIGIT+ EXPONENT? | DIGIT+ EXPONENT;
|
||||
fragment EXPONENT: E ('+'? | '-') DIGIT+;
|
||||
fragment FILE: '[' DIGIT+ ']' | '[' FILENAME_C+ ']';
|
||||
fragment WIN_PATH: [A-Z] ':\\' (FILENAME_C+ '\\')*;
|
||||
fragment FILENAME_C: ~["*[\]\\:/?<>|];
|
||||
fragment SHEET_C: ~['*[\]\\:/?<>();{}#"=&+^%,!\u2229\u222A \-];
|
||||
fragment Q_SHEET_C: ~['*[\]\\:/?];
|
||||
|
||||
fragment A: ('a' | 'A');
|
||||
fragment B: ('b' | 'B');
|
||||
fragment C: ('c' | 'C');
|
||||
fragment D: ('d' | 'D');
|
||||
fragment E: ('e' | 'E');
|
||||
fragment F: ('f' | 'F');
|
||||
fragment G: ('g' | 'G');
|
||||
fragment H: ('h' | 'H');
|
||||
fragment I: ('i' | 'I');
|
||||
fragment J: ('j' | 'J');
|
||||
fragment K: ('k' | 'K');
|
||||
fragment L: ('l' | 'L');
|
||||
fragment M: ('m' | 'M');
|
||||
fragment N: ('n' | 'N');
|
||||
fragment O: ('o' | 'O');
|
||||
fragment P: ('p' | 'P');
|
||||
fragment Q: ('q' | 'Q');
|
||||
fragment R: ('r' | 'R');
|
||||
fragment S: ('s' | 'S');
|
||||
fragment T: ('t' | 'T');
|
||||
fragment U: ('u' | 'U');
|
||||
fragment V: ('v' | 'V');
|
||||
fragment W: ('w' | 'W');
|
||||
fragment X: ('x' | 'X');
|
||||
fragment Y: ('y' | 'Y');
|
||||
fragment Z: ('z' | 'Z');
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="ExcelDna.AddIn" version="0.34.6" targetFramework="net471" />
|
||||
<package id="ExcelDna.Integration" version="0.34.6" targetFramework="net471" />
|
||||
</packages>
|
||||
@@ -48,30 +48,50 @@ namespace bts.excel.rtd
|
||||
MDSubscribe.market_data(ven).lookup_security_desc((uint)security_key, out string desc);
|
||||
return desc ?? string.Empty;
|
||||
}
|
||||
|
||||
static IObservable<IEnumerable<object>> md_observable(string contract, string symbol, string security_type, string exchange)
|
||||
|
||||
internal static Func<SecurityImmutable, bool> md_where(string contract, string symbol, string security_type, string exchange, out Venue ven)
|
||||
{
|
||||
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))
|
||||
return Observable.Return(new [] {"Can't Parse Exchange: " + exchange ?? string.Empty});
|
||||
throw new ArgumentException("Can't Parse Exchange: " + exchange ?? string.Empty);
|
||||
if (!Utilities.parse_security_type(security_type, out sectype))
|
||||
return Observable.Return(new [] {"Can't Parse Security Type: " + security_type ?? string.Empty});
|
||||
throw new ArgumentException("Can't Parse Security Type: " + security_type ?? string.Empty);
|
||||
if (string.IsNullOrEmpty(symbol))
|
||||
return Observable.Return(new []{"Can't Parse Symbol: " + symbol ?? string.Empty});
|
||||
throw new ArgumentException("Can't Parse Symbol: " + symbol ?? string.Empty);
|
||||
if (string.IsNullOrEmpty(contract))
|
||||
return Observable.Return(new []{"Can't Parse Contract: " + contract ?? string.Empty});
|
||||
throw new ArgumentException("Can't Parse Contract: " + contract ?? string.Empty );
|
||||
|
||||
Func<SecurityImmutable, bool> where = (x => x.Exchange == ven && x.Symbol == symbol && x.security_type() == sectype &&
|
||||
x.Glbx.SecurityDesc == contract);
|
||||
Venue ven_ = ven;
|
||||
Func<SecurityImmutable, bool> where = (x => x.Exchange == ven_ && x.Symbol == symbol &&
|
||||
x.security_type() == sectype && x.security_desc() == contract);
|
||||
return where;
|
||||
}
|
||||
|
||||
var stream = market_data(ven).subscribe_combined(where);
|
||||
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);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Subscribe to BTS Spark full depth market data")]
|
||||
public static object spark_md_book(
|
||||
@@ -90,16 +110,7 @@ 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(
|
||||
@@ -174,7 +185,6 @@ namespace bts.excel.rtd
|
||||
return TopicMDFormatter.format(upd, topic);
|
||||
return x;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,12 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<NoWarn>0108,3021</NoWarn>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<Antlr4UseCSharpGenerator>true</Antlr4UseCSharpGenerator>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -28,10 +31,21 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="System.Linq.Dynamic" Version="1.0.7" />
|
||||
|
||||
<PackageReference Include="ExcelDna.AddIn" Version="0.34.6" />
|
||||
<PackageReference Include="ExcelDna.Integration" Version="0.34.6" />
|
||||
<PackageReference Include="ExcelDna.AddIn" Version="1.0.0" />
|
||||
<PackageReference Include="ExcelDna.Integration" Version="1.0.0" />
|
||||
<PackageReference Include="ExcelDna.IntelliSense" Version="1.1.0" />
|
||||
<PackageReference Include="ExcelDna.Registration" Version="0.34.7" />
|
||||
<PackageReference Include="ExcelDna.Interop" Version="14.0.1" />
|
||||
|
||||
<PackageReference Include="Antlr4" Version="4.6.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Antlr4.Runtime" Version="4.6.6" />
|
||||
<PackageReference Include="Antlr4.CodeGenerator" Version="4.6.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -44,27 +58,45 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="spark_excel-AddIn.dna" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\ExcelDna.Build.props" />
|
||||
<None Include="$(IntermediateOutputPath)\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<!-- <ItemGroup>
|
||||
<Antlr4 Include="grammar/ExcelFormula.g4">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CustomToolNamespace>bts.excel.grammar</CustomToolNamespace>
|
||||
<Listener>True</Listener>
|
||||
<Visitor>True</Visitor>
|
||||
</Antlr4>
|
||||
</ItemGroup> -->
|
||||
|
||||
<ItemGroup>
|
||||
<Proto3Excel Include="**/*.proto" />
|
||||
<Proto3Excel Include="../../lib/api/services/*.proto" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="spark_excel_example_2018.xlsm"><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="../../sdk/examples/ExcelAlgoTemplate.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<!-- <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
|
||||
<ItemGroup>
|
||||
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
|
||||
@@ -74,9 +106,9 @@
|
||||
-->
|
||||
|
||||
<!-- <Import Project="$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets" Condition="Exists('$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets')" /> -->
|
||||
<Import Project="$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets"/>
|
||||
<!-- <Import Project="$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets"/>
|
||||
<Target Name="EnsureExcelDnaTargetsImported" BeforeTargets="BeforeBuild" Condition="'$(ExcelDnaTargetsImported)' == ''">
|
||||
<Error Condition="!Exists('$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets') And ('$(RunExcelDnaBuild)' != '' And $(RunExcelDnaBuild))" Text="You are trying to build with ExcelDna, but the NuGet targets file that ExcelDna depends on is not available on this computer. This is probably because the ExcelDna package has not been committed to source control, or NuGet Package Restore is not enabled. Please enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
|
||||
<Error Condition="Exists('$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets') And ('$(RunExcelDnaBuild)' != '' And $(RunExcelDnaBuild))" Text="ExcelDna cannot be run because NuGet packages were restored prior to the build running, and the targets file was unavailable when the build started. Please build the project again to include these packages in the build. You may also need to make sure that your build server does not delete packages prior to each build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
|
||||
</Target>
|
||||
</Target> -->
|
||||
</Project>
|
||||
@@ -61,6 +61,7 @@
|
||||
<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>
|
||||
|
||||
@@ -99,6 +100,7 @@
|
||||
<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>
|
||||
@@ -470,22 +470,35 @@ message FilteredAdminDataRequest { }
|
||||
service AdminService {
|
||||
option (bts.api.service_description) = "Provides a management interface to the user model";
|
||||
// account
|
||||
rpc create_account (AccountCreateAPIArgument) returns (AdminServiceCreateAccountResult);
|
||||
rpc update_account (AccountUpdateAPIArgument) returns (AdminServiceOperationResult);
|
||||
rpc delete_account(IDAPIArgument) returns (AdminServiceOperationResult);
|
||||
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; };
|
||||
// users
|
||||
rpc create_user(UserCreateAPIArgument) returns (AdminServiceCreateEntityResult);
|
||||
rpc update_user(UserUpdateAPIArgument) returns (AdminServiceOperationResult);
|
||||
rpc delete_user(IDAPIArgument) returns (AdminServiceOperationResult);
|
||||
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; };
|
||||
// subscription
|
||||
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);
|
||||
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; };
|
||||
// orders
|
||||
rpc get_order_summary(bts.api.VoidMessage) returns (OrdersSummary);
|
||||
rpc cancel_orders(OrderCancelRequest) returns (bts.api.VoidMessage);
|
||||
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; };
|
||||
// reports
|
||||
rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEntitlementReportUnit);
|
||||
rpc md_entitlement_data(bts.api.VoidMessage) returns (stream MDEntitlementReportUnit)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
// misc
|
||||
rpc md_permissions_data(bts.api.VoidMessage) returns (stream UserSymbolMDPermissionUnit);
|
||||
rpc md_permissions_data(bts.api.VoidMessage) returns (stream UserSymbolMDPermissionUnit)
|
||||
{ option(skip_excel_generation) = true; };
|
||||
}
|
||||
@@ -12,6 +12,8 @@ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/sdk
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/build_examples.sh
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/md_publisher_example.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/order_plugin_example.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/excel_plugin_example.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/ExcelAlgoTemplate.cpp
|
||||
server_layout)
|
||||
|
||||
add_custom_target(sdk_dir ALL DEPENDS ${CMAKE_BINARY_DIR}/sdk)
|
||||
|
||||
@@ -0,0 +1,701 @@
|
||||
#include "spark/lib/config/config.h"
|
||||
#include "spark/lib/config/protos/system.pb.h"
|
||||
#include "spark/lib/domain_model/Security.h"
|
||||
#include "spark/lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
#include "spark/lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "spark/lib/plugin/SparkServices.h"
|
||||
|
||||
#include "spark/lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "spark/lib/orderman/orderman_utils/Book.h"
|
||||
#include "spark/lib/utils/PBUtils.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "spark/lib/os/os.h"
|
||||
|
||||
#include <variant>
|
||||
|
||||
using namespace bts::api::orderman;
|
||||
using namespace bts::orderman;
|
||||
|
||||
// This file serves as a template for the excel algogen process the following defines are replaced by the algogen
|
||||
// I thought it useful to put this file in the build to make sure the template compiles in it's own right
|
||||
|
||||
//#define __AlgoName__ __
|
||||
#define __FunctionDefs__
|
||||
#define __FunctionImpls__
|
||||
#define __FunctionCalls__
|
||||
|
||||
#define __ParameterDefs__
|
||||
#define __ParameterLaunches__
|
||||
#define __ParameterSetups__ do {} while(0);
|
||||
#define __ParameterUpdates__ do {} while(0);
|
||||
|
||||
#define __DualDefs__
|
||||
#define __DualImpls__
|
||||
|
||||
#define __OrderSetups__ do {} while(0);
|
||||
#define __OrderAttaches__ 0
|
||||
|
||||
#define __NeedsBook__ true
|
||||
#define __NeedsTrades__ true
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace bts
|
||||
{
|
||||
namespace excel_algos___AlgoName__
|
||||
{
|
||||
|
||||
//excel representation of a book
|
||||
struct ExcelBookAccessor
|
||||
{
|
||||
Book& book;
|
||||
ExcelBookAccessor(Book& book) : book(book) { }
|
||||
const double& operator()(int row, int col) const
|
||||
{
|
||||
return this->operator()(row, col);
|
||||
}
|
||||
double operator()(int row, int col)
|
||||
{
|
||||
row = row - 5; //TODO c++ book vs c# book
|
||||
if(col == 0)
|
||||
{
|
||||
int z = row-book.n_levels();
|
||||
if(z < book.n_levels() && z >= 0)
|
||||
return book.bid()[z].size();
|
||||
else
|
||||
return os::nan();
|
||||
}
|
||||
else if(col == 1)
|
||||
{
|
||||
if(row < book.n_levels())
|
||||
{
|
||||
int z = book.n_levels()-1-row;
|
||||
return book.ask()[z].price();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(row < 2*book.n_levels())
|
||||
{
|
||||
int z = row-book.n_levels();
|
||||
return book.bid()[z].price();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(col == 2)
|
||||
{
|
||||
int z = book.n_levels()-1-row;
|
||||
if(z < book.n_levels() && z >= 0)
|
||||
return book.ask()[z].size();
|
||||
}
|
||||
return os::nan();
|
||||
}
|
||||
};
|
||||
|
||||
//typedef std::variant<uint, int, double, std::string> vtypes;
|
||||
struct ChildStateAccessor
|
||||
{
|
||||
bts::orderman::ChildState& state;
|
||||
ChildStateAccessor(bts::orderman::ChildState& state) : state(state) { }
|
||||
const double& operator()(int row, int col) const
|
||||
{
|
||||
return this->operator()(row, col);
|
||||
}
|
||||
double operator()(int row, int col)
|
||||
{
|
||||
if(row != 0)
|
||||
return os::nan();
|
||||
else
|
||||
{
|
||||
switch(col)
|
||||
{
|
||||
case 0:
|
||||
return state.price.value();
|
||||
case 1:
|
||||
return state.quantity.value();
|
||||
case 2:
|
||||
return state.total_fills.value();
|
||||
case 3:
|
||||
return state.avg_price.value();
|
||||
case 4:
|
||||
return state.last_fills.value();
|
||||
case 5:
|
||||
return state.last_price.value();
|
||||
case 6:
|
||||
return (int)state.status;
|
||||
default:
|
||||
return os::nan();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct Range;
|
||||
|
||||
template<typename T>
|
||||
struct range_iterator : public std::iterator<std::forward_iterator_tag, T>
|
||||
{
|
||||
Range<T>& p;
|
||||
int row, col;
|
||||
public:
|
||||
range_iterator(Range<T>& x, int r, int c) : p(x), row(r), col(c) {}
|
||||
range_iterator(const range_iterator& mit) : p(mit.p), row(mit.row), col(mit.col) {}
|
||||
range_iterator& operator++()
|
||||
{
|
||||
if(++col < p.ncols())
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
col = 0;
|
||||
++row;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
range_iterator operator++(int) {range_iterator tmp(*this); operator++(); return tmp;}
|
||||
bool operator==(const range_iterator& rhs) const {return row == rhs.row && col == rhs.col;}
|
||||
bool operator!=(const range_iterator& rhs) const {return row != rhs.row || col != rhs.col;}
|
||||
T& operator*()
|
||||
{
|
||||
auto& v = p(row, col);
|
||||
return v;
|
||||
}
|
||||
const T& operator*() const
|
||||
{
|
||||
const auto& v = p(row, col);
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
//https://stackoverflow.com/questions/8054273/how-to-implement-an-stl-style-iterator-and-avoid-common-pitfalls
|
||||
template<typename T>
|
||||
struct Range
|
||||
{
|
||||
std::vector< std::vector<T> > rows;
|
||||
Range() : rows()
|
||||
{
|
||||
}
|
||||
Range(int nrows, int ncols)
|
||||
{
|
||||
for(int i = 0; i < nrows; i++)
|
||||
rows.push_back( std::vector<T>(ncols) );
|
||||
}
|
||||
Range(const Range& rhs)
|
||||
{
|
||||
rows = rhs.rows;
|
||||
}
|
||||
Range& operator=(const Range& rhs)
|
||||
{
|
||||
this->rows = rhs.rows;
|
||||
return *this;
|
||||
}
|
||||
const T& operator()(int r, int c) const
|
||||
{
|
||||
const auto& v = rows[r][c];
|
||||
return v;
|
||||
}
|
||||
T& operator()(int r, int c)
|
||||
{
|
||||
auto& v = rows[r][c];
|
||||
return v;
|
||||
}
|
||||
size_t size() const
|
||||
{
|
||||
if(rows.size())
|
||||
{
|
||||
auto s = rows.size() * rows[0].size();
|
||||
return s;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
bool empty()
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
size_t nrows()
|
||||
{
|
||||
auto r = rows.size();
|
||||
return r;
|
||||
}
|
||||
size_t ncols()
|
||||
{
|
||||
if(rows.size())
|
||||
{
|
||||
auto s = rows[0].size();
|
||||
return s;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
range_iterator<T> begin()
|
||||
{
|
||||
auto beg = range_iterator<T>(*this, 0, 0);
|
||||
return beg;
|
||||
}
|
||||
range_iterator<T> end()
|
||||
{
|
||||
auto end = range_iterator<T>(*this, nrows(), 0);
|
||||
return end;
|
||||
}
|
||||
};
|
||||
|
||||
class __AlgoName__ : public IAlgoOrder
|
||||
{
|
||||
public:
|
||||
__AlgoName__();
|
||||
~__AlgoName__();
|
||||
|
||||
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices&) override;
|
||||
void do_launch(IAlgoServices&) override;
|
||||
void do_launch_attached(IAlgoServices&, OrderKey, ChildState) override;
|
||||
void do_update(const api::orderman::Update&) override;
|
||||
void on_book(SecurityKey, const Book&) override;
|
||||
void on_ticker(SecurityKey, const TradeVector&) override;
|
||||
void on_child_change(OrderKey, const ChildState&) override;
|
||||
|
||||
private:
|
||||
enum class Status;
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
IAlgoServices& algo_services() const;
|
||||
IBeforeLaunchAlgoServices& init_services() const;
|
||||
|
||||
void do_functions();
|
||||
__FunctionDefs__;
|
||||
|
||||
OrderKey sparkalgo_generate_local_orderid();
|
||||
SecurityKey sparkalgo_resolve_security_id(const string& contract = "", const string& symbol = "",
|
||||
const string& security_type = "", const string& exchange = "");
|
||||
ChildStateAccessor sparkalgo_child_state(OrderKey ok, bool ignored);
|
||||
const string& get_user() const;
|
||||
const string& get_account() const;
|
||||
bts::api::Side get_side() const;
|
||||
double get_price() const;
|
||||
uint32_t get_quantity() const;
|
||||
bts::api::orderman::TimeInForce get_time_in_force() const;
|
||||
|
||||
OrderKey cache_attach(OrderKey attached_order, const ChildState& st);
|
||||
OrderKey sparkalgo_setup_order(OrderKey local_order_id, SecurityKey security_id, const string& user,
|
||||
const string& account, bts::api::Side side,
|
||||
bts::api::orderman::TimeInForce time_in_force = bts::api::orderman::TimeInForce::TIME_IN_FORCE_DAY,
|
||||
const string& order_type = "", const string& order_params = "");
|
||||
|
||||
void sparkalgo_send_or_update_order(OrderKey local_order_id, double should_process_inputs, double price, double quantity,
|
||||
const string& order_params_update = "");
|
||||
template <typename T>
|
||||
T& sparkalgo_take_snapshot(const std::string& name, T&);
|
||||
|
||||
|
||||
__DualDefs__;
|
||||
};
|
||||
|
||||
|
||||
const string& __AlgoName__::get_user() const
|
||||
{
|
||||
return init_services().definition().user();
|
||||
}
|
||||
const string& __AlgoName__::get_account() const
|
||||
{
|
||||
return init_services().definition().account();
|
||||
}
|
||||
bts::api::Side __AlgoName__::get_side() const
|
||||
{
|
||||
return init_services().definition().side();
|
||||
}
|
||||
double __AlgoName__::get_price() const
|
||||
{
|
||||
return algo_services().price().value();
|
||||
}
|
||||
uint32_t __AlgoName__::get_quantity() const
|
||||
{
|
||||
return algo_services().quantity().value();
|
||||
}
|
||||
bts::api::orderman::TimeInForce __AlgoName__::get_time_in_force() const
|
||||
{
|
||||
return init_services().definition().time_in_force();
|
||||
}
|
||||
|
||||
struct OrderDef
|
||||
{
|
||||
ChildTemplate* child_template = nullptr;
|
||||
OrderKey child_key = null_order_key();
|
||||
Price price;
|
||||
Quantity quantity;
|
||||
};
|
||||
|
||||
// All state information needed for this algo
|
||||
struct __AlgoName__::PImpl
|
||||
{
|
||||
IBeforeLaunchAlgoServices* init_services = nullptr;
|
||||
IAlgoServices* algo_services = nullptr;
|
||||
std::map<OrderKey, OrderDef> local_order_id_to_child;
|
||||
std::map<OrderKey, bts::orderman::ChildState> child_state;
|
||||
|
||||
typedef std::variant<uint, int, double, std::string, Range<uint>, Range<int>, Range<double>, Range<std::string> > VRtypes;
|
||||
std::map<std::string, VRtypes> snapshots;
|
||||
|
||||
Book last_book;
|
||||
TradeVector last_trades;
|
||||
__ParameterDefs__;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T& __AlgoName__::sparkalgo_take_snapshot(const std::string& tag, T& data)
|
||||
{
|
||||
auto it = me->snapshots.find(tag);
|
||||
if(it == me->snapshots.end())
|
||||
{
|
||||
me->snapshots[tag] = data;
|
||||
return data;
|
||||
}
|
||||
else
|
||||
return std::get<T>(it->second);
|
||||
}
|
||||
|
||||
SecurityKey __AlgoName__::sparkalgo_resolve_security_id(const string& contract, const string& symbol,
|
||||
const string& security_type, const string& exchange)
|
||||
{
|
||||
//bts_assert_msg(contract == "", "ExcelAlgoTemplate doesn't support multiple contracts yet");
|
||||
auto sk = SecurityKey(init_services().security().id());
|
||||
return sk;
|
||||
}
|
||||
|
||||
OrderKey __AlgoName__::sparkalgo_setup_order(
|
||||
OrderKey local_order_id,
|
||||
SecurityKey security_id,
|
||||
const string& user,
|
||||
const string& account,
|
||||
bts::api::Side side,
|
||||
bts::api::orderman::TimeInForce time_in_force,
|
||||
const string& order_type,
|
||||
const string& order_params)
|
||||
{
|
||||
if(me->local_order_id_to_child.find(local_order_id) != me->local_order_id_to_child.end())
|
||||
return local_order_id;
|
||||
auto child_def = init_services().definition();
|
||||
child_def.set_security_key(security_id.value());
|
||||
child_def.set_user(user);
|
||||
child_def.set_account(account);
|
||||
child_def.set_side(side);
|
||||
child_def.set_time_in_force(time_in_force);
|
||||
|
||||
std::string ot = order_type;
|
||||
//TODO custom type
|
||||
if(ot == "")
|
||||
ot = "Limit";
|
||||
ChildTemplate* child_template = nullptr;
|
||||
if(ot == "Limit")
|
||||
{
|
||||
*child_def.mutable_custom_def()->mutable_limit() = LimitOrderDefinition{};
|
||||
child_template = &(init_services().make_child_template(child_def));
|
||||
}
|
||||
else
|
||||
{
|
||||
string par = order_params;
|
||||
bts_assert(false);
|
||||
//*child_def.mutable_custom_def()->CopyFrom(p->as_order_def());
|
||||
}
|
||||
me->local_order_id_to_child[local_order_id] = OrderDef{child_template, null_order_key(),
|
||||
Price(os::nan()), Quantity(0)};
|
||||
return local_order_id;
|
||||
}
|
||||
|
||||
void __AlgoName__::sparkalgo_send_or_update_order(
|
||||
OrderKey local_order_id,
|
||||
double should_process_inputs,
|
||||
double price,
|
||||
double quantity,
|
||||
const string& order_params_update)
|
||||
{
|
||||
if(should_process_inputs > 0 && quantity > 0)
|
||||
{
|
||||
auto it = me->local_order_id_to_child.find(local_order_id);
|
||||
if (it == me->local_order_id_to_child.end())
|
||||
{
|
||||
LOG(OrderMan, Info, "No setup for __AlgoName__ id: {} ", local_order_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto&& def = it->second;
|
||||
if(def.child_key == null_order_key()) //is new
|
||||
{
|
||||
def.price = Price(price);
|
||||
def.quantity = Quantity( (uint32_t)fabs(quantity) );
|
||||
def.child_key = algo_services().launch_child(*def.child_template, def.price, def.quantity);
|
||||
}
|
||||
else //is working
|
||||
{
|
||||
if (def.quantity.value() > 0)
|
||||
{
|
||||
//TODO custom change
|
||||
if (def.price.value() != price || def.quantity.value() != quantity)
|
||||
{
|
||||
Update child_update;
|
||||
child_update.set_price(price);
|
||||
child_update.set_quantity(quantity);
|
||||
algo_services().update_child(def.child_key, child_update);
|
||||
def.price = Price(price);
|
||||
def.quantity = Quantity(quantity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
algo_services().cancel_child(def.child_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__DualImpls__
|
||||
|
||||
__AlgoName__::__AlgoName__() : me(new PImpl()) {}
|
||||
__AlgoName__::~__AlgoName__() {}
|
||||
|
||||
|
||||
IBeforeLaunchAlgoServices& __AlgoName__::init_services() const
|
||||
{
|
||||
if(me->algo_services)
|
||||
return *me->algo_services;
|
||||
else
|
||||
return *me->init_services;
|
||||
}
|
||||
|
||||
IAlgoServices& __AlgoName__::algo_services() const { return *me->algo_services; }
|
||||
|
||||
// This is called outside of the latency sensitive path
|
||||
// once. Expensive initialization should be done here, rather than
|
||||
// in do_launch()
|
||||
OrderOpResult __AlgoName__::do_before_launch(IBeforeLaunchAlgoServices& serv)
|
||||
{
|
||||
me->init_services = &serv;
|
||||
auto &def = serv.definition();
|
||||
bts_assert(def.has_custom_def());
|
||||
bts_assert(def.custom_def().has_dynamic());
|
||||
|
||||
// Validate our parameters, returning an error if we find a
|
||||
// problem. If an error is returned here the order will be
|
||||
// abandoned.
|
||||
auto ¶ms = def.custom_def().dynamic().order_params();
|
||||
|
||||
__ParameterLaunches__;
|
||||
|
||||
if (!serv.is_attaching())
|
||||
{
|
||||
// If we are not attaching, create a child template so we can
|
||||
// quickly launch our child when the time comes.
|
||||
|
||||
// By default, we will be sending a child order with the same side,
|
||||
// security, trader, account, etc, as this algo order, so just
|
||||
// copy our definition to the child definition to start with.
|
||||
__OrderSetups__;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are attaching to an existing order then we do not need
|
||||
// to spawn a new child, so there is no need to setup a child
|
||||
// template
|
||||
}
|
||||
|
||||
// All is good, return a default constructed OrderOpResult which
|
||||
// indicates success.
|
||||
return {};
|
||||
}
|
||||
|
||||
// Called in the latency sensitive path, once, when the algo is first
|
||||
// launched.
|
||||
void __AlgoName__::do_launch(IAlgoServices& services)
|
||||
{
|
||||
// Cache our algo services
|
||||
me->algo_services = &services;
|
||||
|
||||
LOG(OrderMan, Diag, "Launching __AlgoName__ k: {} ",
|
||||
algo_services().order_key().value());
|
||||
|
||||
// Subscribe to market data
|
||||
if(__NeedsBook__)
|
||||
services.subscribe_book( sparkalgo_resolve_security_id() );
|
||||
if(__NeedsTrades__)
|
||||
services.subscribe_ticker( sparkalgo_resolve_security_id() );
|
||||
|
||||
do_functions();
|
||||
}
|
||||
|
||||
OrderKey __AlgoName__::cache_attach(OrderKey attached_order, const ChildState& st)
|
||||
{
|
||||
OrderKey k = sparkalgo_generate_local_orderid();
|
||||
me->local_order_id_to_child[k] = OrderDef{nullptr, attached_order, st.price, st.quantity};
|
||||
return k;
|
||||
}
|
||||
|
||||
// Called in the latency sensitive path, once, when the algo is first
|
||||
// launched. In this case the algo is provided with an existing order
|
||||
// it is now responsible for managing.
|
||||
void __AlgoName__::do_launch_attached(IAlgoServices& services, OrderKey attached_order, ChildState st)
|
||||
{
|
||||
// The order was launched and attached to an existing order, with
|
||||
// key 'attached_order' and current state 'st'
|
||||
|
||||
// Cache our algo services
|
||||
me->algo_services = &services;
|
||||
|
||||
LOG(OrderMan, Diag,
|
||||
"Launching __AlgoName__ k: {} Attached to existing order: {}",
|
||||
algo_services().order_key().value(), attached_order.value());
|
||||
|
||||
// Cache the key of the attached order we will be managing
|
||||
if(me->local_order_id_to_child.size() > 0)
|
||||
algo_services().terminate_error("Can't attach this ExcelAlgoTemplate to order because it contains more than one function");
|
||||
|
||||
__OrderAttaches__;
|
||||
|
||||
if(me->local_order_id_to_child.size() > 1)
|
||||
algo_services().terminate_error("Can't attach this ExcelAlgoTemplate to order because it contains more than one function");
|
||||
|
||||
// Subscribe to market data
|
||||
if(__NeedsBook__)
|
||||
services.subscribe_book( sparkalgo_resolve_security_id() );
|
||||
if(__NeedsTrades__)
|
||||
services.subscribe_ticker( sparkalgo_resolve_security_id() );
|
||||
|
||||
// Simulate a child change so we can update our own state to
|
||||
// reflect that of the child order we attached to. From this point
|
||||
// forward there is no distinction between an algo instance that
|
||||
// spawned a new child order and one that attached to an existing
|
||||
// order.
|
||||
on_child_change(attached_order, st);
|
||||
do_functions();
|
||||
}
|
||||
|
||||
// Called when a human (or a supervising algo) updates a parameter for
|
||||
// this algo. The implementation validates the update and depending on
|
||||
// the validation outcome either applies the update or terminates the
|
||||
// algo.
|
||||
void __AlgoName__::do_update(const api::orderman::Update& update)
|
||||
{
|
||||
LOG(OrderMan, Diag, "__AlgoName__ k: {} receiving update: {}",
|
||||
algo_services().order_key().value(), utils::pb::to_string(update));
|
||||
|
||||
|
||||
// Validate update
|
||||
if (update.quantity() == 0)
|
||||
{
|
||||
// An invalid update has been detected so we terminate the
|
||||
// algo, signaling an error. Any child orders launched by
|
||||
// this algo will also be terminated.
|
||||
algo_services().terminate_error("Invalid update");
|
||||
return;
|
||||
}
|
||||
|
||||
// Updates have two parts, a standard part consisting of price and
|
||||
// size and present in all updates, and an optional part that is
|
||||
// specific to the algo in question. Below we check for the
|
||||
// existance of this optional part and validate if present.
|
||||
if (update.has_custom())
|
||||
{
|
||||
auto ¶ms = update.custom().dynamic().order_params();
|
||||
__ParameterUpdates__;
|
||||
}
|
||||
|
||||
// Update is good, forward it to our child
|
||||
Update child_update;
|
||||
child_update.set_price(update.price());
|
||||
child_update.set_quantity(update.quantity());
|
||||
for(auto & i : me->local_order_id_to_child)
|
||||
algo_services().update_child(i.second.child_key, child_update);
|
||||
|
||||
do_functions();
|
||||
}
|
||||
|
||||
void __AlgoName__::on_ticker(SecurityKey, const TradeVector& t)
|
||||
{
|
||||
me->last_trades = t;
|
||||
LOG(OrderMan, Diag, "__AlgoName__ order on ticker");
|
||||
do_functions();
|
||||
}
|
||||
|
||||
// Called when the book for the specified security has changed
|
||||
void __AlgoName__::on_book(SecurityKey, const Book& b)
|
||||
{
|
||||
me->last_book = b;
|
||||
|
||||
LOG(OrderMan, Diag, "__AlgoName__ order {} on book. BID: {}@{} ASK: {}@{}",
|
||||
algo_services().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
|
||||
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
|
||||
b.side(api::SIDE_SELL)[0].price());
|
||||
|
||||
do_functions();
|
||||
}
|
||||
|
||||
void __AlgoName__::do_functions()
|
||||
{
|
||||
auto my_side = algo_services().definition().side();
|
||||
auto other_side = securities::other_side(my_side);
|
||||
auto &my_side_psz = me->last_book.side(my_side)[0];
|
||||
auto &other_side_psz = me->last_book.side(other_side)[0];
|
||||
|
||||
LOG(OrderMan, Diag,
|
||||
"__AlgoName__ order {} do_functions called. Order: {}@{} Top of "
|
||||
"My side of book: {}@{} Other Side: {}@{}",
|
||||
algo_services().order_key().value(),
|
||||
algo_services().quantity().value(), algo_services().price().value(),
|
||||
my_side_psz.size(), my_side_psz.price(),
|
||||
other_side_psz.size(), other_side_psz.price()
|
||||
);
|
||||
__FunctionCalls__;
|
||||
}
|
||||
|
||||
ChildStateAccessor __AlgoName__::sparkalgo_child_state(OrderKey ok, bool ignored)
|
||||
{
|
||||
UNUSED(ignored);
|
||||
|
||||
auto it = me->child_state.find(ok);
|
||||
bts_assert_msg(it != me->child_state.end(), "can't find order state for key");
|
||||
ChildStateAccessor acc(it->second);
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Our child reported a change:
|
||||
void __AlgoName__::on_child_change(OrderKey key, const ChildState& st)
|
||||
{
|
||||
// Our fill quantity and price is always a reflection of our
|
||||
// child's state
|
||||
me->child_state[key] = st;
|
||||
|
||||
uint32_t fills = 0u; bool all_complete = true, all_cancelled = true;
|
||||
for(const auto& ch : me->child_state)
|
||||
{
|
||||
fills += ch.second.total_fills.value();
|
||||
all_complete = all_complete && (ch.second.status == ChildStatus::COMPLETED || ch.second.status == ChildStatus::CANCELED);
|
||||
all_cancelled = all_cancelled && (ch.second.status == ChildStatus::CANCELED);
|
||||
}
|
||||
algo_services().update_fill_state(Quantity(fills), st.last_price);
|
||||
|
||||
if(all_complete)
|
||||
algo_services().complete();
|
||||
else if(all_cancelled)
|
||||
algo_services().terminate_error("All children cancelled");
|
||||
else
|
||||
do_functions(); //potentially feedback state changes into the order
|
||||
}
|
||||
|
||||
// This is called by the spark daemon at startup. In this call we
|
||||
// describe our algo's parameters and register a factory function to
|
||||
// create an instance of the algo.
|
||||
extern "C" void __Remove__init_spark_plugin(bts::SparkServices& serv)
|
||||
{
|
||||
using namespace bts;
|
||||
|
||||
// Associate the descriptor with a builder function and register
|
||||
// that with the Spark server.
|
||||
__ParameterSetups__;
|
||||
}
|
||||
|
||||
__FunctionImpls__
|
||||
|
||||
|
||||
} // namespace order_example
|
||||
} // namespace bts
|
||||
@@ -24,3 +24,17 @@ g++ -isystem ../externals/include \
|
||||
-Wl,-rpath,.. \
|
||||
-L ../lib \
|
||||
-l spark
|
||||
|
||||
g++ -isystem ../externals/include \
|
||||
-I ../include \
|
||||
-I ../include/spark \
|
||||
-std=c++1z \
|
||||
-fPIC \
|
||||
-g \
|
||||
-shared \
|
||||
-o excel_plugin_example.so \
|
||||
ExcelAlgoTemplate.cpp \
|
||||
excel_plugin_example.cpp \
|
||||
-Wl,-rpath,.. \
|
||||
-L ../lib \
|
||||
-l spark
|
||||
@@ -0,0 +1,950 @@
|
||||
#include "spark/lib/config/config.h"
|
||||
#include "spark/lib/config/protos/system.pb.h"
|
||||
#include "spark/lib/domain_model/Security.h"
|
||||
#include "spark/lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
#include "spark/lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "spark/lib/plugin/SparkServices.h"
|
||||
|
||||
#include "spark/lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "spark/lib/orderman/orderman_utils/Book.h"
|
||||
#include "spark/lib/utils/PBUtils.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "spark/lib/os/os.h"
|
||||
|
||||
#include <variant>
|
||||
|
||||
using namespace bts::api::orderman;
|
||||
using namespace bts::orderman;
|
||||
|
||||
// This file serves as a template for the excel algogen process the following defines are replaced by the algogen
|
||||
// I thought it useful to put this file in the build to make sure the template compiles in it's own right
|
||||
|
||||
//#define algo_example_xlsm __
|
||||
//#define __#FunctionDefs__
|
||||
//#define __#FunctionImpls__
|
||||
//#define __#FunctionCalls__
|
||||
|
||||
//#define __#ParameterDefs__
|
||||
//#define __#ParameterLaunches__
|
||||
//#define __#ParameterSetups__ do {} while(0);
|
||||
//#define __#ParameterUpdates__ do {} while(0);
|
||||
|
||||
//#define __#DualDefs__
|
||||
//#define __#DualImpls__
|
||||
|
||||
//#define __#OrderSetups__ do {} while(0);
|
||||
//#define __#OrderAttaches__ 0
|
||||
|
||||
#define __NeedsBook__ true
|
||||
#define __NeedsTrades__ true
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace bts
|
||||
{
|
||||
namespace excel_algos_algo_example_xlsm
|
||||
{
|
||||
|
||||
//excel representation of a book
|
||||
struct ExcelBookAccessor
|
||||
{
|
||||
Book& book;
|
||||
ExcelBookAccessor(Book& book) : book(book) { }
|
||||
const double& operator()(int row, int col) const
|
||||
{
|
||||
return this->operator()(row, col);
|
||||
}
|
||||
double operator()(int row, int col)
|
||||
{
|
||||
row = row - 5; //TODO c++ book vs c# book
|
||||
if(col == 0)
|
||||
{
|
||||
int z = row-book.n_levels();
|
||||
if(z < book.n_levels() && z >= 0)
|
||||
return book.bid()[z].size();
|
||||
else
|
||||
return os::nan();
|
||||
}
|
||||
else if(col == 1)
|
||||
{
|
||||
if(row < book.n_levels())
|
||||
{
|
||||
int z = book.n_levels()-1-row;
|
||||
return book.ask()[z].price();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(row < 2*book.n_levels())
|
||||
{
|
||||
int z = row-book.n_levels();
|
||||
return book.bid()[z].price();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(col == 2)
|
||||
{
|
||||
int z = book.n_levels()-1-row;
|
||||
if(z < book.n_levels() && z >= 0)
|
||||
return book.ask()[z].size();
|
||||
}
|
||||
return os::nan();
|
||||
}
|
||||
};
|
||||
|
||||
//typedef std::variant<uint, int, double, std::string> vtypes;
|
||||
struct ChildStateAccessor
|
||||
{
|
||||
bts::orderman::ChildState& state;
|
||||
ChildStateAccessor(bts::orderman::ChildState& state) : state(state) { }
|
||||
const double& operator()(int row, int col) const
|
||||
{
|
||||
return this->operator()(row, col);
|
||||
}
|
||||
double operator()(int row, int col)
|
||||
{
|
||||
if(row != 0)
|
||||
return os::nan();
|
||||
else
|
||||
{
|
||||
switch(col)
|
||||
{
|
||||
case 0:
|
||||
return state.price.value();
|
||||
case 1:
|
||||
return state.quantity.value();
|
||||
case 2:
|
||||
return state.total_fills.value();
|
||||
case 3:
|
||||
return state.avg_price.value();
|
||||
case 4:
|
||||
return state.last_fills.value();
|
||||
case 5:
|
||||
return state.last_price.value();
|
||||
case 6:
|
||||
return (int)state.status;
|
||||
default:
|
||||
return os::nan();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct Range;
|
||||
|
||||
template<typename T>
|
||||
struct range_iterator : public std::iterator<std::forward_iterator_tag, T>
|
||||
{
|
||||
Range<T>& p;
|
||||
int row, col;
|
||||
public:
|
||||
range_iterator(Range<T>& x, int r, int c) : p(x), row(r), col(c) {}
|
||||
range_iterator(const range_iterator& mit) : p(mit.p), row(mit.row), col(mit.col) {}
|
||||
range_iterator& operator++()
|
||||
{
|
||||
if(++col < p.ncols())
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
col = 0;
|
||||
++row;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
range_iterator operator++(int) {range_iterator tmp(*this); operator++(); return tmp;}
|
||||
bool operator==(const range_iterator& rhs) const {return row == rhs.row && col == rhs.col;}
|
||||
bool operator!=(const range_iterator& rhs) const {return row != rhs.row || col != rhs.col;}
|
||||
T& operator*()
|
||||
{
|
||||
auto& v = p(row, col);
|
||||
return v;
|
||||
}
|
||||
const T& operator*() const
|
||||
{
|
||||
const auto& v = p(row, col);
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
//https://stackoverflow.com/questions/8054273/how-to-implement-an-stl-style-iterator-and-avoid-common-pitfalls
|
||||
template<typename T>
|
||||
struct Range
|
||||
{
|
||||
std::vector< std::vector<T> > rows;
|
||||
Range() : rows()
|
||||
{
|
||||
}
|
||||
Range(int nrows, int ncols)
|
||||
{
|
||||
for(int i = 0; i < nrows; i++)
|
||||
rows.push_back( std::vector<T>(ncols) );
|
||||
}
|
||||
Range(const Range& rhs)
|
||||
{
|
||||
rows = rhs.rows;
|
||||
}
|
||||
Range& operator=(const Range& rhs)
|
||||
{
|
||||
this->rows = rhs.rows;
|
||||
return *this;
|
||||
}
|
||||
const T& operator()(int r, int c) const
|
||||
{
|
||||
const auto& v = rows[r][c];
|
||||
return v;
|
||||
}
|
||||
T& operator()(int r, int c)
|
||||
{
|
||||
auto& v = rows[r][c];
|
||||
return v;
|
||||
}
|
||||
size_t size() const
|
||||
{
|
||||
if(rows.size())
|
||||
{
|
||||
auto s = rows.size() * rows[0].size();
|
||||
return s;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
bool empty()
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
size_t nrows()
|
||||
{
|
||||
auto r = rows.size();
|
||||
return r;
|
||||
}
|
||||
size_t ncols()
|
||||
{
|
||||
if(rows.size())
|
||||
{
|
||||
auto s = rows[0].size();
|
||||
return s;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
range_iterator<T> begin()
|
||||
{
|
||||
auto beg = range_iterator<T>(*this, 0, 0);
|
||||
return beg;
|
||||
}
|
||||
range_iterator<T> end()
|
||||
{
|
||||
auto end = range_iterator<T>(*this, nrows(), 0);
|
||||
return end;
|
||||
}
|
||||
};
|
||||
|
||||
class algo_example_xlsm : public IAlgoOrder
|
||||
{
|
||||
public:
|
||||
algo_example_xlsm();
|
||||
~algo_example_xlsm();
|
||||
|
||||
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices&) override;
|
||||
void do_launch(IAlgoServices&) override;
|
||||
void do_launch_attached(IAlgoServices&, OrderKey, ChildState) override;
|
||||
void do_update(const api::orderman::Update&) override;
|
||||
void on_book(SecurityKey, const Book&) override;
|
||||
void on_ticker(SecurityKey, const TradeVector&) override;
|
||||
void on_child_change(OrderKey, const ChildState&) override;
|
||||
|
||||
private:
|
||||
enum class Status;
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
IAlgoServices& algo_services() const;
|
||||
IBeforeLaunchAlgoServices& init_services() const;
|
||||
|
||||
void do_functions();
|
||||
//__FunctionDefs__
|
||||
void join_k31();
|
||||
void join_k32();
|
||||
void join_k33();;
|
||||
|
||||
OrderKey sparkalgo_generate_local_orderid();
|
||||
SecurityKey sparkalgo_resolve_security_id(const string& contract = "", const string& symbol = "",
|
||||
const string& security_type = "", const string& exchange = "");
|
||||
ChildStateAccessor sparkalgo_child_state(OrderKey ok, bool ignored);
|
||||
const string& get_user() const;
|
||||
const string& get_account() const;
|
||||
bts::api::Side get_side() const;
|
||||
double get_price() const;
|
||||
uint32_t get_quantity() const;
|
||||
bts::api::orderman::TimeInForce get_time_in_force() const;
|
||||
|
||||
OrderKey cache_attach(OrderKey attached_order, const ChildState& st);
|
||||
OrderKey sparkalgo_setup_order(OrderKey local_order_id, SecurityKey security_id, const string& user,
|
||||
const string& account, bts::api::Side side,
|
||||
bts::api::orderman::TimeInForce time_in_force = bts::api::orderman::TimeInForce::TIME_IN_FORCE_DAY,
|
||||
const string& order_type = "", const string& order_params = "");
|
||||
|
||||
void sparkalgo_send_or_update_order(OrderKey local_order_id, double should_process_inputs, double price, double quantity,
|
||||
const string& order_params_update = "");
|
||||
template <typename T>
|
||||
T& sparkalgo_take_snapshot(const std::string& name, T&);
|
||||
|
||||
|
||||
//__DualDefs__
|
||||
|
||||
ExcelBookAccessor sparkalgo_md_book_id(SecurityKey sk);
|
||||
|
||||
int sparkalgo_snapshot_switch(const std::string& name, int on_off);
|
||||
|
||||
bts::api::Side sparkalgo_other_side(bts::api::Side side);;
|
||||
};
|
||||
|
||||
|
||||
const string& algo_example_xlsm::get_user() const
|
||||
{
|
||||
return init_services().definition().user();
|
||||
}
|
||||
const string& algo_example_xlsm::get_account() const
|
||||
{
|
||||
return init_services().definition().account();
|
||||
}
|
||||
bts::api::Side algo_example_xlsm::get_side() const
|
||||
{
|
||||
return init_services().definition().side();
|
||||
}
|
||||
double algo_example_xlsm::get_price() const
|
||||
{
|
||||
return algo_services().price().value();
|
||||
}
|
||||
uint32_t algo_example_xlsm::get_quantity() const
|
||||
{
|
||||
return algo_services().quantity().value();
|
||||
}
|
||||
bts::api::orderman::TimeInForce algo_example_xlsm::get_time_in_force() const
|
||||
{
|
||||
return init_services().definition().time_in_force();
|
||||
}
|
||||
|
||||
struct OrderDef
|
||||
{
|
||||
ChildTemplate* child_template = nullptr;
|
||||
OrderKey child_key = null_order_key();
|
||||
Price price;
|
||||
Quantity quantity;
|
||||
};
|
||||
|
||||
// All state information needed for this algo
|
||||
struct algo_example_xlsm::PImpl
|
||||
{
|
||||
IBeforeLaunchAlgoServices* init_services = nullptr;
|
||||
IAlgoServices* algo_services = nullptr;
|
||||
std::map<OrderKey, OrderDef> local_order_id_to_child;
|
||||
std::map<OrderKey, bts::orderman::ChildState> child_state;
|
||||
|
||||
typedef std::variant<uint, int, double, std::string, Range<uint>, Range<int>, Range<double>, Range<std::string> > VRtypes;
|
||||
std::map<std::string, VRtypes> snapshots;
|
||||
|
||||
Book last_book;
|
||||
TradeVector last_trades;
|
||||
//__ParameterDefs__
|
||||
double join_thresh;
|
||||
OrderKey join_c31_order_id;
|
||||
OrderKey join_c32_order_id;
|
||||
OrderKey join_c31_order_id;
|
||||
OrderKey join_c33_order_id;
|
||||
OrderKey join_c32_order_id;;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T& algo_example_xlsm::sparkalgo_take_snapshot(const std::string& tag, T& data)
|
||||
{
|
||||
auto it = me->snapshots.find(tag);
|
||||
if(it == me->snapshots.end())
|
||||
{
|
||||
me->snapshots[tag] = data;
|
||||
return data;
|
||||
}
|
||||
else
|
||||
return std::get<T>(it->second);
|
||||
}
|
||||
|
||||
SecurityKey algo_example_xlsm::sparkalgo_resolve_security_id(const string& contract, const string& symbol,
|
||||
const string& security_type, const string& exchange)
|
||||
{
|
||||
//bts_assert_msg(contract == "", "ExcelAlgoTemplate doesn't support multiple contracts yet");
|
||||
auto sk = SecurityKey(init_services().security().id());
|
||||
return sk;
|
||||
}
|
||||
|
||||
OrderKey algo_example_xlsm::sparkalgo_setup_order(
|
||||
OrderKey local_order_id,
|
||||
SecurityKey security_id,
|
||||
const string& user,
|
||||
const string& account,
|
||||
bts::api::Side side,
|
||||
bts::api::orderman::TimeInForce time_in_force,
|
||||
const string& order_type,
|
||||
const string& order_params)
|
||||
{
|
||||
if(me->local_order_id_to_child.find(local_order_id) != me->local_order_id_to_child.end())
|
||||
return local_order_id;
|
||||
auto child_def = init_services().definition();
|
||||
child_def.set_security_key(security_id.value());
|
||||
child_def.set_user(user);
|
||||
child_def.set_account(account);
|
||||
child_def.set_side(side);
|
||||
child_def.set_time_in_force(time_in_force);
|
||||
|
||||
std::string ot = order_type;
|
||||
//TODO custom type
|
||||
if(ot == "")
|
||||
ot = "Limit";
|
||||
ChildTemplate* child_template = nullptr;
|
||||
if(ot == "Limit")
|
||||
{
|
||||
*child_def.mutable_custom_def()->mutable_limit() = LimitOrderDefinition{};
|
||||
child_template = &(init_services().make_child_template(child_def));
|
||||
}
|
||||
else
|
||||
{
|
||||
string par = order_params;
|
||||
bts_assert(false);
|
||||
//*child_def.mutable_custom_def()->CopyFrom(p->as_order_def());
|
||||
}
|
||||
me->local_order_id_to_child[local_order_id] = OrderDef{child_template, null_order_key(),
|
||||
Price(os::nan()), Quantity(0)};
|
||||
return local_order_id;
|
||||
}
|
||||
|
||||
void algo_example_xlsm::sparkalgo_send_or_update_order(
|
||||
OrderKey local_order_id,
|
||||
double should_process_inputs,
|
||||
double price,
|
||||
double quantity,
|
||||
const string& order_params_update)
|
||||
{
|
||||
if(should_process_inputs > 0 && quantity > 0)
|
||||
{
|
||||
auto it = me->local_order_id_to_child.find(local_order_id);
|
||||
if (it == me->local_order_id_to_child.end())
|
||||
{
|
||||
LOG(OrderMan, Info, "No setup for algo_example_xlsm id: {} ", local_order_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto&& def = it->second;
|
||||
if(def.child_key == null_order_key()) //is new
|
||||
{
|
||||
def.price = Price(price);
|
||||
def.quantity = Quantity( (uint32_t)fabs(quantity) );
|
||||
def.child_key = algo_services().launch_child(*def.child_template, def.price, def.quantity);
|
||||
}
|
||||
else //is working
|
||||
{
|
||||
if (def.quantity.value() > 0)
|
||||
{
|
||||
//TODO custom change
|
||||
if (def.price.value() != price || def.quantity.value() != quantity)
|
||||
{
|
||||
Update child_update;
|
||||
child_update.set_price(price);
|
||||
child_update.set_quantity(quantity);
|
||||
algo_services().update_child(def.child_key, child_update);
|
||||
def.price = Price(price);
|
||||
def.quantity = Quantity(quantity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
algo_services().cancel_child(def.child_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//__DualImpls__
|
||||
|
||||
OrderKey algo_example_xlsm::sparkalgo_generate_local_orderid()
|
||||
{
|
||||
static uint32_t id = 0;
|
||||
return OrderKey( ++id );
|
||||
}
|
||||
|
||||
ExcelBookAccessor algo_example_xlsm::sparkalgo_md_book_id(SecurityKey sk)
|
||||
{
|
||||
ExcelBookAccessor acc(me->last_book);
|
||||
return acc;
|
||||
}
|
||||
|
||||
int algo_example_xlsm::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);
|
||||
}
|
||||
}
|
||||
|
||||
bts::api::Side algo_example_xlsm::sparkalgo_other_side(bts::api::Side side)
|
||||
{
|
||||
return securities::other_side(side);
|
||||
}
|
||||
|
||||
algo_example_xlsm::algo_example_xlsm() : me(new PImpl()) {}
|
||||
algo_example_xlsm::~algo_example_xlsm() {}
|
||||
|
||||
|
||||
IBeforeLaunchAlgoServices& algo_example_xlsm::init_services() const
|
||||
{
|
||||
if(me->algo_services)
|
||||
return *me->algo_services;
|
||||
else
|
||||
return *me->init_services;
|
||||
}
|
||||
|
||||
IAlgoServices& algo_example_xlsm::algo_services() const { return *me->algo_services; }
|
||||
|
||||
// This is called outside of the latency sensitive path
|
||||
// once. Expensive initialization should be done here, rather than
|
||||
// in do_launch()
|
||||
OrderOpResult algo_example_xlsm::do_before_launch(IBeforeLaunchAlgoServices& serv)
|
||||
{
|
||||
me->init_services = &serv;
|
||||
auto &def = serv.definition();
|
||||
bts_assert(def.has_custom_def());
|
||||
bts_assert(def.custom_def().has_dynamic());
|
||||
|
||||
// Validate our parameters, returning an error if we find a
|
||||
// problem. If an error is returned here the order will be
|
||||
// abandoned.
|
||||
auto ¶ms = def.custom_def().dynamic().order_params();
|
||||
|
||||
//__ParameterLaunches__
|
||||
|
||||
auto join_thresh_param = params.find("join_thresh");
|
||||
if ( join_thresh_param == params.end() )
|
||||
return OrderOpResult{ ORDER_ERROR_INVALID_ORDER_DEFINITON,"join_thresh parameter must be defined"};
|
||||
me->join_thresh = join_thresh_param->second.as_double();
|
||||
;
|
||||
|
||||
if (!serv.is_attaching())
|
||||
{
|
||||
// If we are not attaching, create a child template so we can
|
||||
// quickly launch our child when the time comes.
|
||||
|
||||
// By default, we will be sending a child order with the same side,
|
||||
// security, trader, account, etc, as this algo order, so just
|
||||
// copy our definition to the child definition to start with.
|
||||
//__OrderSetups__
|
||||
auto&& security_id = sparkalgo_resolve_security_id() ;
|
||||
|
||||
auto&& join_b31 = sparkalgo_generate_local_orderid ( security_id) ;
|
||||
auto&& user = get_user();
|
||||
auto&& account = get_account();
|
||||
auto&& side = get_side();
|
||||
me->join_c31_order_id = sparkalgo_setup_order ( join_b31, security_id, user, account, side);
|
||||
auto&& security_desc = std::string("SIM:CLZ9") ;
|
||||
auto&& symbol = std::string("CL") ;
|
||||
auto&& instrument_type = std::string("Future") ;
|
||||
auto&& exchange = std::string("SIM") ;
|
||||
auto&& join_b5 = sparkalgo_resolve_security_id ( security_desc, symbol, instrument_type, exchange) ;
|
||||
auto&& join_b32 = sparkalgo_generate_local_orderid ( join_b5) ;
|
||||
me->join_c32_order_id = sparkalgo_setup_order ( join_b32, join_b5, user, account, side);
|
||||
auto&& join_b33 = sparkalgo_generate_local_orderid ( join_b5) ;
|
||||
me->join_c33_order_id = sparkalgo_setup_order ( join_b33, join_b5, user, account, sparkalgo_other_side ( side));;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are attaching to an existing order then we do not need
|
||||
// to spawn a new child, so there is no need to setup a child
|
||||
// template
|
||||
}
|
||||
|
||||
// All is good, return a default constructed OrderOpResult which
|
||||
// indicates success.
|
||||
return {};
|
||||
}
|
||||
|
||||
// Called in the latency sensitive path, once, when the algo is first
|
||||
// launched.
|
||||
void algo_example_xlsm::do_launch(IAlgoServices& services)
|
||||
{
|
||||
// Cache our algo services
|
||||
me->algo_services = &services;
|
||||
|
||||
LOG(OrderMan, Diag, "Launching algo_example_xlsm k: {} ",
|
||||
algo_services().order_key().value());
|
||||
|
||||
// Subscribe to market data
|
||||
if(__NeedsBook__)
|
||||
services.subscribe_book( sparkalgo_resolve_security_id() );
|
||||
if(__NeedsTrades__)
|
||||
services.subscribe_ticker( sparkalgo_resolve_security_id() );
|
||||
|
||||
do_functions();
|
||||
}
|
||||
|
||||
OrderKey algo_example_xlsm::cache_attach(OrderKey attached_order, const ChildState& st)
|
||||
{
|
||||
OrderKey k = sparkalgo_generate_local_orderid();
|
||||
me->local_order_id_to_child[k] = OrderDef{nullptr, attached_order, st.price, st.quantity};
|
||||
return k;
|
||||
}
|
||||
|
||||
// Called in the latency sensitive path, once, when the algo is first
|
||||
// launched. In this case the algo is provided with an existing order
|
||||
// it is now responsible for managing.
|
||||
void algo_example_xlsm::do_launch_attached(IAlgoServices& services, OrderKey attached_order, ChildState st)
|
||||
{
|
||||
// The order was launched and attached to an existing order, with
|
||||
// key 'attached_order' and current state 'st'
|
||||
|
||||
// Cache our algo services
|
||||
me->algo_services = &services;
|
||||
|
||||
LOG(OrderMan, Diag,
|
||||
"Launching algo_example_xlsm k: {} Attached to existing order: {}",
|
||||
algo_services().order_key().value(), attached_order.value());
|
||||
|
||||
// Cache the key of the attached order we will be managing
|
||||
if(me->local_order_id_to_child.size() > 0)
|
||||
algo_services().terminate_error("Can't attach this ExcelAlgoTemplate to order because it contains more than one function");
|
||||
|
||||
//__OrderAttaches__
|
||||
me->join_c31_order_id = cache_attach(attached_order, st);
|
||||
me->join_c32_order_id = cache_attach(attached_order, st);
|
||||
me->join_c31_order_id = cache_attach(attached_order, st);
|
||||
me->join_c33_order_id = cache_attach(attached_order, st);
|
||||
me->join_c32_order_id = cache_attach(attached_order, st);;
|
||||
|
||||
if(me->local_order_id_to_child.size() > 1)
|
||||
algo_services().terminate_error("Can't attach this ExcelAlgoTemplate to order because it contains more than one function");
|
||||
|
||||
// Subscribe to market data
|
||||
if(__NeedsBook__)
|
||||
services.subscribe_book( sparkalgo_resolve_security_id() );
|
||||
if(__NeedsTrades__)
|
||||
services.subscribe_ticker( sparkalgo_resolve_security_id() );
|
||||
|
||||
// Simulate a child change so we can update our own state to
|
||||
// reflect that of the child order we attached to. From this point
|
||||
// forward there is no distinction between an algo instance that
|
||||
// spawned a new child order and one that attached to an existing
|
||||
// order.
|
||||
on_child_change(attached_order, st);
|
||||
do_functions();
|
||||
}
|
||||
|
||||
// Called when a human (or a supervising algo) updates a parameter for
|
||||
// this algo. The implementation validates the update and depending on
|
||||
// the validation outcome either applies the update or terminates the
|
||||
// algo.
|
||||
void algo_example_xlsm::do_update(const api::orderman::Update& update)
|
||||
{
|
||||
LOG(OrderMan, Diag, "algo_example_xlsm k: {} receiving update: {}",
|
||||
algo_services().order_key().value(), utils::pb::to_string(update));
|
||||
|
||||
|
||||
// Validate update
|
||||
if (update.quantity() == 0)
|
||||
{
|
||||
// An invalid update has been detected so we terminate the
|
||||
// algo, signaling an error. Any child orders launched by
|
||||
// this algo will also be terminated.
|
||||
algo_services().terminate_error("Invalid update");
|
||||
return;
|
||||
}
|
||||
|
||||
// Updates have two parts, a standard part consisting of price and
|
||||
// size and present in all updates, and an optional part that is
|
||||
// specific to the algo in question. Below we check for the
|
||||
// existance of this optional part and validate if present.
|
||||
if (update.has_custom())
|
||||
{
|
||||
auto ¶ms = update.custom().dynamic().order_params();
|
||||
//__ParameterUpdates__
|
||||
|
||||
auto join_thresh_param = params.find("join_thresh");
|
||||
if ( join_thresh_param == params.end() )
|
||||
{
|
||||
algo_services().terminate_error("Can't find parameter join_thresh ");
|
||||
return;
|
||||
}
|
||||
me->join_thresh = join_thresh_param->second.as_double();
|
||||
;
|
||||
}
|
||||
|
||||
// Update is good, forward it to our child
|
||||
Update child_update;
|
||||
child_update.set_price(update.price());
|
||||
child_update.set_quantity(update.quantity());
|
||||
for(auto & i : me->local_order_id_to_child)
|
||||
algo_services().update_child(i.second.child_key, child_update);
|
||||
|
||||
do_functions();
|
||||
}
|
||||
|
||||
void algo_example_xlsm::on_ticker(SecurityKey, const TradeVector& t)
|
||||
{
|
||||
me->last_trades = t;
|
||||
LOG(OrderMan, Diag, "algo_example_xlsm order on ticker");
|
||||
do_functions();
|
||||
}
|
||||
|
||||
// Called when the book for the specified security has changed
|
||||
void algo_example_xlsm::on_book(SecurityKey, const Book& b)
|
||||
{
|
||||
me->last_book = b;
|
||||
|
||||
LOG(OrderMan, Diag, "algo_example_xlsm order {} on book. BID: {}@{} ASK: {}@{}",
|
||||
algo_services().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
|
||||
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
|
||||
b.side(api::SIDE_SELL)[0].price());
|
||||
|
||||
do_functions();
|
||||
}
|
||||
|
||||
void algo_example_xlsm::do_functions()
|
||||
{
|
||||
auto my_side = algo_services().definition().side();
|
||||
auto other_side = securities::other_side(my_side);
|
||||
auto &my_side_psz = me->last_book.side(my_side)[0];
|
||||
auto &other_side_psz = me->last_book.side(other_side)[0];
|
||||
|
||||
LOG(OrderMan, Diag,
|
||||
"algo_example_xlsm order {} do_functions called. Order: {}@{} Top of "
|
||||
"My side of book: {}@{} Other Side: {}@{}",
|
||||
algo_services().order_key().value(),
|
||||
algo_services().quantity().value(), algo_services().price().value(),
|
||||
my_side_psz.size(), my_side_psz.price(),
|
||||
other_side_psz.size(), other_side_psz.price()
|
||||
);
|
||||
//__FunctionCalls__
|
||||
join_k31();
|
||||
join_k32();
|
||||
join_k33();;
|
||||
}
|
||||
|
||||
ChildStateAccessor algo_example_xlsm::sparkalgo_child_state(OrderKey ok, bool ignored)
|
||||
{
|
||||
UNUSED(ignored);
|
||||
|
||||
auto it = me->child_state.find(ok);
|
||||
bts_assert_msg(it != me->child_state.end(), "can't find order state for key");
|
||||
ChildStateAccessor acc(it->second);
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Our child reported a change:
|
||||
void algo_example_xlsm::on_child_change(OrderKey key, const ChildState& st)
|
||||
{
|
||||
// Our fill quantity and price is always a reflection of our
|
||||
// child's state
|
||||
me->child_state[key] = st;
|
||||
|
||||
uint32_t fills = 0u; bool all_complete = true, all_cancelled = true;
|
||||
for(const auto& ch : me->child_state)
|
||||
{
|
||||
fills += ch.second.total_fills.value();
|
||||
all_complete = all_complete && (ch.second.status == ChildStatus::COMPLETED || ch.second.status == ChildStatus::CANCELED);
|
||||
all_cancelled = all_cancelled && (ch.second.status == ChildStatus::CANCELED);
|
||||
}
|
||||
algo_services().update_fill_state(Quantity(fills), st.last_price);
|
||||
|
||||
if(all_complete)
|
||||
algo_services().complete();
|
||||
else if(all_cancelled)
|
||||
algo_services().terminate_error("All children cancelled");
|
||||
else
|
||||
do_functions(); //potentially feedback state changes into the order
|
||||
}
|
||||
|
||||
// This is called by the spark daemon at startup. In this call we
|
||||
// describe our algo's parameters and register a factory function to
|
||||
// create an instance of the algo.
|
||||
extern "C" void init_spark_plugin(bts::SparkServices& serv)
|
||||
{
|
||||
using namespace bts;
|
||||
|
||||
// Associate the descriptor with a builder function and register
|
||||
// that with the Spark server.
|
||||
//__ParameterSetups__
|
||||
|
||||
DynamicOrderDescriptor algo_desc;
|
||||
algo_desc.set_dynamic_order_type("algo_example_xlsm");
|
||||
|
||||
|
||||
auto join_thresh_param = algo_desc.add_param_desc();
|
||||
join_thresh_param->set_param_name("join_thresh");
|
||||
join_thresh_param->mutable_param_type()->set_primitive(DYNAMIC_ORDER_PRIMITIVE_DOUBLE);
|
||||
|
||||
|
||||
serv.register_dynamic_algo_order(algo_desc, []() { return std::make_unique<algo_example_xlsm>(); });
|
||||
;
|
||||
}
|
||||
|
||||
//__FunctionImpls__
|
||||
|
||||
void algo_example_xlsm::join_k31()
|
||||
{
|
||||
auto&& join_c31 = me->join_c31_order_id ;
|
||||
auto&& security_id = sparkalgo_resolve_security_id() ;
|
||||
auto&& join_c17 = sparkalgo_md_book_id ( security_id)(9,1) ;
|
||||
|
||||
auto range_init = [](auto& initval, int nrows, int ncols)
|
||||
{
|
||||
Range< std::decay_t<decltype((initval))> > rng(nrows, ncols);
|
||||
rng(0,0) = initval;
|
||||
return rng;
|
||||
};
|
||||
|
||||
auto range_set = [](auto& range, auto& val, int r, int c)
|
||||
{
|
||||
range(r,c) = val;
|
||||
};
|
||||
auto&& Join_C17_C18 = range_init(join_c17, 2, 1);
|
||||
auto&& join_c18 = sparkalgo_md_book_id ( security_id)(10,1) ;
|
||||
range_set(Join_C17_C18, join_c18, 1, 0);
|
||||
|
||||
auto Join_C17_C18_avg = 0.0;
|
||||
for(auto& Join_C17_C18_i : Join_C17_C18 )
|
||||
Join_C17_C18_avg += Join_C17_C18_i;
|
||||
Join_C17_C18_avg /= Join_C17_C18.size();
|
||||
|
||||
auto&& join_f29 = Join_C17_C18_avg ;
|
||||
auto&& on_off = 1.0 + join_f29 ;
|
||||
auto&& join_b17 = sparkalgo_md_book_id ( security_id)(9,0) ;
|
||||
auto&& Join_B17_D18 = range_init(join_b17, 2, 3);
|
||||
range_set(Join_B17_D18, join_c17, 0, 1);
|
||||
auto&& join_d17 = sparkalgo_md_book_id ( security_id)(9,2) ;
|
||||
range_set(Join_B17_D18, join_d17, 0, 2);
|
||||
auto&& join_b18 = sparkalgo_md_book_id ( security_id)(10,0) ;
|
||||
range_set(Join_B17_D18, join_b18, 1, 0);
|
||||
range_set(Join_B17_D18, join_c18, 1, 1);
|
||||
auto&& join_d18 = sparkalgo_md_book_id ( security_id)(10,2) ;
|
||||
range_set(Join_B17_D18, join_d18, 1, 2);
|
||||
auto&& join_g18 = ( join_c17 > 0.0 ? (sparkalgo_take_snapshot ( std::string("book"), Join_B17_D18)(1,1)) : (0.0) ) ;
|
||||
auto&& join_thresh = me->join_thresh;
|
||||
auto&& join_i31 = sparkalgo_snapshot_switch ( std::string("should_join"), ( (join_c18 > join_g18 && join_b18 >= join_thresh && true) ? (1.0) : (0.0) ) ) ;
|
||||
auto&& init_price = get_price();
|
||||
auto&& join_g31 = ( join_i31 ? (sparkalgo_take_snapshot ( std::string("join_price"), join_c18)) : (init_price) ) ;
|
||||
auto&& quantity = get_quantity();
|
||||
auto&& join_h31 = quantity ;
|
||||
sparkalgo_send_or_update_order ( join_c31, on_off, join_g31, join_h31) ;
|
||||
}
|
||||
|
||||
void algo_example_xlsm::join_k32()
|
||||
{
|
||||
auto&& join_c32 = me->join_c32_order_id ;
|
||||
auto&& security_id = sparkalgo_resolve_security_id() ;
|
||||
auto&& join_c17 = sparkalgo_md_book_id ( security_id)(9,1) ;
|
||||
|
||||
auto range_init = [](auto& initval, int nrows, int ncols)
|
||||
{
|
||||
Range< std::decay_t<decltype((initval))> > rng(nrows, ncols);
|
||||
rng(0,0) = initval;
|
||||
return rng;
|
||||
};
|
||||
|
||||
auto range_set = [](auto& range, auto& val, int r, int c)
|
||||
{
|
||||
range(r,c) = val;
|
||||
};
|
||||
auto&& Join_C17_C18 = range_init(join_c17, 2, 1);
|
||||
auto&& join_c18 = sparkalgo_md_book_id ( security_id)(10,1) ;
|
||||
range_set(Join_C17_C18, join_c18, 1, 0);
|
||||
|
||||
auto Join_C17_C18_avg = 0.0;
|
||||
for(auto& Join_C17_C18_i : Join_C17_C18 )
|
||||
Join_C17_C18_avg += Join_C17_C18_i;
|
||||
Join_C17_C18_avg /= Join_C17_C18.size();
|
||||
|
||||
auto&& join_f29 = Join_C17_C18_avg ;
|
||||
auto&& on_off = 1.0 + join_f29 ;
|
||||
auto&& join_b17 = sparkalgo_md_book_id ( security_id)(9,0) ;
|
||||
auto&& Join_B17_D18 = range_init(join_b17, 2, 3);
|
||||
range_set(Join_B17_D18, join_c17, 0, 1);
|
||||
auto&& join_d17 = sparkalgo_md_book_id ( security_id)(9,2) ;
|
||||
range_set(Join_B17_D18, join_d17, 0, 2);
|
||||
auto&& join_b18 = sparkalgo_md_book_id ( security_id)(10,0) ;
|
||||
range_set(Join_B17_D18, join_b18, 1, 0);
|
||||
range_set(Join_B17_D18, join_c18, 1, 1);
|
||||
auto&& join_d18 = sparkalgo_md_book_id ( security_id)(10,2) ;
|
||||
range_set(Join_B17_D18, join_d18, 1, 2);
|
||||
auto&& join_g18 = ( join_c17 > 0.0 ? (sparkalgo_take_snapshot ( std::string("book"), Join_B17_D18)(1,1)) : (0.0) ) ;
|
||||
auto&& join_thresh = me->join_thresh;
|
||||
auto&& join_i31 = sparkalgo_snapshot_switch ( std::string("should_join"), ( (join_c18 > join_g18 && join_b18 >= join_thresh && true) ? (1.0) : (0.0) ) ) ;
|
||||
auto&& init_price = get_price();
|
||||
auto&& join_g31 = ( join_i31 ? (sparkalgo_take_snapshot ( std::string("join_price"), join_c18)) : (init_price) ) ;
|
||||
auto&& join_g32 = join_g31 - 0.03 ;
|
||||
auto&& quantity = get_quantity();
|
||||
auto&& join_h31 = quantity ;
|
||||
auto&& join_h32 = join_h31 + 2.0 ;
|
||||
sparkalgo_send_or_update_order ( join_c32, on_off, join_g32, join_h32) ;
|
||||
}
|
||||
|
||||
void algo_example_xlsm::join_k33()
|
||||
{
|
||||
auto&& join_c33 = me->join_c33_order_id ;
|
||||
auto&& join_c31 = me->join_c31_order_id ;
|
||||
auto&& join_p31 = sparkalgo_child_state ( join_c31, true)(1,2) ;
|
||||
|
||||
auto range_init = [](auto& initval, int nrows, int ncols)
|
||||
{
|
||||
Range< std::decay_t<decltype((initval))> > rng(nrows, ncols);
|
||||
rng(0,0) = initval;
|
||||
return rng;
|
||||
};
|
||||
|
||||
auto range_set = [](auto& range, auto& val, int r, int c)
|
||||
{
|
||||
range(r,c) = val;
|
||||
};
|
||||
auto&& Join_P31_P32 = range_init(join_p31, 2, 1);
|
||||
auto&& join_c32 = me->join_c32_order_id ;
|
||||
auto&& join_p32 = sparkalgo_child_state ( join_c32, false)(0,2) ;
|
||||
range_set(Join_P31_P32, join_p32, 1, 0);
|
||||
|
||||
auto Join_P31_P32_avg = 0.0;
|
||||
for(auto& Join_P31_P32_i : Join_P31_P32 )
|
||||
Join_P31_P32_avg += Join_P31_P32_i;
|
||||
|
||||
auto&& join_h33 = Join_P31_P32_avg ;
|
||||
auto&& join_f33 = ( join_h33 > 0.0 ? (true) : (false) ) ;
|
||||
auto&& security_id = sparkalgo_resolve_security_id() ;
|
||||
auto&& join_c18 = sparkalgo_md_book_id ( security_id)(10,1) ;
|
||||
auto&& join_c17 = sparkalgo_md_book_id ( security_id)(9,1) ;
|
||||
auto&& join_b17 = sparkalgo_md_book_id ( security_id)(9,0) ;
|
||||
auto&& Join_B17_D18 = range_init(join_b17, 2, 3);
|
||||
range_set(Join_B17_D18, join_c17, 0, 1);
|
||||
auto&& join_d17 = sparkalgo_md_book_id ( security_id)(9,2) ;
|
||||
range_set(Join_B17_D18, join_d17, 0, 2);
|
||||
auto&& join_b18 = sparkalgo_md_book_id ( security_id)(10,0) ;
|
||||
range_set(Join_B17_D18, join_b18, 1, 0);
|
||||
range_set(Join_B17_D18, join_c18, 1, 1);
|
||||
auto&& join_d18 = sparkalgo_md_book_id ( security_id)(10,2) ;
|
||||
range_set(Join_B17_D18, join_d18, 1, 2);
|
||||
auto&& join_g18 = ( join_c17 > 0.0 ? (sparkalgo_take_snapshot ( std::string("book"), Join_B17_D18)(1,1)) : (0.0) ) ;
|
||||
auto&& join_thresh = me->join_thresh;
|
||||
auto&& join_i31 = sparkalgo_snapshot_switch ( std::string("should_join"), ( (join_c18 > join_g18 && join_b18 >= join_thresh && true) ? (1.0) : (0.0) ) ) ;
|
||||
auto&& init_price = get_price();
|
||||
auto&& join_g31 = ( join_i31 ? (sparkalgo_take_snapshot ( std::string("join_price"), join_c18)) : (init_price) ) ;
|
||||
auto&& join_g32 = join_g31 - 0.03 ;
|
||||
auto&& hedge_ticks = 0.1 ;
|
||||
auto&& join_g33 = join_g32 + hedge_ticks ;
|
||||
sparkalgo_send_or_update_order ( join_c33, join_f33, join_g33, join_h33) ;
|
||||
}
|
||||
|
||||
|
||||
} // namespace order_example
|
||||
} // namespace bts
|
||||
|
||||
Reference in new issue
Block a user