using System; using System.Collections.Generic; using System.Linq; using System.Text; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using HeuristicLab.Problems.DataAnalysis.Symbolic; using HeuristicLab.Problems.DataAnalysis; namespace HeuristicLab.Problems.TradeRules { [StorableClass] [Item("Interpreter", "Represents a grammar for Trading Problems")] public sealed class Interpreter : ParameterizedNamedItem, ISymbolicDataAnalysisExpressionTreeInterpreter { private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic"; private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions"; [ThreadStatic] private static double [] EMAValue; [ThreadStatic] private static double inOut; [ThreadStatic] private static double meanUp; [ThreadStatic] private static double meanDown; [ThreadStatic] private static double lastRSI; #region private classes //This class manipulate the instructions of the stack private class InterpreterState { private double[] argumentStack; private int argumentStackPointer; private Instruction[] code; private int pc; public int ProgramCounter { get { return pc; } set { pc = value; } } internal InterpreterState(Instruction[] code, int argumentStackSize) { this.code = code; this.pc = 0; if (argumentStackSize > 0) { this.argumentStack = new double[argumentStackSize]; } this.argumentStackPointer = 0; } internal void Reset() { this.pc = 0; this.argumentStackPointer = 0; } internal Instruction NextInstruction() { return code[pc++]; } private void Push(double val) { argumentStack[argumentStackPointer++] = val; } private double Pop() { return argumentStack[--argumentStackPointer]; } internal void CreateStackFrame(double[] argValues) { // push in reverse order to make indexing easier for (int i = argValues.Length - 1; i >= 0; i--) { argumentStack[argumentStackPointer++] = argValues[i]; } Push(argValues.Length); } internal void RemoveStackFrame() { int size = (int)Pop(); argumentStackPointer -= size; } internal double GetStackFrameValue(ushort index) { // layout of stack: // [0] <- argumentStackPointer // [StackFrameSize = N + 1] // [Arg0] <- argumentStackPointer - 2 - 0 // [Arg1] <- argumentStackPointer - 2 - 1 // [...] // [ArgN] <- argumentStackPointer - 2 - N // return argumentStack[argumentStackPointer - index - 2]; } } //Operation codes private class OpCodes { public const byte Add = 1; public const byte Sub = 2; public const byte Mul = 3; public const byte GT = 5; public const byte LT = 6; public const byte AND = 7; public const byte OR = 8; public const byte NOT = 9; public const byte BOOLEAN = 10; public const byte Average = 11; public const byte MACD = 12; public const byte Variable = 13; public const byte Constant = 14; public const byte ConstantInt = 16; public const byte BoolConstant = 15; public const byte Max = 17; public const byte Min = 18; public const byte Lag = 19; public const byte RSI = 20; public const byte EMA = 21; } #endregion #region IStatefulItem public void InitializeState() { EvaluatedSolutions.Value = 0; } public void ClearState() { } #endregion private Dictionary symbolToOpcode = new Dictionary() { { typeof(Addition), OpCodes.Add }, { typeof(Subtraction), OpCodes.Sub }, { typeof(Multiplication), OpCodes.Mul }, { typeof(Constant), OpCodes.Constant }, { typeof(BoolConstant), OpCodes.BoolConstant }, { typeof(ConstantInt), OpCodes.ConstantInt }, { typeof(GreaterThan), OpCodes.GT }, { typeof(LessThan), OpCodes.LT }, { typeof(And), OpCodes.AND }, { typeof(Or), OpCodes.OR }, { typeof(Not), OpCodes.NOT}, { typeof(AverageTrade), OpCodes.Average}, { typeof(MACD), OpCodes.MACD}, { typeof(RSI), OpCodes.RSI}, { typeof(EMA), OpCodes.EMA}, { typeof(Max), OpCodes.Max}, { typeof(Min), OpCodes.Min}, { typeof(Lag), OpCodes.Lag}, { typeof(HeuristicLab.Problems.DataAnalysis.Symbolic.Variable), OpCodes.Variable }, }; public override bool CanChangeName { get { return false; } } public override bool CanChangeDescription { get { return false; } } #region parameter properties public IValueParameter CheckExpressionsWithIntervalArithmeticParameter { get { return (IValueParameter)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; } } public IValueParameter EvaluatedSolutionsParameter { get { return (IValueParameter)Parameters[EvaluatedSolutionsParameterName]; } } #endregion #region properties public BoolValue CheckExpressionsWithIntervalArithmetic { get { return CheckExpressionsWithIntervalArithmeticParameter.Value; } set { CheckExpressionsWithIntervalArithmeticParameter.Value = value; } } public IntValue EvaluatedSolutions { get { return EvaluatedSolutionsParameter.Value; } set { EvaluatedSolutionsParameter.Value = value; } } #endregion private double Evaluate(Dataset dataset, ref int row, InterpreterState state) { Instruction currentInstr = state.NextInstruction(); switch (currentInstr.opCode) { case OpCodes.Add: { double s = Evaluate(dataset, ref row, state); for (int i = 1; i < currentInstr.nArguments; i++) { s += Evaluate(dataset, ref row, state); } return s; } case OpCodes.Sub: { double s = Evaluate(dataset, ref row, state); for (int i = 1; i < currentInstr.nArguments; i++) { s -= Evaluate(dataset, ref row, state); } if (currentInstr.nArguments == 1) s = -s; return s; } case OpCodes.Mul: { double p = Evaluate(dataset, ref row, state); for (int i = 1; i < currentInstr.nArguments; i++) { p *= Evaluate(dataset, ref row, state); } return p; } case OpCodes.Average: { double sum = Evaluate(dataset, ref row, state); int integerValue = (int) Math.Floor(sum); if (integerValue > 100) integerValue = 100; if (row < integerValue) { string variableName = dataset.GetValue(row, 2); double inferiorValue = Convert.ToDouble(variableName); return inferiorValue/(row+1); } else { string variableName = dataset.GetValue(row, 2); double meanValue1 = Convert.ToDouble(variableName); string variableName2 = dataset.GetValue((row - integerValue), 2); double meanValue2 = Convert.ToDouble(variableName2); return (meanValue1 - meanValue2) / integerValue; } } case OpCodes.MACD: { /*double firstMean = Evaluate(dataset, ref row, state); double secondMean = Evaluate(dataset, ref row, state); double signal = Evaluate(dataset, ref row, state); double firstEMA = 0.0; double secondEMA = 0.0; double lastFirstEMA = 0.0; double lastSecondEMA = 0.0; double macd = 0.0; double firstFactor = 0.0; double signalValue = Double.NegativeInfinity; if (row == (firstMean - 1)) { string variableName = dataset.GetValue(row, 2); double meanValue1 = Convert.ToDouble(variableName); firstEMA = meanValue1 / firstMean; } if (row == (secondMean-1)) { string variableName = dataset.GetValue(row, 2); double meanValue2 = Convert.ToDouble(variableName); secondEMA = meanValue2 / secondMean; } string variableName2 = dataset.GetValue(row, 1); double intValue = Convert.ToDouble(variableName2); if (row > (firstMean-1)) { firstFactor = 2 / (firstMean + 1); firstEMA = (intValue * firstFactor) + (lastFirstEMA * (1-firstFactor)); } if (row > (secondMean-1)) { double secondFactor = 2 / (secondMean + 1); secondEMA = (intValue * secondFactor) + (lastSecondEMA * (1 - secondFactor)); } lastFirstEMA = firstEMA; lastSecondEMA = secondEMA; macd = firstEMA - secondEMA; if ((row < (firstMean-1)) || (row < (secondMean-1))) return 0.0; else return macd;*/ return 0.0; //macd = firstEMA - secondEMA; //if (row == Math.Max(firstMean, secondMean)) signalValue = macd; //if (row > firstMean && row > secondMean) //{ // double factor = 2 / (signal + 1); // signalValue = (macd * factor) + (lastSignal * (1 - factor)); //} //if (!Double.IsNegativeInfinity(signalValue)) lastSignal = signalValue; //return signalValue > macd ? 1.0 : -1.0; } case OpCodes.AND: { double result = Evaluate(dataset, ref row, state); for (int i = 1; i < currentInstr.nArguments; i++) { if (result > 0.0) result = Evaluate(dataset, ref row, state); else { SkipInstructions(state); } } return result > 0.0 ? 1.0 : -1.0; } case OpCodes.OR: { double result = Evaluate(dataset, ref row, state); for (int i = 1; i < currentInstr.nArguments; i++) { if (result <= 0.0) result = Evaluate(dataset, ref row, state); else { SkipInstructions(state); } } return result > 0.0 ? 1.0 : -1.0; } case OpCodes.NOT: { return Evaluate(dataset, ref row, state) > 0.0 ? -1.0 : 1.0; } case OpCodes.BOOLEAN: { var booleanTreeNode = currentInstr.dynamicNode as BoolConstantTreeNode; return booleanTreeNode.Value; } case OpCodes.GT: { double x = Evaluate(dataset, ref row, state); double y = Evaluate(dataset, ref row, state); if (x > y) return 1.0; else return -1.0; } case OpCodes.LT: { double x = Evaluate(dataset, ref row, state); double y = Evaluate(dataset, ref row, state); if (x < y) return 1.0; else return -1.0; } case OpCodes.Variable: { if (row < 0 || row >= dataset.Rows) return double.NaN; var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode; return ((IList)currentInstr.iArg0)[row] * variableTreeNode.Weight; } case OpCodes.EMA: { //I assume you want to calculate the EMA as defined at http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average //for the mean value saved at position 1 of the dataset //and timevalue specifies how many days you want to include in your EMA double timeValue = (int)Evaluate(dataset, ref row, state); //get all mean values from row - timeValue up to the actual row double[] meanValues = dataset.GetDoubleValues("insert variable name", Enumerable.Range(row - (int)timeValue, timeValue)).ToArray(); double EMA = meanValues[0]; double factor = 2.0 / (timeValue + 1.0); for (int i = 1; i < timeValue; i++) { EMA = meanValues[i] * factor + (1 - factor) * EMA; } return EMA; } case OpCodes.Constant: { var constTreeNode = currentInstr.dynamicNode as ConstantTreeNode; return constTreeNode.Value; } case OpCodes.BoolConstant: { var boolConstTreeNode = currentInstr.dynamicNode as BoolConstantTreeNode; return boolConstTreeNode.Value; } case OpCodes.ConstantInt: { var constIntTreeNode = currentInstr.dynamicNode as ConstantIntTreeNode; return constIntTreeNode.Value; } case OpCodes.Max: { int n = (int) Evaluate(dataset, ref row, state); double max = Double.NegativeInfinity; int i = Math.Min(n,row); while(i>=0) { int position = row - i; string variableName = dataset.GetValue(position, 1); double intValue = Convert.ToDouble(variableName); if (intValue>max) max = intValue; i--; } return max; } case OpCodes.Min: { int n = (int)Evaluate(dataset, ref row, state); double min = Double.NegativeInfinity; int i = Math.Min(n, row); while (i >= 0) { int position = row - i; string variableName = dataset.GetValue(position, 1); double intValue = Convert.ToDouble(variableName); if (intValue < min) min = intValue; i--; } return min; } case OpCodes.Lag: { int n = (int) Evaluate(dataset, ref row, state); if (n>row) return 0; int position = row - n; string variableName = dataset.GetValue(position, 1); double intValue = Convert.ToDouble(variableName); return intValue; } case OpCodes.RSI: { /* double numberOfDays = Evaluate(dataset, ref row, state); double todayRSI=0.0; if (numberOfDays > row) return 0.0; else { double addUp = 0; double addDown = 0; double change=0; if (row == (numberOfDays)) { for (int k = 1; k < (numberOfDays+1); k++) { string variableName = dataset.GetValue(k, 1); double intValue = Convert.ToDouble(variableName); variableName = dataset.GetValue((k - 1), 1); double intValue2 = Convert.ToDouble(variableName); change = intValue - intValue2; if (change > 0) addUp = addUp + change; else addDown = addDown - change; } meanUp = addUp / numberOfDays; meanDown = addDown / numberOfDays; if (meanDown != 0) todayRSI = 100 - (100 / (1 + (meanUp / meanDown))); else todayRSI = 100; } else { string variableName = dataset.GetValue(row, 1); double intValue = Convert.ToDouble(variableName); variableName = dataset.GetValue((row - 1), 1); double intValue2 = Convert.ToDouble(variableName); change = intValue - intValue2; addUp = meanUp * (numberOfDays - 1); addDown = meanDown * (numberOfDays - 1); if (change > 0) addUp = addUp + change; else addDown = addDown - change; meanUp = addUp / numberOfDays; meanDown = addDown / numberOfDays; if (meanDown != 0) todayRSI = 100 - (100 / (1 + (meanUp / meanDown))); else todayRSI = 100; } } if ((lastRSI < 70) && (todayRSI >= 70)) inOut = 1; else if((lastRSI > 30) && (todayRSI <= 30)) inOut=-1; lastRSI = todayRSI;*/ return 0.0; } default: throw new NotSupportedException(); } } private byte MapSymbolToOpCode(ISymbolicExpressionTreeNode treeNode) { if (symbolToOpcode.ContainsKey(treeNode.Symbol.GetType())) return symbolToOpcode[treeNode.Symbol.GetType()]; else throw new NotSupportedException("Symbol: " + treeNode.Symbol); } // skips a whole branch private void SkipInstructions(InterpreterState state) { int i = 1; while (i > 0) { i += state.NextInstruction().nArguments; i--; } } [StorableConstructor] private Interpreter(bool deserializing) : base(deserializing) { } private Interpreter(Interpreter original, Cloner cloner) : base(original, cloner) { } public override IDeepCloneable Clone(Cloner cloner) { return new Interpreter(this, cloner); } public Interpreter() : base("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.") { Parameters.Add(new ValueParameter(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false))); Parameters.Add(new ValueParameter(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0))); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { if (!Parameters.ContainsKey(EvaluatedSolutionsParameterName)) Parameters.Add(new ValueParameter(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0))); } //Take the symbolic expression values of the tree public IEnumerable GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, IEnumerable rows) { if (CheckExpressionsWithIntervalArithmetic.Value) throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter."); EvaluatedSolutions.Value++; // increment the evaluated solutions counter var compiler = new SymbolicExpressionTreeCompiler(); Instruction[] code = compiler.Compile(tree, MapSymbolToOpCode);//Take the type of symbol int necessaryArgStackSize = 0; for (int i = 0; i < code.Length; i++) { Instruction instr = code[i]; if (instr.opCode == OpCodes.Variable) { var variableTreeNode = instr.dynamicNode as VariableTreeNode; instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName); code[i] = instr; } else if (instr.opCode == OpCodes.EMA) { instr.iArg0 = EMAValue; } } var state = new InterpreterState(code, necessaryArgStackSize); //Evaluate each row of the datase foreach (var rowEnum in rows) { int row = rowEnum; state.Reset(); yield return Evaluate(dataset, ref row, state); } } } }