Formula Parsing
This commit is contained in:
parent
740d255bb2
commit
fc53fa71bc
|
@ -12,9 +12,212 @@ using Core.Main;
|
||||||
using Core.Helper;
|
using Core.Helper;
|
||||||
using Core.Main.DataObjects.PTMagicData;
|
using Core.Main.DataObjects.PTMagicData;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Core.ProfitTrailer
|
namespace Core.ProfitTrailer
|
||||||
{
|
{
|
||||||
|
public class OperandToken : Token
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public class OrToken : OperandToken
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AndToken : OperandToken
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BooleanValueToken : Token
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FalseToken : BooleanValueToken
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TrueToken : BooleanValueToken
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParenthesisToken : Token
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ClosedParenthesisToken : ParenthesisToken
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OpenParenthesisToken : ParenthesisToken
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NegationToken : Token
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class Token
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Tokenizer
|
||||||
|
{
|
||||||
|
private readonly StringReader _reader;
|
||||||
|
private string _text;
|
||||||
|
|
||||||
|
public Tokenizer(string text)
|
||||||
|
{
|
||||||
|
_text = text;
|
||||||
|
_reader = new StringReader(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Token> Tokenize()
|
||||||
|
{
|
||||||
|
var tokens = new List<Token>();
|
||||||
|
while (_reader.Peek() != -1)
|
||||||
|
{
|
||||||
|
while (Char.IsWhiteSpace((char) _reader.Peek()))
|
||||||
|
{
|
||||||
|
_reader.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_reader.Peek() == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var c = (char) _reader.Peek();
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '!':
|
||||||
|
tokens.Add(new NegationToken());
|
||||||
|
_reader.Read();
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
tokens.Add(new OpenParenthesisToken());
|
||||||
|
_reader.Read();
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
tokens.Add(new ClosedParenthesisToken());
|
||||||
|
_reader.Read();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (Char.IsLetter(c))
|
||||||
|
{
|
||||||
|
var token = ParseKeyword();
|
||||||
|
tokens.Add(token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var remainingText = _reader.ReadToEnd() ?? string.Empty;
|
||||||
|
throw new Exception(string.Format("Unknown grammar found at position {0} : '{1}'", _text.Length - remainingText.Length, remainingText));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token ParseKeyword()
|
||||||
|
{
|
||||||
|
var text = new StringBuilder();
|
||||||
|
while (Char.IsLetter((char) _reader.Peek()))
|
||||||
|
{
|
||||||
|
text.Append((char) _reader.Read());
|
||||||
|
}
|
||||||
|
|
||||||
|
var potentialKeyword = text.ToString().ToLower();
|
||||||
|
|
||||||
|
switch (potentialKeyword)
|
||||||
|
{
|
||||||
|
case "true":
|
||||||
|
return new TrueToken();
|
||||||
|
case "false":
|
||||||
|
return new FalseToken();
|
||||||
|
case "and":
|
||||||
|
return new AndToken();
|
||||||
|
case "or":
|
||||||
|
return new OrToken();
|
||||||
|
default:
|
||||||
|
throw new Exception("Expected keyword (True, False, and, or) but found "+ potentialKeyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class Parser
|
||||||
|
{
|
||||||
|
private readonly IEnumerator<Token> _tokens;
|
||||||
|
|
||||||
|
public Parser(IEnumerable<Token> tokens)
|
||||||
|
{
|
||||||
|
_tokens = tokens.GetEnumerator();
|
||||||
|
_tokens.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Parse()
|
||||||
|
{
|
||||||
|
while (_tokens.Current != null)
|
||||||
|
{
|
||||||
|
var isNegated = _tokens.Current is NegationToken;
|
||||||
|
if (isNegated)
|
||||||
|
_tokens.MoveNext();
|
||||||
|
|
||||||
|
var boolean = ParseBoolean();
|
||||||
|
if (isNegated)
|
||||||
|
boolean = !boolean;
|
||||||
|
|
||||||
|
while (_tokens.Current is OperandToken)
|
||||||
|
{
|
||||||
|
var operand = _tokens.Current;
|
||||||
|
if (!_tokens.MoveNext())
|
||||||
|
{
|
||||||
|
throw new Exception("Missing expression after operand");
|
||||||
|
}
|
||||||
|
var nextBoolean = ParseBoolean();
|
||||||
|
|
||||||
|
if (operand is AndToken)
|
||||||
|
boolean = boolean && nextBoolean;
|
||||||
|
else
|
||||||
|
boolean = boolean || nextBoolean;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Empty expression");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ParseBoolean()
|
||||||
|
{
|
||||||
|
if (_tokens.Current is BooleanValueToken)
|
||||||
|
{
|
||||||
|
var current = _tokens.Current;
|
||||||
|
_tokens.MoveNext();
|
||||||
|
|
||||||
|
if (current is TrueToken)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (_tokens.Current is OpenParenthesisToken)
|
||||||
|
{
|
||||||
|
_tokens.MoveNext();
|
||||||
|
|
||||||
|
var expInPars = Parse();
|
||||||
|
|
||||||
|
if (!(_tokens.Current is ClosedParenthesisToken))
|
||||||
|
throw new Exception("Expecting Closing Parenthesis");
|
||||||
|
|
||||||
|
_tokens.MoveNext();
|
||||||
|
|
||||||
|
return expInPars;
|
||||||
|
}
|
||||||
|
if (_tokens.Current is ClosedParenthesisToken)
|
||||||
|
throw new Exception("Unexpected Closed Parenthesis");
|
||||||
|
|
||||||
|
// since its not a BooleanConstant or Expression in parenthesis, it must be a expression again
|
||||||
|
var val = Parse();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class StrategyHelper
|
public static class StrategyHelper
|
||||||
{
|
{
|
||||||
public static string GetStrategyShortcut(string strategyName, bool onlyValidStrategies)
|
public static string GetStrategyShortcut(string strategyName, bool onlyValidStrategies)
|
||||||
|
@ -36,7 +239,6 @@ namespace Core.ProfitTrailer
|
||||||
}
|
}
|
||||||
|
|
||||||
// buy/sell strategies beginning with PT 2.3.3 contain the stragegy designation letter followed by a colon and space.
|
// buy/sell strategies beginning with PT 2.3.3 contain the stragegy designation letter followed by a colon and space.
|
||||||
|
|
||||||
// remove the letter and colon, change to shortcut, then reapply the letter and colon
|
// remove the letter and colon, change to shortcut, then reapply the letter and colon
|
||||||
if (strategyName.Contains(":"))
|
if (strategyName.Contains(":"))
|
||||||
{
|
{
|
||||||
|
@ -317,10 +519,22 @@ namespace Core.ProfitTrailer
|
||||||
|
|
||||||
if (!isValidStrategy )
|
if (!isValidStrategy )
|
||||||
{
|
{
|
||||||
// Temporary until a fix for formula true/false
|
// Parse Formulas
|
||||||
if (strategy.Name.Contains("FORMULA"))
|
if (strategy.Name.Contains("FORMULA"))
|
||||||
{
|
{
|
||||||
strategyText += "<span class=\"label label-warning\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"AND/OR FORMULA\">FORM</span> ";
|
string expression = strategy.Name.Remove(0, 10);
|
||||||
|
expression = expression.Replace("<span class=\"tdgreen\">","true").Replace("<span class=\"red\">","false").Replace("</span>","").Replace("&&","and").Replace("||","or");
|
||||||
|
expression = Regex.Replace(expression, @"[ABCDEFGHIJKLMNOPQRSTUVWXYZ]", String.Empty);
|
||||||
|
var tokens = new Tokenizer(expression).Tokenize();
|
||||||
|
var parser = new Parser(tokens);
|
||||||
|
if (parser.Parse()) {
|
||||||
|
strategyText += "<span class=\"label label-success\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"CONDITIONAL FORMULA\">(FORM)</span> ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strategyText += "<span class=\"label label-danger\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"CONDITIONAL FORMULA\">(FORM)</span> ";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue