Compare commits

..
29 Commits
Author SHA1 Message Date
Kevin Darby c8badf5689 Merged in fix_span_error_message (pull request #306)
guard against UI setting cash and leverage factors for accounts I don't know about when span is off

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-17 14:51:23 +00:00
Kevin Darby 58533bf16f guard against UI setting cash and leverage factors for accounts I don't know about when span is off 2019-07-17 10:48:16 -04:00
Pedro Pinto 6b3c3ded68 Fixed up release notes. 2019-07-17 10:24:51 -04:00
Pedro Pinto d65e00fade Merged in SP-132-make-algo-shutdown-orderly (pull request #305)
RTM. SP-132 make algo shutdown orderly
2019-07-17 14:23:09 +00:00
Pedro Pinto 68e5d07aef Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-07-17 09:36:15 -04:00
Pedro Pinto 68345a181e Updated release notes. 2019-07-17 09:35:43 -04:00
Kevin Darby 69c121a19c Merged in fix_span (pull request #301)
fix crash where account name > 12 chars, tested with long account name

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-17 13:32:33 +00:00
Pedro Pinto 780ce7416f Merge branch 'master' into SP-132-make-algo-shutdown-orderly 2019-07-17 09:07:54 -04:00
Pedro Pinto 9fe338a62a Updated release notes. 2019-07-17 09:07:24 -04:00
Jake Norbut f027af66c8 Merged in ob_price_filter_tick_comparison (pull request #304)
RTM - Ob price filter tick comparison

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-07-16 21:43:23 +00:00
Jake Norbut 6b2708b160 PR suggestions 2019-07-16 16:34:59 -05:00
Jake Norbut 534474621f price filter should use new add_price tick-based method 2019-07-16 15:51:27 -05:00
Jake Norbut 554614ab72 add means to perform price comparsion based on tick equality 2019-07-16 15:50:44 -05:00
Pedro Pinto 2ede69a263 Updated release notes. 2019-07-16 16:21:31 -04:00
Pedro Pinto 9def94aceb Changed IB to cancel rather than error if it sees a child cancel.
This allows IB to remain agnostic of wheter the child cancel was a
simply a consequence of the IB itself shutting down, in which case it
would be ignored. If the IB instead errorred out than that error
superseeds the cancel and results in the IB reporting an error.
2019-07-16 15:55:28 -04:00
Pedro Pinto 0af30b3cc0 Changed cover to handle fills while canceling. 2019-07-16 15:32:38 -04:00
Taha Afzal a91c2d9bc6 SP-146 (Limiter will not accept unresolved fills) 2019-07-16 11:20:15 -04:00
Pedro Pinto 481c1fd2de Fixed gcc build. 2019-07-16 10:29:59 -04:00
Pedro Pinto e80fc807e0 Moved all reporting state calls to top level event handler.
Any CPU the algo order receives must originate from a top level event,
its simpler to consolidate all report guards in those events, instead
of chasing every call that comes out of the algo.
2019-07-16 10:20:30 -04:00
Pedro Pinto c8e054d079 Added algo wrapper documentation. 2019-07-16 10:10:46 -04:00
Pedro Pinto 9480e76d4b Cleaned up algo wrapper 2019-07-16 10:10:11 -04:00
Pedro Pinto 538411353a Improved cover to hedge while shutting down. 2019-07-16 08:48:36 -04:00
Pedro Pinto da6998c391 Do not error out when receiving an udpate from an unknown child.
Because the child is presumably a detached child.
2019-07-15 16:43:39 -04:00
Pedro Pinto 60786b105c Handled terminate_error while shutting down. 2019-07-15 16:26:22 -04:00
Pedro Pinto f488248abf Fixed handling of child update while shutdown. 2019-07-15 16:03:36 -04:00
Pedro Pinto 830b590e5b Algos now go through an orderly shutdown phase.
During which they can still process child updates.
2019-07-15 10:59:13 -04:00
Kevin Darby c9c93b3cf0 Merged in fix_span1 (pull request #300)
fix crash when acct names are > 12 chars

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-12 18:58:13 +00:00
Kevin Darby 9e5d43f44a fix crash where account name > 12 chars, tested with long account name 2019-07-11 17:53:48 -04:00
Kevin Darby 5c1965f534 fix crash when acct names are > 12 chars 2019-07-11 14:07:31 -04:00
49 changed files with 415 additions and 2764 deletions

No files matched your search

+1 -1
View File
@@ -104,7 +104,7 @@
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"args": [
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64.xll"
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
+19
View File
@@ -4,6 +4,25 @@ weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 2.7.0
### User Visible Changes
- Allowed algos to process "in-flight" fills while shutting down
- Changed the cover algo to continue to hedge while shutting down
### API Changes
- Changed the API so that algos do not fully terminate until all
their children are terminated. Algos that are in the process of
shutting down in response to a complete or cancel call will still
receive child updates, which they can use to calculate and report
an accurate fill count (during this shutdown stage algos cannot
create, cancel or update children). A new IAlgoServices method,
is_shutting_down(), can be used to determine if an algo is
shutting down.
## 2.6.2
### User Visible Changes
- Fixed an issue in the ladder where in some cases the mini order book could not be brought up from a badge
- Fixed a rare race condition that could lead to erroneous limit enforcement
- Fixed an issue with the margin limits where an error could occur if an account name with more than 12 chars was used
## 2.6.1
### User Visible Changes
@@ -144,7 +144,7 @@ namespace bts.shell.orderbook {
pb.add_discrete_values(fs.symbols, o => o.security?.Symbol);
// numeric
pb.add_numeric(fs.qty, o => o.order.quantity());
pb.add_numeric(fs.price, o => o.order.price());
pb.add_price(fs.price, o => o.order.price(), o => o.security);
pb.add_numeric(fs.qty_filled, o => o.order.filled());
pb.add_numeric(fs.avg_price, o => o.order.avg_price());
pb.add_numeric(fs.last_price, o => o.order.last_price());
@@ -37,6 +37,51 @@ namespace mbts.spark.filters {
_predicates.Add(impl);
bool impl(TRow o) => max.CompareTo(selector(o)) >= 0;
}
}
// this is a copy of add_numeric except we do a tick_equals comparsion
// if the ticks are not equals, then it falls back to a regular double comparison
//
// if the SecurityImmutable? doesn't have a value
// then we fallback to double comparison
public void add_price(DoubleMinMaxState value, Func<TRow, double> selector, Func<TRow, SecurityImmutable?> si_selector) {
if (value == null)
return;
if (value.minimum.active) {
var min = value.minimum.value.Value;
if (value.maximum.active) {
var max = value.maximum.value.Value;
_predicates.Add(impl);
bool impl(TRow o) {
var v = selector(o);
var si = si_selector(o);
if (si.HasValue) {
return si.Value.ContractSpec.compare_tick_fallback_to_double(min, v) <= 0 &&
si.Value.ContractSpec.compare_tick_fallback_to_double(max, v) >= 0;
}
return min.CompareTo(v) <= 0 && max.CompareTo(v) >= 0;
}
} else {
_predicates.Add(impl);
bool impl(TRow o) {
var si = si_selector(o);
if (si.HasValue) {
return si.Value.ContractSpec.compare_tick_fallback_to_double(min, selector(o)) <= 0;
}
return min.CompareTo(selector(o)) <= 0;
}
}
} else if (value.maximum.active) {
var max = value.maximum.value.Value;
_predicates.Add(impl);
bool impl(TRow o) {
var si = si_selector(o);
if (si.HasValue) {
return si.Value.ContractSpec.compare_tick_fallback_to_double(max, selector(o)) >= 0;
}
return max.CompareTo(selector(o)) >= 0;
}
}
}
public void add_regex(string str, Func<TRow, string> selector) {
@@ -128,6 +128,9 @@ namespace mbts.spark {
public static IEqualityComparer<double> tick_equality_comparer(this ContractSpecificationImmutable contract) =>
EqualityComparer.Create((double a, double b) => tick_equals(contract, a, b), x => MathUtils.RoundFractionalUnits(x, contract.LowTick));
public static int compare_tick_fallback_to_double(this ContractSpecificationImmutable contract, double x, double y) =>
tick_equals(contract, x, y) ? 0 : x.CompareTo(y);
public static double round_to_tick(this ContractSpecificationImmutable spec, double value) {
double intpart = (long)value;
double floatpart = value % 1;
@@ -3,5 +3,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("spark_core")]
[assembly: InternalsVisibleTo("sparkcmd")]
[assembly: InternalsVisibleTo("spark_test")]
[assembly: InternalsVisibleTo("spark_excel")]
+1 -1
View File
@@ -223,7 +223,7 @@ namespace bts.md
{
if (!timeout.HasValue)
{
timeout = TimeSpan.FromHours(24);
timeout = TimeSpan.FromMilliseconds(10000);
}
CancellationTokenSource src = new CancellationTokenSource(timeout.Value);
SecurityImmutable? security = await Task.Run<SecurityImmutable?>(async () =>
Binary file not shown.
-297
View File
@@ -1,297 +0,0 @@
using System;
using System.Diagnostics;
using System.Text;
using bts.excel.extensions;
namespace bts.excel
{
/// <summary>
/// Defines a reference to some Cell instance, whether it exists or not.
/// </summary>
/// <remarks>
/// Static methods are provided for conversion of column names etc.
/// </remarks>
public sealed class CellRef : IComparable, IComparable<CellRef>
{
public CellRef(ExcelDna.Integration.ExcelReference rref) : this(rref.GetSheetName(),
(rref.ColumnFirst + 1, rref.RowFirst + 1) )
{
}
/// <summary>
/// Constructor for the class: CellRef
/// </summary>
/// <param name="sheetName">Name of the sheet where the cell belongs.</param>
/// <param name="coordinate">Coordinate (column, row) for the cell in the sheet.</param>
public CellRef(string sheetName, (int col1, int row1) coordinate)
{
SheetName = sheetName;
SimpleSheetName = simple_sheet_name(SheetName);
Coordinate = coordinate;
LocalAddress = ConvertCoordToAddress(coordinate);
}
static string simple_sheet_name(string sn)
{
int open = sn.IndexOf('[');
int close = sn.IndexOf(']', open + 1);
var ssn = sn.Substring(0, open) + sn.Substring(close + 1);
ssn = ssn.Replace("[", "").Replace("]", "_").Replace(".", "_");
return ssn;
}
public string named_range { get; set;}
public bool in_vector {get; set;} = false;
public (int col, int row) vector_coordinates {get; set;}
/// <summary>
/// Maximum number of rows, to e compatible with Excel.
/// </summary>
private const int MaxRows = 1048576;
/// <summary>
/// Maximum number of columns, to be compatible with Excel.
/// </summary>
private const int MaxColumns = 16384;
// NOTE: Worksheet and workbook specifications and limits
// http://office.microsoft.com/en-gb/excel-help/excel-specifications-and-limits-HP010342495.aspx
//
// Worksheet size: 1,048,576 rows by 16,384 columns.
// * 1048575 is the highest number representable in 20 bits.
// * 16383 is the highest number representable in 14 bits. That leaves 2 spare bits in a 16-bit word.
// Column width: 255 characters (I'm not sure what the real significance of this is).
// Total number of characters that a cell can contain: 32,767 characters
// * 32766 is the highest number representable in 15 bits. That leaves 1 spare bit in a 16-bit word.
//
/// <summary>
/// Name of the sheet where the cell belongs.
/// </summary>
/// <remarks>
/// All cells belong to some Sheet instance, so this name must never be null or empty.
/// </remarks>
public string SheetName { get; }
public string SimpleSheetName { get; }
/// <summary>
/// Coordinate (column, row) for the cell in the sheet.
/// </summary>
public (int col1, int row1) Coordinate { get; }
public CellRef Offset(int columnOffset, int rowOffset)
{
return new CellRef(SheetName, (Column + columnOffset, Row + rowOffset));
}
public int Column => Coordinate.col1;
public int Row => Coordinate.row1;
public string LocalAddress { get; internal set; }
#region Address conversion
/// <summary>
/// Converts a column identifier, using letters, to a column number.
/// </summary>
/// <param name="columnName">Column identifier using letters.</param>
/// <returns>Column number.</returns>
/// <exception cref="ArgumentOutOfRangeException">When column number exceeds maximum.</exception>
public static int ConvertToColumnNumber(string columnName)
{
var number = 0;
var pow = 1;
for (var i = columnName.Length - 1; i >= 0; i--)
{
number += (columnName[i] - 'A' + 1) * pow;
pow *= 26;
}
if (number > MaxColumns)
{
throw new ArgumentOutOfRangeException(nameof(columnName),
"Column number from columnName exceeds maximum");
}
return number;
}
/// <summary>
/// Converts a column number to a column identifier using letters.
/// </summary>
/// <param name="columnNumber">Column number.</param>
/// <returns>Column identifier using letters.</returns>
/// <exception cref="ArgumentOutOfRangeException">When column number is invalid.</exception>
public static string ConvertToColumnName(int columnNumber)
{
if (columnNumber < 1 || columnNumber > MaxColumns)
{
throw new ArgumentOutOfRangeException(nameof(columnNumber),
"Invalid column number");
}
var dividend = columnNumber;
var columnName = string.Empty;
while (dividend > 0)
{
var modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo) + columnName;
dividend = (dividend - modulo) / 26;
}
return columnName;
}
/// <summary>
/// Converts an address, like "A1", to a cell coordinate.
/// </summary>
/// <param name="address">Cell address, like "A1".</param>
/// <returns>Coordinate of cell on sheet.</returns>
/// <exception cref="ArgumentNullException">When address argument is null.</exception>
/// <exception cref="ArgumentException">When address is invalid.</exception>
public static (int col1, int row1) ConvertAddressToCoord(string address)
{
if (address == null)
{
throw new ArgumentNullException(nameof(address));
}
address = address.Replace("$", string.Empty);
address = address.ToUpper();
var letters = new StringBuilder();
foreach (var c in address)
{
if (c < 'A' || c > 'Z')
{
break;
}
letters.Append(c);
}
if (letters.Length == 0 || letters.Length > 3)
{
throw new ArgumentException(
"Column name requires 1 to 3 letters",
nameof(address));
}
var numbers = address.Substring(letters.Length);
if (numbers.Length == 0)
{
throw new ArgumentException("Row number is required", nameof(address));
}
Debug.Assert(letters.Length + numbers.Length == address.Length);
var row1 = int.Parse(numbers);
var col1 = ConvertToColumnNumber(letters.ToString());
return (col1, row1);
}
/// <summary>
/// Convert cell coordinate to cell address, like "A1".
/// </summary>
/// <param name="coordinate">Coordinate of cell on sheet.</param>
/// <param name="absolute">Provide absolute address, like "$A$1".</param>
/// <returns>Cell address, like "A1", or optionally like "$A$1".</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static string ConvertCoordToAddress((int col1, int row1) coordinate, bool absolute = false)
{
if (coordinate.row1 < 1 || coordinate.row1 > MaxRows ||
coordinate.col1 < 1 || coordinate.col1 > MaxColumns)
{
throw new ArgumentOutOfRangeException(nameof(coordinate),
"Invalid coordinate");
}
var (col1, row1) = coordinate;
return absolute
? $"${ConvertToColumnName(col1)}${row1}"
: $"{ConvertToColumnName(col1)}{row1}";
}
#endregion
#region IComparable
public int CompareTo(CellRef other)
{
if (other == null) return -1;
return string.Compare(ToString(), other.ToString(),
StringComparison.Ordinal);
}
public int CompareTo(object obj)
{
if (obj.GetType() != GetType()) return -1;
return CompareTo((CellRef)obj);
}
#endregion
#region HashCode & Equals
/// <returns>Unique key</returns>
public override string ToString()
{
return to_name();
}
public string to_address()
{
if (!string.IsNullOrEmpty(SheetName))
return $"'{SheetName}'!{LocalAddress}";
else
return $"{LocalAddress}";
}
public string to_name()
{
if(!string.IsNullOrEmpty(named_range))
return named_range;
else
{
if (!string.IsNullOrEmpty(SheetName))
{
return $"{SimpleSheetName}_{LocalAddress}".ToLowerInvariant();
}
else
return $"{LocalAddress}".ToLowerInvariant();
}
}
public string r1_c1_include_sheet()
{
if (!string.IsNullOrEmpty(SheetName))
return $"'{SimpleSheetName}'!R{this.Row}C{this.Column}";
else
return $"R{this.Row}C{this.Column}";
}
public string r1_c1()
{
return $"R{this.Row}C{this.Column}";
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj != null && !(obj is CellRef))
throw new ArgumentException("Object must be of type CellRef.");
return GetHashCode().Equals(obj.GetHashCode());
}
#endregion
}
}
@@ -1,104 +0,0 @@
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);
}
}
}
@@ -1,26 +0,0 @@
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);
}
}
}
@@ -1,82 +0,0 @@
using System;
using System.Collections.Concurrent;
using ExcelDna.Integration;
using spark_excel.grammar;
using Antlr4.Runtime;
namespace bts.excel
{
internal class ExcelFormulaErrorListener : BaseErrorListener
{
public bool IsValid { get; private set; } = true;
public string offendingSymbol { get; private set;}
public int line { get; private set; }
public int charPositionInLine { get; private set; }
public string msg { get; private set; }
public RecognitionException exception { get; private set;}
public override void SyntaxError(IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
{
IsValid = false;
this.offendingSymbol = offendingSymbol.Text;
this.line = line;
this.charPositionInLine = charPositionInLine;
this.msg = msg;
this.exception = e;
}
}
//https://theantlrguy.atlassian.net/wiki/spaces/ST/pages/1409118/Language+Translation+Using+ANTLR+and+StringTemplate
//https://stackoverflow.com/questions/31511610/antlr-source-to-target-language-conversion
public class Generate
{
[ExcelFunction(IsMacroType = true)]
static public string bts_generate_code([ExcelArgument(AllowReference=true)] object sink, string function_name = null)
{
ExcelReference rref = (ExcelReference)sink;
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
var antlrstream = new AntlrInputStream(formula.ToString());
var lexer = new ExcelFormulaLexer(antlrstream);
var tokens = new CommonTokenStream(lexer);
var parser = new ExcelFormulaParser(tokens);
var error_listener = new ExcelFormulaErrorListener();
//parser.RemoveErrorListeners();
parser.AddErrorListener(error_listener);
var parsetree = parser.main();
if (!error_listener.IsValid)
{
write_file(function_name, error_listener.offendingSymbol + error_listener.msg + error_listener.exception?.ToString());
throw new Exception(error_listener.offendingSymbol + error_listener.msg, error_listener.exception);
}
//AbstractParseTreeVisitor
GeneratorVisitor visitor = new GeneratorVisitor(rref);
object val = visitor.VisitMain(parsetree);
if(string.IsNullOrEmpty(function_name))
function_name = new CellRef(rref).to_name();
string ret = $"auto {function_name}() {Environment.NewLine}{{{Environment.NewLine}";
visitor.get_defs().ForEach(x => ret += " " + x + Environment.NewLine);
ret += " return " + val.ToString() + " ;";
ret += $"{Environment.NewLine} }}";
write_file(function_name, ret);
return ret;
}
static void write_file(string function_name, string text)
{
using(System.IO.StreamWriter file =
new System.IO.StreamWriter($"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\{function_name}.function"))
{
file.WriteLine(text);
}
}
}
}
@@ -1,410 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using spark_excel.grammar;
using Antlr4.Runtime;
using ExcelDna.Integration;
using bts.excel.extensions;
namespace bts.excel
{
public interface IDefinitionVisitor
{
void add_def(string def);
List<string> get_defs();
}
public class GeneratorVisitor : ExcelFormulaBaseVisitor<object>, IDefinitionVisitor
{
ExcelReference _ref;
functions.lib.FunctionLibrary _library = new functions.lib.FunctionLibrary();
ExcelFormulaErrorListener _error_listener = new ExcelFormulaErrorListener();
List<string> _defs = new List<string>();
Dictionary<string, string> _def_map = new Dictionary<string, string>();
public GeneratorVisitor(ExcelReference rref)
{
_ref = rref;
}
public void add_def(string def)
{
_defs.Add(def);
}
public void map_def(string def, string mapped)
{
_def_map[def] = mapped;
}
public List<string> get_defs()
{
var list = _defs.Distinct();
List<string> defs = new List<string>();
foreach(var d in list)
defs.Add( _def_map.TryGetValue(d, out string mapped) ? mapped : d );
return defs;
}
#region Context visitors overrides
static ConcurrentDictionary<string, string> _algo_params = new ConcurrentDictionary<string, string>();
public static void register_algo_parameter(string sheet, string name)
{
_algo_params[name] = sheet;
}
public override object VisitName(ExcelFormulaParser.NameContext context)
{
var name = context.GetText();
var lookup = name;
if(!lookup.Contains("!"))
lookup = "!" + lookup;
object value = XlCall.Excel(XlCall.xlfGetName, lookup);
if(value != null && !(value is ExcelError))
{
string[] par = value.ToString().Split(new[] {'=', 'R', 'C'}, StringSplitOptions.RemoveEmptyEntries);
int r = int.Parse(par[0]) - 1;
int c = int.Parse(par[1]) - 1;
var rref = new ExcelReference(r, r, c, c, _ref.GetSheetName());
process_cell(rref);
}
return name;
}
private object process_cell(ExcelReference rref)
{
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
var formstring = formula.ToString();
if(string.IsNullOrEmpty(formstring))
formstring = "0.0";
if (!formstring.StartsWith("="))
formstring = "=" + formstring;
var cellref = new CellRef(rref);
string r1c1 = cellref.r1_c1();
var cellname = XlCall.Excel(XlCall.xlfGetDef, r1c1, cellref.SheetName);
bool named = false;
if (!(cellname is ExcelError))
{
named = true;
cellref.named_range = cellname.ToString();
}
var antlrstream = new AntlrInputStream(formstring);
var lexer = new ExcelFormulaLexer(antlrstream);
var tokens = new CommonTokenStream(lexer);
var parser = new ExcelFormulaParser(tokens);
parser.AddErrorListener(_error_listener);
var parsetree = parser.main();
if (!_error_listener.IsValid)
{
//throw new ExcelFormulaException(_error_listener.ErrorLocation, _error_listener.ErrorMessage);
var def = $"{cellref.ToString()} = Error parsing cell {_error_listener.offendingSymbol } {_error_listener.exception?.ToString()} ;";
add_def(def);
}
else
{
var def = $"auto&& {cellref.ToString()} = {this.VisitMain(parsetree)} ;";
add_def(def);
if (named && _algo_params.ContainsKey(cellref.named_range))
{
map_def(def, $"auto&& {cellref.named_range} = spark_algo_parameter(\"{cellref.named_range}\");");
}
}
return cellref;
}
public override object VisitMain(ExcelFormulaParser.MainContext context)
{
return context.expr().Accept(this);
}
public override object VisitMulDiv(ExcelFormulaParser.MulDivContext context)
{
var expr = context.expr();
var arg1 = expr[0].Accept(this);
if (arg1 is ErrorValue)
return arg1;
var arg2 = expr[1].Accept(this);
if (arg2 is ErrorValue)
return arg2;
string code = $"{arg1} {context.op.Text} {arg2}";
return code;
}
public override object VisitAddSub(ExcelFormulaParser.AddSubContext context)
{
var expr = context.expr();
var arg1 = expr[0].Accept(this);
if (arg1 is ErrorValue)
return arg1;
var arg2 = expr[1].Accept(this);
if (arg2 is ErrorValue)
return arg2;
string code = $"{arg1} {context.op.Text} {arg2}";
return code;
}
public override object VisitPercentage(ExcelFormulaParser.PercentageContext context)
{
var expr = context.expr();
var arg1 = expr.Accept(this);
if (arg1 is ErrorValue)
return arg1;
return $"( {arg1} / 100.0 )";
}
public override object VisitRelational(ExcelFormulaParser.RelationalContext context)
{
var expr = context.expr();
var arg1 = expr[0].Accept(this);
if (arg1 is ErrorValue)
return arg1;
var arg2 = expr[1].Accept(this);
if (arg2 is ErrorValue)
return arg2;
return $"{arg1} {context.op.Text} {arg2}";
}
public override object VisitError(ExcelFormulaParser.ErrorContext context)
{
return new ErrorValue(context.GetText());
}
public override object VisitString(ExcelFormulaParser.StringContext context)
{
var str = context.GetText();
return $"std::string({str})";
}
public override object VisitUnary(ExcelFormulaParser.UnaryContext context)
{
var expr = context.expr();
if(context.sign.Text == "-")
return "-" + expr.Accept(this);
else
return expr.Accept(this);
}
public override object VisitFunction(ExcelFormulaParser.FunctionContext context)
{
var name = context.name.Text;
if (!_library.is_defined(name))
return ErrorValue.Name;
var expr = context.expr();
var args = new IExpression[expr.Length];
for (var i = 0; i < expr.Length; i++)
args[i] = new Expression<object>(expr[i], this);
return _library.call_function(name, args, this);
}
public override object VisitParenthesis(ExcelFormulaParser.ParenthesisContext context)
{
return context.expr().Accept(this);
}
public override object VisitNumber(ExcelFormulaParser.NumberContext context)
{
var num = context.GetText();
if(double.TryParse(num, out double res))
return res.ToString("0.0############");
return num;
}
public override object VisitConcatenate(ExcelFormulaParser.ConcatenateContext context)
{
var expr = context.expr();
var arg1 = "std::to_string(" + expr[0].Accept(this) + ")";
var arg2 = "std::to_string(" + expr[1].Accept(this) + ")";
return $"{arg1}+{arg2}";
}
public override object VisitRange(ExcelFormulaParser.RangeContext context)
{
var rng = context.rng();
return rng.Accept(this);
}
public override object VisitRangeList(ExcelFormulaParser.RangeListContext context)
{
var result = new List<RangeRef>();
foreach (var range in context.rng())
{
var obj = range.Accept(this);
switch (obj)
{
case ErrorValue err:
result.Add(null);
break;
case RangeRef rangeRef:
result.Add(rangeRef);
break;
case null:
result.Add(null);
break;
default:
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
}
}
return result;
}
public override object VisitRangeRef(ExcelFormulaParser.RangeRefContext context)
{
var obj = context.@ref().Accept(this);
switch (obj)
{
case ErrorValue _:
return obj;
case CellRef start:
var (column, row) = CellRef.ConvertAddressToCoord(context.end.Text);
var rng = new RangeRef(start, column, row);
if(rng.IsOneCell())
{
var rref = new ExcelReference(row - 1, row - 1, column - 1, column - 1, start.SheetName);
process_cell(rref);
}
else
{
var def =
$@"std::vector<auto> {rng.to_name()};
auto {rng.to_name()}_load = [=](auto&& val)
{{
}};
";
var rref = new ExcelReference(rng.Start.Row - 1, rng.End.Row - 1,
rng.Start.Column- 1, rng.End.Column - 1, start.SheetName);
//process_cell(rref);
}
return rng;
case null:
return null;
default:
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
}
}
public override object VisitCell(ExcelFormulaParser.CellContext context)
{
return context.@ref().Accept(this);
}
public override object VisitCellList(ExcelFormulaParser.CellListContext context)
{
var result = new List<CellRef>();
foreach (var @ref in context.@ref())
{
var obj = @ref.Accept(this);
switch (obj)
{
case ErrorValue _:
// TODO: Warn that error has been replaced with null here?
result.Add(null);
break;
case CellRef cellRef:
result.Add(cellRef);
break;
case null:
result.Add(null);
break;
default:
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
}
}
return result;
}
public override object VisitCellRef(ExcelFormulaParser.CellRefContext context)
{
var colRow = context.colRow.Text;
var (col1, row1) = CellRef.ConvertAddressToCoord(colRow);
var sheetname = context.sheet?.Text ?? _ref.GetSheetName();
var rref = new ExcelReference(row1 - 1, row1 - 1, col1 - 1, col1 - 1, sheetname);
return process_cell(rref);
}
// public override object VisitRangeRef(ExcelFormulaParser.RangeRefContext context)
// {
// var obj = context.@ref().Accept(this);
// var end = context.COL_ROW();
//return obj;
// var result = new List<CellRef>();
// var rng = context.GetRuleContexts<spark_excel.grammar.ExcelFormulaParser.RngContext>();
// foreach(var r in rng)
// {
// var obj = r.Accept(this);
// switch (obj)
// {
// case ErrorValue err:
// result.Add(null);
// break;
// case CellRef rangeRef:
// result.Add(rangeRef);
// break;
// case null:
// result.Add(null);
// break;
// default:
// throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
// }
// }
// return result;
// var colRow = context.COL_ROW();
// var colrowstring = colRow.ToString();
// var (col1, row1) = CellRef.ConvertAddressToCoord(colrowstring);
// var sheetname = !colrowstring.Contains("!") ? _simple_sheetname : _simple_sheetname;
// var rref = new ExcelReference(row1 - 1, row1 - 1, col1 - 1, col1 - 1, sheetname);
// return process_cell(rref, sheetname);
// switch (obj)
// {
// case ErrorValue _:
// return obj;
// case CellRef start:
// var (column, row) = CellRef.ConvertAddressToCoord(context.end.Text);
// return new RangeRef(start, column, row);
// case null:
// return null;
// default:
// throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
// }
// }
#endregion
}
}
-149
View File
@@ -1,149 +0,0 @@
using System.Collections.Generic;
using System.Diagnostics;
namespace bts.excel
{
public class RangeRef
{
public RangeRef(CellRef start, CellRef end)
{
Start = start;
End = end;
}
public string named_range { get; set;}
public RangeRef(CellRef start, int endColumn, int endRow)
: this(start, new CellRef(start.SheetName, (endColumn, endRow)))
{ }
public RangeRef Offset(int columnOffset, int rowOffset)
{
var start = Start.Offset(columnOffset, rowOffset);
var end = End.Offset(columnOffset, rowOffset);
return new RangeRef(start, end);
}
public CellRef Start { get; }
public CellRef End { get; }
public override string ToString()
{
return to_name();
}
public string SheetName => Start.SheetName;
public string SimpleSheetName => Start.SimpleSheetName;
public string to_address()
{
return string.Format("'{0}'!{1}:{2}",
SheetName,
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
}
public string to_name()
{
if (!string.IsNullOrEmpty(named_range))
return named_range;
else
{
return string.Format("{0}_{1}_{2}",
Start.SimpleSheetName,
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
}
}
public bool IsVector()
{
return IsSingleRow() || IsSingleColumn();
}
public bool IsOneCell()
{
return IsSingleRow() && IsSingleColumn();
}
internal List<CellRef> Get2DVector()
{
var result = new List<CellRef>();
var sheetName = Start.SheetName;
var numRows = NumRows();
var lastRow = Start.Row + numRows - 1;
for (var row = Start.Row; row <= lastRow; row++)
{
var numCols = NumCols();
var lastCol = Start.Column + numCols - 1;
for (var col = Start.Column; col <= lastCol; col++)
{
result.Add(new CellRef(sheetName, (col, row)));
}
}
return result;
}
internal List<CellRef> GetVector()
{
Debug.Assert(IsVector());
var result = new List<CellRef>();
if (IsSingleColumn())
{
var col = Start.Column;
var sheetName = Start.SheetName;
var numRows = NumRows();
var lastRow = Start.Row + numRows - 1;
for (var row = Start.Row; row <= lastRow; row++)
{
result.Add(new CellRef(sheetName, (col, row)));
}
}
else
{
Debug.Assert(IsSingleRow());
var row = Start.Row;
var sheetName = Start.SheetName;
var numCols = NumCols();
var lastCol = Start.Column + numCols - 1;
for (var col = Start.Column; col <= lastCol; col++)
{
result.Add(new CellRef(sheetName, (col, row)));
}
}
return result;
}
public bool IsSingleColumn()
{
Debug.Assert(Start.SheetName == End.SheetName);
return Start.Column == End.Column;
}
public bool IsSingleRow()
{
Debug.Assert(Start.SheetName == End.SheetName);
return Start.Row == End.Row;
}
public int NumRows()
{
Debug.Assert(Start.SheetName == End.SheetName);
return End.Row - Start.Row + 1;
}
public int NumCols()
{
Debug.Assert(Start.SheetName == End.SheetName);
return End.Column - Start.Column + 1;
}
}
}
@@ -1,75 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using bts.excel.functions;
using ExcelDna.Registration;
namespace bts.excel.functions.lib
{
public interface IFunction
{
object function(IExpression[] args, IDefinitionVisitor dv);
}
public class FunctionLibrary
{
private readonly Dictionary<string, IFunction> _functions =
new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Delegate> _dnafunctions =
new Dictionary<string, Delegate>(StringComparer.OrdinalIgnoreCase);
public FunctionLibrary()
{
add_functions(Logical.functions);
add_functions(Mathematics.functions);
add_functions(Stat.functions);
var dna = ExcelRegistration.GetExcelFunctions();
foreach(var xl in dna)
{
_dnafunctions[xl.FunctionAttribute.Name] =
xl.FunctionLambda.Compile();
}
}
public bool is_defined(string name) => _functions.ContainsKey(name) || _dnafunctions.ContainsKey(name);
public void add_function(string name, IFunction function)
{
_functions.Add(name, function);
}
public void add_functions(Dictionary<string, IFunction> delegates)
{
foreach (var f in delegates)
_functions.Add(f.Key, f.Value);
}
public object call_function(string name, IExpression[] args, IDefinitionVisitor dv)
{
if(_functions.TryGetValue(name, out IFunction func))
return func.function(args, dv);
else
return call_dna_function(name, args, dv);
}
object call_dna_function(string name, IExpression[] args, IDefinitionVisitor dv)
{
if(_dnafunctions.TryGetValue(name, out Delegate func))
{
object[] param = args.Select(x => x.evaluate()).ToArray();
// var pp = func.Method.GetParameters();
// if(pp.Length -1 != param.Length)
// throw new ArgumentException("invalide args to dna function " + name);
string code = $"{name} ( "; int i = 0;
foreach(var p in param)
code += p.ToString() + (i++ < param.Length-1 ? ", " : "");
code += ")";
return code;
}
else
return ErrorValue.Name;
}
}
}
@@ -1,55 +0,0 @@
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()},
};
}
}
}
@@ -1,32 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class And : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: AND(logical1, [logical2], ...)
// https://support.office.com/en-gb/article/AND-function-5f19b2e8-e1df-4408-897a-ce285a19e9d9
if (args.Length < 2)
{
throw new ArgumentException(
$"AND function requires at least 2 arguments, got {args.Length}",
nameof(args));
}
string code = "(";
foreach (var fx in args)
{
var arg = fx.evaluate();
if (arg is ErrorValue)
return arg;
code += arg + " && ";
}
code += "true)";
return code;
}
}
}
@@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Average : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: Average(number1,[number2],...)
// https://support.office.com/en-GB/article/AVERAGE-function-047bac88-d466-426c-a32b-8f33eb960cf6
if (args.Length < 1)
{
throw new ArgumentException(
$"Average function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string name = "avg_" + new Random().Next(0, int.MaxValue);
string def = ($"auto {name} = 0.0;{Environment.NewLine}");
int count = 0;
foreach (var arg in args)
{
var argv = arg.evaluate();
if (arg is ErrorValue)
return arg;
count++;
def += $" {name} += {argv};{Environment.NewLine}";
}
if(count > 1)
def += $" {name} /= {count}";
dv.add_def(def);
return name;
}
}
}
@@ -1,35 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class If : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: IF(logical_test, value_if_true, [value_if_false])
// https://support.office.com/en-gb/article/IF-function-69aed7c9-4e8a-4755-a9bc-aa8bbff73be2
if (args.Length != 2 && args.Length != 3)
{
throw new ArgumentException(
"IF function takes 2 or 3 arguments",
nameof(args));
}
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
string code = string.Empty;
if (args.Length == 3)
{
code += $"( {arg} ? ({ args[1].evaluate() }) : ({args[2].evaluate()}) ) ";
}
else
{
code += $"( {arg} ? ({ args[1].evaluate() }) : false) ";
}
return code;
}
}
}
@@ -1,32 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class IfError : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: IFERROR(value, value_if_error)
// https://support.office.com/en-gb/article/IFERROR-function-c526fd07-caeb-47b8-8bb6-63f3e417f611
if (args.Length != 2)
{
throw new ArgumentException(
"IFERROR function takes exactly 2 arguments",
nameof(args));
}
try
{
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
return arg;
}
catch (Exception)
{
return args[1].evaluate();
}
}
}
}
@@ -1,26 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class Ln : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: LN(number)
// https://support.office.com/en-gb/article/LN-function-81fe1ed7-dac9-4acd-ba1d-07a142c6118f
if (args.Length != 1)
{
throw new ArgumentException(
$"LN function takes only 1 argument, got {args.Length}");
}
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
string code = $"log({arg})";
return code;
}
}
}
@@ -1,40 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class Log : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: LOG(number, [base])
// https://support.office.com/en-gb/article/LOG-function-4e82f196-1ca9-4747-8fb0-6c4a3abb3280
switch (args.Length)
{
case 1:
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
string code = $"log10({arg})";
return code;
case 2:
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
var arg2 = args[1].evaluate();
if (arg2 is ErrorValue)
return arg2;
string code2 = $"log({arg1}, {arg2})";
return code2;
default:
throw new ArgumentException(
$"LOG function takes only 1 or 2 arguments, got {args.Length}",
nameof(args));
}
}
}
}
@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Max : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: MAX(number1, [number2], ...)
// https://support.office.com/en-GB/article/MAX-function-e0012414-9ac8-4b34-9a47-73e662c08098
if (args.Length < 1)
{
throw new ArgumentException(
$"MAX function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string code = $"std::max(\n";
foreach (var arg in args)
{
var argv = arg.evaluate();
if (argv is ErrorValue)
return argv;
code += $"{argv},";
}
code += $"{ args[args.Length-1].evaluate()} );\n";
return code;
}
}
}
@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Min : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: MIN(number1, [number2], ...)
// https://support.office.com/en-GB/article/MIN-function-61635d12-920f-4ce2-a70f-96f202dcc152
if (args.Length < 1)
{
throw new ArgumentException(
$"MIN function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string code = $"std::min(\n";
foreach (var arg in args)
{
var argv = arg.evaluate();
if (argv is ErrorValue)
return argv;
code += $"{argv},";
}
code += $"{ args[args.Length - 1].evaluate()} );\n";
return code;
}
}
}
@@ -1,25 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class Or : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: OR(logical1, [logical2], ...)
// https://support.office.com/en-gb/article/OR-function-7d17ad14-8700-4281-b308-00b131e22af0
string code = "(";
foreach (var fx in args)
{
var arg = fx.evaluate();
if (arg is ErrorValue)
return arg;
code += arg + " || ";
}
code += "false)";
return code;
}
}
}
@@ -1,22 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class Pi : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: PI()
// https://support.office.com/en-gb/article/PI-function-264199d0-a3ba-46b8-975a-c4a04608989b
if (args.Length != 0)
{
throw new ArgumentException(
$"PI function takes no arguments, got {args.Length}",
nameof(args));
}
return Math.PI.ToString();
}
}
}
@@ -1,30 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class Power : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: POWER(number, power)
// https://support.office.com/en-gb/article/POWER-function-d3f2908b-56f4-4c3f-895a-07fb519c362a
if (args.Length != 2)
{
throw new ArgumentException(
$"POWER function takes 2 arguments, got {args.Length}",
nameof(args));
}
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
var arg2 = args[1].evaluate();
if (arg2 is ErrorValue)
return arg2;
return $"pow({arg1}, {arg2})";
}
}
}
@@ -1,25 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class Sin : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: SIN(number)
// https://support.office.com/en-gb/article/SIN-function-654975c2-05c4-4831-9a24-2c65e4040fdf
if (args.Length != 1)
{
throw new ArgumentException(
$"Sin function takes only 1 argument, got {args.Length}");
}
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
return $"sin({arg1})";
}
}
}
@@ -1,25 +0,0 @@
using System;
namespace bts.excel.functions.lib
{
public class Sqrt : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: SQRT(number)
// https://support.office.com/en-gb/article/SQRT-function-654975c2-05c4-4831-9a24-2c65e4040fdf
if (args.Length != 1)
{
throw new ArgumentException(
$"SQRT function takes only 1 argument, got {args.Length}");
}
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
return $"sqrt({arg1})";
}
}
}
@@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Sum : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv)
{
// Syntax: SUM(number1,[number2],...)
// https://support.office.com/en-GB/article/SUM-function-043e1c7d-7726-4e80-8f32-07b23e057f89
if (args.Length < 1)
{
throw new ArgumentException(
$"SUM function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string name = "sum_" + new Random().Next(0, int.MaxValue);
string def = ($"auto {name} = 0.0;{Environment.NewLine}");
foreach (var arg in args)
{
var argv = arg.evaluate();
if (arg is ErrorValue)
return arg;
def += $" {name} += {argv};{Environment.NewLine}";
}
dv.add_def(def);
return name;
}
}
}
@@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using ExcelDna.Integration;
namespace bts.excel.extensions
{
public static class ExcelDNAExtensions
{
public static string GetSheetName(this ExcelReference rref)
{
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, rref);
return sheetname;
}
}
}
@@ -11,7 +11,6 @@ using bts.excel;
using bts.excel.extensions;
using bts.excel.rtd;
using Newtonsoft.Json;
using bts.api;
namespace bts.api.orderman
@@ -21,14 +20,6 @@ namespace bts.api.orderman
readonly static Lazy<user.AuthService> _users = new Lazy<user.AuthService>(() => Locator.locate<user.AuthService>(), true);
readonly static Lazy<admin.AdminService> _admin = new Lazy<admin.AdminService>(() => Locator.locate<admin.AdminService>(), true);
struct OrderDef
{
public Create create;
public Response response;
}
static ConcurrentDictionary<long, OrderDef> _orders = new ConcurrentDictionary<long, OrderDef>();
[ExcelFunction(Category = "BTS", Description = "Get All Accessible Accounts")]
async public static void spark_get_accounts(string login_token, ExcelAsyncHandle handle)
{
@@ -51,66 +42,48 @@ namespace bts.api.orderman
}
}
static Response cancel_order(long order_id)
{
var resp = _service.Value.cancel_order(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
return resp;
}
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID", IsMacroType = true)]
public static object spark_orderservice_cancel_order(long order_id)
{
var resp = cancel_order(order_id);
var resp = _service.Value.cancel_order(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
return parse_response(resp);
}
static Response update_order(long order_id, double price, long quantity, CustomOrderDefinition cust = null)
{
var resp = _service.Value.update_order(new Update()
{
OrderKey = Convert.ToUInt32(order_id),
Price = price,
Quantity = Convert.ToUInt32(quantity) ,
Custom = cust
});
return resp;
}
[ExcelFunction(Category = "BTS", Description = "Update an Order by Order ID", IsMacroType = true)]
public static object spark_orderservice_update_order(long order_id, double price, long quantity)
{
var resp = update_order(order_id, price, quantity);
var resp = _service.Value.update_order(new Update() { OrderKey = Convert.ToUInt32(order_id), Price = price, Quantity = Convert.ToUInt32(quantity) });
return parse_response(resp);
}
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key", IsMacroType = true)]
public static object spark_orderservice_send_order_key(
[ExcelArgument("Required. Only send the order if > 1")] long on_off,
[ExcelArgument("Required. Local Order ID used to differentiate between create / update")] long local_order_id,
[ExcelArgument("Required. 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 = null,
[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. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
[ExcelArgument("OPTIONAL. (YES,NO) - Default YES")] string manual_order_indicator = null,
[ExcelArgument("OPTIONAL. (1,2,3,4) - Default 1")] string cti_code = null,
[ExcelArgument("OPTIONAL. (CUSTOMER,FIRM) - Default FIRM")] string customer_or_firm = null)
{
if(on_off <= 0)
return "this order is turned off";
if(_orders.ContainsKey(local_order_id))
return "use a new local order id per order";
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),
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);
@@ -125,14 +98,12 @@ namespace bts.api.orderman
return "Could not resolve account: " + account;
cr.NewOrder.Account = btaccount.Name;
cr.NewOrder.AccountId = btaccount.Id;
if(string.IsNullOrEmpty(order_type))
order_type = "Limit";
cr.NewOrder.CustomDef = custom_def(order_type, order_params);
if(cr.NewOrder.CustomDef == null || cr.NewOrder.CustomDef.CustomCase == CustomOrderDefinition.CustomOneofCase.None)
return "Could not parse order_type and/or order_params info";
var resp = _service.Value.create_order(cr);
_orders[local_order_id] = new OrderDef { create = cr, response = resp };
_local_order_id = local_order_id;
return parse_response(resp, true);
}
@@ -158,7 +129,7 @@ namespace bts.api.orderman
}
static CustomOrderDefinition custom_def(string order_type, string order_params)
{
{
var member = ReflectionExtensions.get_fields_or_properties(typeof(CustomOrderDefinition))
.Where(x => x.Name.ToLower() == order_type.ToLower()).FirstOrDefault();
if (member != null)
@@ -171,13 +142,13 @@ namespace bts.api.orderman
}
else
return null;
}
}
static long _local_order_id = 0;
[ExcelFunction(Category = "BTS", Description = "Send or Modify or Cancel a Spark Order ")]
public async static void spark_send_or_update_order(
[ExcelArgument("Required. Only send the order if > 1")] long on_off,
[ExcelArgument("Required. Local Order ID used to differentiate between create / update")] long local_order_id,
[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,
@@ -185,64 +156,29 @@ namespace bts.api.orderman
[ExcelArgument("Required. Account")] string account,
[ExcelArgument("Required. Price")] double price,
[ExcelArgument("Required. Quantity")] long quantity,
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type = null,
[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,
ExcelAsyncHandle handle = 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)
{
try
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))
{
Venue ven;
if (!Utilities.parse_venue(exchange, out ven))
handle.SetResult("Can't Parse Exchange: " + (exchange ?? string.Empty));
if (on_off <= 0)
handle.SetResult("this order is turned off");
var sec = await MDSubscribe.market_data(ven).find_security_async(x => x.security_desc() == security_desc);
if (sec == null)
{
handle.SetResult("Can't find security desc " + security_desc);
}
else
{
if (_orders.TryGetValue(local_order_id, out OrderDef def) && def.response.OrderKey > 0)
{
if(quantity > 0)
{
if (string.IsNullOrEmpty(order_type))
order_type = "Limit";
var cust = custom_def(order_type, order_params);
if (cust == null || cust.CustomCase == CustomOrderDefinition.CustomOneofCase.None)
handle.SetResult("Could not parse order_type and/or order_params info");
if(def.create.Price != price || def.create.Quantity != Convert.ToUInt32(quantity) ||
(cust != null && !cust.Equals(def.create.NewOrder.CustomDef)))
{
var resp = update_order(def.response.OrderKey, price, quantity, cust);
if(resp.ErrorCode == OrderError.Ok)
{
def.create.Price = price;
def.create.Quantity = Convert.ToUInt32(quantity);
}
handle.SetResult( parse_response(resp, true) );
}
else
handle.SetResult( parse_response(def.response, true) );
}
else
{
var resp = cancel_order(def.response.OrderKey);
handle.SetResult( parse_response(resp, true) );
}
}
else
handle.SetResult( spark_orderservice_send_order_key(on_off, local_order_id, (long)(sec.Value.Id), side, user, account, price, quantity,
order_type, order_params, time_in_force) );
}
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);
}
catch (Exception ex)
else
{
handle.SetException(ex);
return "Can't find security desc " + security_desc;
}
}
}
Binary file not shown.
-723
View File
@@ -1,723 +0,0 @@
/*
[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;
@@ -1,93 +0,0 @@
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');
+5
View File
@@ -0,0 +1,5 @@
<?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>
+5 -29
View File
@@ -15,11 +15,9 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
<NoWarn>0108,3021</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<Version>1.0.0</Version>
<Antlr4UseCSharpGenerator>true</Antlr4UseCSharpGenerator>
</PropertyGroup>
<ItemGroup>
@@ -30,21 +28,10 @@
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.Linq.Dynamic" Version="1.0.7" />
<PackageReference Include="ExcelDna.AddIn" Version="1.0.0" />
<PackageReference Include="ExcelDna.Integration" Version="1.0.0" />
<PackageReference Include="ExcelDna.AddIn" Version="0.34.6" />
<PackageReference Include="ExcelDna.Integration" Version="0.34.6" />
<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>
@@ -63,28 +50,17 @@
<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>
@@ -98,9 +74,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>
@@ -1,91 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using ExcelDna.Integration;
using bts.api;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace bts.excel
{
public class AlgoHelpers
{
static ConcurrentDictionary<string, object> _cache = new ConcurrentDictionary<string, object>();
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_parameter([ExcelArgument(AllowReference = true)] object parameter)
{
ExcelReference par = (ExcelReference)parameter;
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, par);
var cellref = new CellRef(sheetname, (par.ColumnFirst + 1, par.RowFirst + 1));
string r1c1 = cellref.r1_c1();
var cellname = XlCall.Excel(XlCall.xlfGetDef, r1c1, sheetname);
if (!(cellname is ExcelError))
{
GeneratorVisitor.register_algo_parameter(sheetname, cellname.ToString());
return true;
}
else
return $"{ErrorValue.Name}, please make algo parameters named ranges";
// ExcelReference caller = (ExcelReference)XlCall.Excel(XlCall.xlfCaller);
// var refr = $"R{caller.RowFirst+1}C{caller.ColumnFirst+1}:R{caller.RowLast+1}C{caller.ColumnLast+1}";
// var reff = new CellRef(sheetname, (caller.ColumnFirst + 1, caller.RowFirst + 1));
// refr = reff.r1_c1();
// bool hidden = false; bool local = true;
// var res = XlCall.Excel(XlCall.xlcDefineName, name, "=" + refr, Type.Missing, Type.Missing, hidden, Type.Missing, local);
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_take_snapshot(object data, string tag)
{
object val;
if(!_cache.TryGetValue(tag, out val))
{
_cache[tag] = val = data;
}
return val;
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_get_snapshot(string tag)
{
_cache.TryGetValue(tag, out object val);
return val;
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_snapshot_switch(string tag, int on_off)
{
object val;
if (!_cache.TryGetValue(tag, out val) && on_off > 0)
{
_cache[tag] = val = on_off;
}
return val ?? 0;
}
[ExcelFunction(IsMacroType = true)]
static public void sparkalgo_reset_all_snapshots(object _)
{
_cache.Clear();
}
static public void sparkalgo_reset_all_snapshots_no_args()
{
_cache.Clear();
}
[ExcelFunction(IsMacroType = true)]
static public void sparkalgo_reset_snapshot(string tag)
{
_cache.TryRemove(tag, out object val);
}
}
}
+116 -20
View File
@@ -16,7 +16,13 @@ namespace orderman {
using namespace api::orderman;
template <class K, class V> using Map = boost::container::flat_map<K, V>;
enum class AlgoWrapperOrder::Status { INACTIVE, W_ATTACH, WORKING, COMPLETED };
enum class AlgoWrapperOrder::Status {
INACTIVE,
W_ATTACH,
WORKING,
SHUTTING_DOWN,
COMPLETED
};
class ChildTemplate {
public:
@@ -62,6 +68,7 @@ struct AlgoWrapperOrder::PImpl {
OrderKey attach_target = null_order_key();
std::optional<api::orderman::Update> pending_update;
std::optional<std::pair<OrderStatus, std::string>> pending_terminate;
OrderStatus external_status() const { return _state.status(); }
void set_external_status(OrderStatus s) {
@@ -222,6 +229,7 @@ OrderOpResult AlgoWrapperOrder::on_activate(Price p, Quantity q,
return {};
case Status::W_ATTACH:
case Status::SHUTTING_DOWN:
case Status::WORKING:
bts_assert(false);
}
@@ -238,6 +246,7 @@ OrderOpResult AlgoWrapperOrder::on_update(Update const &update) {
me->pending_update = update;
return {};
case Status::COMPLETED:
case Status::SHUTTING_DOWN:
return {};
case Status::WORKING: {
me->set_price_quantity(Price(update.price()),
@@ -275,6 +284,7 @@ orderman::OrderOpResult AlgoWrapperOrder::on_detach() {
switch (me->status) {
case Status::W_ATTACH:
case Status::WORKING:
case Status::SHUTTING_DOWN:
for (auto &[_, c] : me->children) {
(void)_;
c.order.mark_detached_from_parent();
@@ -309,6 +319,7 @@ orderman::OrderOpResult AlgoWrapperOrder::on_cancel() {
break;
}
break;
case Status::SHUTTING_DOWN:
case Status::COMPLETED:
break;
case Status::INACTIVE:
@@ -347,13 +358,20 @@ Quantity AlgoWrapperOrder::total_fills() const {
return Quantity(me->state().filled_quantity());
}
bool AlgoWrapperOrder::is_shutting_down() const {
return me->status == Status::SHUTTING_DOWN;
}
void AlgoWrapperOrder::subscribe_book(SecurityKey k) {
if (!is_working())
return;
bts_assert(me->book_subs.find(k) == me->book_subs.end());
me->book_subs[k] = me->services->subscribe_to_book(
k, [this, k](auto const &ev) { me->algo->on_book(k, ev.book); });
k, [this, k](auto const &ev) {
ReportStateGuard _(*this);
me->algo->on_book(k, ev.book);
});
}
void AlgoWrapperOrder::unsubscribe_book(SecurityKey k) {
@@ -392,7 +410,10 @@ void AlgoWrapperOrder::subscribe_timer() {
bts_assert(!me->has_timer_sub);
me->has_timer_sub = true;
me->services->subscribe_to_time(
[this](auto const &) { me->algo->on_timer(); });
[this](auto const &) {
ReportStateGuard _(*this);
me->algo->on_timer();
});
}
void AlgoWrapperOrder::unsubscribe_timer() {
@@ -485,10 +506,9 @@ void AlgoWrapperOrder::detach_all_children() {
}
void AlgoWrapperOrder::update_fill_state(Quantity q, Price p) {
if (!is_working())
if (!is_working() && !is_shutting_down())
return;
ReportStateGuard _(*this);
auto previous_qty = me->state().filled_quantity();
if (previous_qty > q.value()) {
LOG(OrderMan, Error,
@@ -510,35 +530,37 @@ void AlgoWrapperOrder::update_fill_state(Quantity q, Price p) {
void AlgoWrapperOrder::set_ladder_visibility(bool is_visible) {
if (!is_working())
return;
ReportStateGuard _(*this);
me->set_ladder_visibility(is_visible);
}
void AlgoWrapperOrder::cancel(std::string const &msg) {
if (!is_working())
return;
ReportStateGuard _(*this);
terminate(ORDER_STATUS_CANCELED, msg);
if (working_children_count() == 0)
terminate(ORDER_STATUS_CANCELED, msg);
else
shutdown(ORDER_STATUS_CANCELED, msg);
}
void AlgoWrapperOrder::terminate_error(std::string const &msg) {
if (!is_working() && me->status != Status::W_ATTACH)
if (!is_working() && !is_shutting_down() && me->status != Status::W_ATTACH)
return;
ReportStateGuard _(*this);
// If we attached and an error was signaled we do not want to
// cancel our child because that child might be a long running
// order with a good queue position that we want to preserve
if (is_attaching())
me->children.erase(me->attach_target);
terminate(ORDER_STATUS_ERROR, msg);
}
void AlgoWrapperOrder::complete() {
if (!is_working())
return;
ReportStateGuard _(*this);
terminate(ORDER_STATUS_COMPLETED);
if (working_children_count() == 0)
terminate(ORDER_STATUS_COMPLETED);
else
shutdown(ORDER_STATUS_COMPLETED);
}
// --------------------------------------------------------------------------------
@@ -573,6 +595,7 @@ void AlgoWrapperOrder::on_attach_complete(
break;
case Status::COMPLETED:
break;
case Status::SHUTTING_DOWN:
case Status::INACTIVE:
case Status::WORKING:
bts_assert(false);
@@ -587,6 +610,7 @@ void AlgoWrapperOrder::on_child_order_update(
switch (me->status) {
case Status::WORKING:
case Status::W_ATTACH:
case Status::SHUTTING_DOWN:
if (auto it = me->children.find(child_key); it != me->children.end()) {
auto &child_record = it->second;
switch (new_child_state.status()) {
@@ -625,16 +649,38 @@ void AlgoWrapperOrder::on_child_order_update(
break;
}
} else
terminate_error(
fmt::format("Unknown child order update: {}", child_key.value()));
return;
LOG(OrderMan, Info,
"AlgoOrder {} received update for presumably detached order {}. "
"Ignoring it",
key().value(), child_key.value());
break;
case Status::COMPLETED:
return;
case Status::INACTIVE:
bts_assert(false);
return;
}
bts_assert(false);
if (me->status == Status::SHUTTING_DOWN)
try_complete_shutdown();
}
void AlgoWrapperOrder::try_complete_shutdown() {
bts_assert(me->status == Status::SHUTTING_DOWN);
bts_assert(me->children.size());
bts_assert(me->pending_terminate.has_value());
if (working_children_count() == 0) {
LOG(OrderMan, Info, "AlgoOrder {} completed shutdown. Terminated",
key().value());
terminate(me->pending_terminate.value().first,
me->pending_terminate.value().second);
} else {
LOG(OrderMan, Info,
"AlgoOrder {} still has {} orders working. Shutdown still in "
"progress",
key().value(), working_children_count());
}
}
void AlgoWrapperOrder::setup_transitions() {
@@ -676,8 +722,21 @@ void AlgoWrapperOrder::setup_transitions() {
// From WORKING
me->transition_actions[{Status::WORKING, Status::COMPLETED}] = [this]() {
cleanup();
me->children.clear();
_transition(Status::COMPLETED);
};
me->transition_actions[{Status::WORKING, Status::SHUTTING_DOWN}] = [this]() {
cleanup();
_transition(Status::SHUTTING_DOWN);
};
// From SHUTTING_DOWN
me->transition_actions[{Status::SHUTTING_DOWN, Status::COMPLETED}] =
[this]() {
me->children.clear();
me->pending_terminate.reset();
_transition(Status::COMPLETED);
};
}
void AlgoWrapperOrder::check_invariants() {
@@ -697,6 +756,7 @@ void AlgoWrapperOrder::check_invariants() {
bts_assert(me->book_subs.empty());
bts_assert(me->children.empty());
bts_assert(!me->has_timer_sub);
bts_assert(!me->pending_terminate.has_value());
break;
case Status::WORKING:
@@ -704,10 +764,23 @@ void AlgoWrapperOrder::check_invariants() {
bts_assert(me->security);
bts_assert(me->external_status() == ORDER_STATUS_WORKING);
bts_assert(!me->pending_update.has_value());
bts_assert(!me->pending_terminate.has_value());
break;
case Status::SHUTTING_DOWN:
bts_assert(working_children_count());
bts_assert(me->services);
bts_assert(me->security);
bts_assert(me->external_status() == ORDER_STATUS_WORKING);
bts_assert(me->book_subs.empty());
bts_assert(!me->has_timer_sub);
bts_assert(!me->pending_update.has_value());
bts_assert(me->pending_terminate.has_value());
break;
case Status::COMPLETED:
bts_assert(me->children.empty());
bts_assert(!me->pending_update.has_value());
bts_assert(me->external_status() == ORDER_STATUS_CANCELED ||
me->external_status() == ORDER_STATUS_COMPLETED ||
me->external_status() == ORDER_STATUS_ERROR ||
@@ -715,6 +788,7 @@ void AlgoWrapperOrder::check_invariants() {
bts_assert(me->book_subs.empty());
bts_assert(!me->has_timer_sub);
bts_assert(!me->pending_update.has_value());
bts_assert(!me->pending_terminate.has_value());
break;
}
}
@@ -745,15 +819,25 @@ void AlgoWrapperOrder::terminate(api::orderman::OrderStatus st,
transition(Status::COMPLETED);
}
void AlgoWrapperOrder::shutdown(api::orderman::OrderStatus st,
std::string const &error) {
bts_assert(!me->pending_terminate.has_value());
bts_assert(working_children_count());
me->pending_terminate = std::make_pair(st, error);
LOG(OrderMan, Info,
"AlgoOrder {} is shutting down. Waiting for {} child orders to complete "
"before terminating",
key().value(), working_children_count());
transition(Status::SHUTTING_DOWN);
}
void AlgoWrapperOrder::cleanup() {
for (auto const &s : me->book_subs) {
me->services->unsubscribe_from_book(s.second);
me->book_subs.clear();
}
for (auto &c : me->children) {
for (auto &c : me->children)
c.second.order.cancel();
me->children.clear();
}
if (me->has_timer_sub)
unsubscribe_timer();
me->pending_update.reset();
@@ -785,6 +869,16 @@ OrderKey AlgoWrapperOrder::index_and_launch_child(ChildOrder &&child, Price p,
return k;
}
size_t AlgoWrapperOrder::working_children_count() const {
auto working_children_count = 0;
for (auto &c : me->children)
if (!c.second.state.has_value() ||
c.second.state.value().status == ChildStatus::WORKING) {
working_children_count++;
}
return working_children_count;
}
bool AlgoWrapperOrder::is_working() const {
return me->status == Status::WORKING;
}
@@ -799,6 +893,8 @@ log::CharPtrWrapper AlgoWrapperOrder::state_name(Status st) {
return {"INACTIVE"};
case Status::WORKING:
return {"WORKING"};
case Status::SHUTTING_DOWN:
return {"SHUTTING_DOWN"};
}
bts_assert(false);
return {""};
@@ -40,6 +40,7 @@ class AlgoWrapperOrder : public Order,
Price price() const override;
Quantity quantity() const override;
Quantity total_fills() const override;
bool is_shutting_down() const override;
void subscribe_book(SecurityKey) override;
void unsubscribe_book(SecurityKey) override;
void subscribe_child_change(OrderKey) override;
@@ -75,10 +76,13 @@ class AlgoWrapperOrder : public Order,
void transition(Status);
void _transition(Status);
void terminate(api::orderman::OrderStatus, std::string const &error = "");
void shutdown(api::orderman::OrderStatus, std::string const &error = "");
void try_complete_shutdown();
void cleanup();
void update_child_record(api::orderman::OrderState const &, ChildRecord *);
OrderKey index_and_launch_child(ChildOrder &&, Price, Quantity, bool);
bool is_working() const ;
size_t working_children_count() const;
log::CharPtrWrapper state_name(Status) ;
ChildStatus map_child_status(api::orderman::OrderStatus) ;
bool _is_attaching(api::orderman::OrderDefinition const &) const ;
@@ -20,6 +20,13 @@ class IAlgoServices : public IBeforeLaunchAlgoServices {
// Returns the filled quantity for this algo
virtual Quantity total_fills() const = 0;
// Returns wheter the algo is in the process of shutting down (due
// to a cancel, an error, or just having completed its work). When
// algo is shutting down any requests to create, update or cancel
// child orders will be ignored.
virtual bool is_shutting_down() const = 0;
// --------------------------------------------------------------
// Event Subscriptions
// --------------------------------------------------------------
@@ -10,18 +10,8 @@ namespace orderman {
using namespace api::orderman;
enum class CoverAlgo::Status { WORKING, WAITING_FOR_HEDGES, DONE };
const char *CoverAlgo::status_name(Status st) {
switch (st) {
case Status::WORKING:
return "WORKING";
case Status::WAITING_FOR_HEDGES:
return "WAITING_FOR_HEDGES";
case Status::DONE:
return "Done";
}
return nullptr;
}
enum class CoverAlgo::Status { WORKING, WAITING_FOR_HEDGES, DONE, CANCELING };
struct CoverAlgo::PImpl {
IAlgoServices *algo;
OrderKey child_key = null_order_key();
@@ -92,8 +82,11 @@ void CoverAlgo::do_launch_attached(IAlgoServices &algo, OrderKey k,
on_child_change(k, st);
}
void CoverAlgo::on_child_change(OrderKey k, ChildState const &st) {
if (k == me->child_key) {
void CoverAlgo::on_covered_change(ChildState const &st) {
// First launch hedge if appropriate
switch (me->status) {
case Status::WORKING:
case Status::CANCELING:
if (st.total_fills.value() > me->hedged_fills.value()) {
auto hedge_qty =
Quantity(st.total_fills.value() - me->hedged_fills.value());
@@ -116,6 +109,15 @@ void CoverAlgo::on_child_change(OrderKey k, ChildState const &st) {
hedge_qty.value(), hedge_price.value());
}
algo().update_fill_state(st.total_fills, st.last_price);
break;
case Status::WAITING_FOR_HEDGES:
case Status::DONE:
break;
}
// Now analyze child to figure out wheter we should terminate
switch (me->status) {
case Status::WORKING:
switch (st.status) {
case ChildStatus::WORKING:
break;
@@ -125,6 +127,7 @@ void CoverAlgo::on_child_change(OrderKey k, ChildState const &st) {
algo().order_key().value());
algo().set_ladder_visibility(false);
me->status = Status::WAITING_FOR_HEDGES;
check_terminate();
break;
case ChildStatus::CANCELED:
LOG(OrderMan, Info, "CoverAlgo {}. Child was canceled, terminating",
@@ -133,30 +136,60 @@ void CoverAlgo::on_child_change(OrderKey k, ChildState const &st) {
algo().terminate_error("Unexpected child cancel");
break;
}
} else {
break;
case Status::CANCELING:
switch (st.status) {
case ChildStatus::WORKING:
break;
case ChildStatus::CANCELED:
if (me->cancel_on_external_hedge_cancel) {
LOG(OrderMan, Info, "CoverAlgo {}. Hedge was canceled, terminating",
algo().order_key().value());
me->status = Status::DONE;
algo().terminate_error("Unexpected child cancel");
} else
me->hedges.erase(
std::remove(me->hedges.begin(), me->hedges.end(), k),
me->hedges.end());
break;
case ChildStatus::COMPLETED:
me->hedges.erase(std::remove(me->hedges.begin(), me->hedges.end(), k),
me->hedges.end());
LOG(OrderMan, Info, "CoverAlgo {}. Shutdown complete",
algo().order_key().value());
me->status = Status::DONE;
algo().cancel();
break;
}
break;
case Status::WAITING_FOR_HEDGES:
case Status::DONE:
break;
}
}
void CoverAlgo::on_hedge_change(OrderKey k, ChildState const &st) {
// First adjust our hedge count or cancel if something went wrong
switch (st.status) {
case ChildStatus::WORKING:
break;
case ChildStatus::COMPLETED:
me->hedges.erase(std::remove(me->hedges.begin(), me->hedges.end(), k),
me->hedges.end());
break;
case ChildStatus::CANCELED:
if (me->cancel_on_external_hedge_cancel) {
LOG(OrderMan, Info, "CoverAlgo {}. Hedge was canceled, terminating",
algo().order_key().value());
me->status = Status::DONE;
algo().terminate_error("Unexpected child cancel");
return;
} else
me->hedges.erase(std::remove(me->hedges.begin(), me->hedges.end(), k),
me->hedges.end());
break;
}
// Now if we are waiting for hedges to complete, bail
check_terminate();
}
void CoverAlgo::on_child_change(OrderKey k, ChildState const &st) {
if (k == me->child_key)
on_covered_change(st);
else
on_hedge_change(k,st);
}
void CoverAlgo::do_update(api::orderman::Update const &update) {
if (me->status == Status::WORKING) {
LOG(OrderMan, Diag, "CoverAlgo order {} receiving update: {}",
@@ -225,11 +258,45 @@ void CoverAlgo::check_terminate() {
}
IAlgoOrder::OnCancelResponse CoverAlgo::on_cancel() {
if (!me->cancel_hedges_on_cancel)
for (auto &k : me->hedges)
algo().detach_child(k);
switch (me->status) {
case Status::WORKING: {
if (!me->cancel_hedges_on_cancel)
for (auto &k : me->hedges)
algo().detach_child(k);
algo().cancel_child(me->child_key);
LOG(OrderMan, Info,
"CoverAlgo {} received cancel. Canceling child and waiting for an ack",
algo().order_key());
me->status = Status::CANCELING;
return IAlgoOrder::OnCancelResponse::IGNORE;
}
case Status::CANCELING:
return IAlgoOrder::OnCancelResponse::IGNORE;
case Status::WAITING_FOR_HEDGES:
return IAlgoOrder::OnCancelResponse::HONOR;
case Status::DONE:
bts_assert(false);
return IAlgoOrder::OnCancelResponse::HONOR;
}
bts_assert(false);
return IAlgoOrder::OnCancelResponse::HONOR;
}
const char *CoverAlgo::status_name(Status st) {
switch (st) {
case Status::WORKING:
return "WORKING";
case Status::WAITING_FOR_HEDGES:
return "WAITING_FOR_HEDGES";
case Status::DONE:
return "DONE";
case Status::CANCELING:
return "CANCELING";
}
return nullptr;
}
} // namespace orderman
} // namespace bts
@@ -31,6 +31,9 @@ private:
void init_hedge_template(IBeforeLaunchAlgoServices &,
api::orderman::CustomOrderDefinition const &);
void check_terminate();
void on_hedge_change(OrderKey hedge, ChildState const&) ;
void on_covered_change(ChildState const&);
const char *status_name(Status st);
};
@@ -145,7 +145,7 @@ void IcebergAlgo::on_child_change(OrderKey k, ChildState const &st) {
on_timer();
return;
case ChildStatus::CANCELED:
algo().terminate_error("Unexpected child cancel");
algo().cancel("Unexpected child cancel");
return;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@@ -0,0 +1,39 @@
#+TITLE: Algo Wrapper State Machien
#+OPTIONS: ^:nil
#+OPTIONS: H:2
#+OPTIONS: ^:nil H:2 author:nil creator:nil timestamp:nil toc:nil
#+OPTIONS: html-postamble:nil
* State Machine
#+begin_src dot :file algo_wrapper_sm.png :cmdline -Kdot -Tpng
digraph G {
bgcolor="transparent";
edge [ fontname=Helvetica, fontsize=12 ];
start [style=invisible];
COMPLETED [style=filled] ;
start -> INACTIVE;
INACTIVE -> COMPLETED [label="qty=0|error"];
INACTIVE -> W_ATTACH [label="ok & attach"];
INACTIVE -> WORKING [label="ok & !attach"];
W_ATTACH -> COMPLETED [label=attach_fail];
W_ATTACH -> WORKING [label=attach_ok];
WORKING -> COMPLETED [label="[wchild=0]cancel|complete"];
WORKING -> COMPLETED [label=error];
WORKING -> SHUTTING_DOWN [label="[wchild>0]cancel|complete"];
SHUTTING_DOWN -> COMPLETED [label="wchild==0|error|on_detach"];
}
#+END_SRC
#+RESULTS:
[[file:algo_wrapper_sm.png]]
* Invariants
| | attach_target | exernal_status | pending_update | pending_terminate | working_children | subs |
|---------------+---------------+--------------------------------------+----------------+-------------------+------------------+------|
| INACTIVE | | | | | | 0 |
| W_ATTACH | 1 | WORKING | | 0 | 0 | 0 |
| WORKING | | WORKING | 0 | 0 | | |
| SHUTTING_DOWN | | WORKING | 0 | 1 | >0 | 0 |
| COMPLETED | | CANCELED, COMPLETED, ERROR, REJECTED | 0 | 0 | 0 | 0 |
+1
View File
@@ -27,6 +27,7 @@ struct Proxy : public IAlgoServices {
Price price() const { return Price(0); }
Quantity quantity() const { return Quantity(0); }
Quantity total_fills() const { return Quantity(0); }
bool is_shutting_down() const {return false;}
void subscribe_book(SecurityKey){};
void unsubscribe_book(SecurityKey) {}
void subscribe_child_change(OrderKey) {}
@@ -43,14 +43,10 @@ MarginServiceServer::MarginServiceServer(bts::api::ServiceContext& context, md::
_last_report(),
_first_time(true)
{
LOG(Gen, Info, "MarginServiceServer: service started");
if(!config::get<bts::config::SpanMargin>().enabled())
return;
// _admin_service.get_admin_data(api::VoidMessage(),
// [=] (const bts::api::Result<::bts::api::admin::AdminData> r)
// {
// if(r.value())
// _admin_data = *r.value();
// });
LOG(Gen, Info, "MarginServiceServer: service started");
_admin_service.subscribe_admin_data() |
rxcpp::operators::subscribe<api::admin::Admin_dataUpdate>(
@@ -200,6 +196,13 @@ void MarginServiceServer::incremental_update_report(const std::string& account,
void MarginServiceServer::set_cash_factor(const ::bts::api::position::CashFactorUpdate& request,
const api::Response<::bts::api::ResultMessage>& reply)
{
if(!config::get<bts::config::SpanMargin>().enabled())
{
api::ResultMessage resp; resp.set_success(true);
reply(resp);
return;
}
std::scoped_lock<std::recursive_mutex> l(_lock);
auto acct = AdminServiceUtils::find_account(_admin_data, request.account(), false);
if(!acct)
@@ -231,6 +234,13 @@ void MarginServiceServer::set_cash_factor(const ::bts::api::position::CashFactor
void MarginServiceServer::set_cash_balance(const ::bts::api::position::CashBalanceUpdate& request,
const api::Response<::bts::api::ResultMessage>& reply)
{
if(!config::get<bts::config::SpanMargin>().enabled())
{
api::ResultMessage resp; resp.set_success(true);
reply(resp);
return;
}
std::scoped_lock<std::recursive_mutex> l(_lock);
auto acct = AdminServiceUtils::find_account(_admin_data, request.account(), false);
if(!acct)
@@ -262,6 +272,13 @@ void MarginServiceServer::set_cash_balance(const ::bts::api::position::CashBalan
void MarginServiceServer::set_should_enforce_margin(const ::bts::api::position::EnforceUpdate& request,
const api::Response<::bts::api::ResultMessage>& reply)
{
if(!config::get<bts::config::SpanMargin>().enabled())
{
api::ResultMessage resp; resp.set_success(true);
reply(resp);
return;
}
std::scoped_lock<std::recursive_mutex> l(_lock);
auto acct = AdminServiceUtils::find_account(_admin_data, request.account(), false);
if(!acct)
@@ -84,16 +84,12 @@ me(new PImpl(context,
rxcpp::operators::subscribe<api::orderman::FillsUpdate>(
[this](auto const& update) {
for (const auto& f : update.removed())
if(_valid_account_id(*f))
me->publisher.add_or_update(_on_fill(*f,false));
me->publisher.add_or_update(_on_fill(*f,false));
for (const auto& f : update.added())
if(_valid_account_id(*f))
me->publisher.add_or_update(_on_fill(*f,true));
me->publisher.add_or_update(_on_fill(*f,true));
for (const auto& f : update.changed()) {
if(_valid_account_id(*f.previous()) && _valid_account_id(*f.current())){
me->publisher.add_or_update(_on_fill(*f.previous(),false));
me->publisher.add_or_update(_on_fill(*f.current(),true));
}
me->publisher.add_or_update(_on_fill(*f.previous(),false));
me->publisher.add_or_update(_on_fill(*f.current(),true));
}
me->publisher.flush();
_compute_pnl_and_publish();