Free cookie consent management tool by TermsFeed Policy Generator

source: branches/sluengo/HeuristicLab.Problems.TradeRules/Interpreter.cs @ 9171

Last change on this file since 9171 was 9171, checked in by gkronber, 11 years ago

added comments suggesting a solution for calculating the EMA using a cache.

File size: 25.4 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using HeuristicLab.Common;
6using HeuristicLab.Core;
7using HeuristicLab.Data;
8using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
9using HeuristicLab.Parameters;
10using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
11using HeuristicLab.Problems.DataAnalysis.Symbolic;
12using HeuristicLab.Problems.DataAnalysis;
13
14namespace HeuristicLab.Problems.TradeRules
15{
16    [StorableClass]
17    [Item("Interpreter", "Represents a grammar for Trading Problems")]
18    public sealed class Interpreter : ParameterizedNamedItem, ISymbolicDataAnalysisExpressionTreeInterpreter
19    {
20        private const string CheckExpressionsWithIntervalArithmeticParameterName = "CheckExpressionsWithIntervalArithmetic";
21        private const string EvaluatedSolutionsParameterName = "EvaluatedSolutions";
22        [ThreadStatic]
23        private static double [] EMAValue;
24        [ThreadStatic]
25        private static double inOut;
26        [ThreadStatic]
27        private static double meanUp;
28        [ThreadStatic]
29        private static double meanDown;
30        [ThreadStatic]
31        private static double lastRSI;
32   
33    // [ThreadStatic]
34    // private Dictionary<Instruction, double> lastEMACache;
35   
36        #region private classes
37        //This class manipulate the instructions of the stack
38        private class InterpreterState
39        {
40            private double[] argumentStack;
41            private int argumentStackPointer;
42            private Instruction[] code;
43            private int pc;
44            public int ProgramCounter
45            {
46                get { return pc; }
47                set { pc = value; }
48            }
49            internal InterpreterState(Instruction[] code, int argumentStackSize)
50            {
51                this.code = code;
52                this.pc = 0;
53                if (argumentStackSize > 0)
54                {
55                    this.argumentStack = new double[argumentStackSize];
56                }
57                this.argumentStackPointer = 0;
58            }
59
60            internal void Reset()
61            {
62                this.pc = 0;
63                this.argumentStackPointer = 0;
64            }
65
66            internal Instruction NextInstruction()
67            {
68                return code[pc++];
69            }
70            private void Push(double val)
71            {
72                argumentStack[argumentStackPointer++] = val;
73            }
74            private double Pop()
75            {
76                return argumentStack[--argumentStackPointer];
77            }
78
79            internal void CreateStackFrame(double[] argValues)
80            {
81                // push in reverse order to make indexing easier
82                for (int i = argValues.Length - 1; i >= 0; i--)
83                {
84                    argumentStack[argumentStackPointer++] = argValues[i];
85                }
86                Push(argValues.Length);
87            }
88
89            internal void RemoveStackFrame()
90            {
91                int size = (int)Pop();
92                argumentStackPointer -= size;
93            }
94
95            internal double GetStackFrameValue(ushort index)
96            {
97                // layout of stack:
98                // [0]   <- argumentStackPointer
99                // [StackFrameSize = N + 1]
100                // [Arg0] <- argumentStackPointer - 2 - 0
101                // [Arg1] <- argumentStackPointer - 2 - 1
102                // [...]
103                // [ArgN] <- argumentStackPointer - 2 - N
104                // <Begin of stack frame>
105                return argumentStack[argumentStackPointer - index - 2];
106            }
107        }
108
109        //Operation codes
110        private class OpCodes
111        {
112            public const byte Add = 1;
113            public const byte Sub = 2;
114            public const byte Mul = 3;
115
116            public const byte GT = 5;
117            public const byte LT = 6;
118
119            public const byte AND = 7;
120            public const byte OR = 8;
121            public const byte NOT = 9;
122            public const byte BOOLEAN = 10;
123
124            public const byte Average = 11;
125            public const byte MACD = 12;
126
127            public const byte Variable = 13;
128            public const byte Constant = 14;
129            public const byte ConstantInt = 16;
130            public const byte BoolConstant = 15;
131            public const byte Max = 17;
132            public const byte Min = 18;
133            public const byte Lag = 19;
134            public const byte RSI = 20;
135            public const byte EMA = 21;
136           
137
138        }
139        #endregion
140
141        #region IStatefulItem
142        public void InitializeState()
143        {
144            EvaluatedSolutions.Value = 0;
145        }
146
147        public void ClearState()
148        {
149        }
150        #endregion
151
152        private Dictionary<Type, byte> symbolToOpcode = new Dictionary<Type, byte>() {
153      { typeof(Addition), OpCodes.Add },
154      { typeof(Subtraction), OpCodes.Sub },
155      { typeof(Multiplication), OpCodes.Mul },
156      { typeof(Constant), OpCodes.Constant },
157      { typeof(BoolConstant), OpCodes.BoolConstant },
158      { typeof(ConstantInt), OpCodes.ConstantInt },
159      { typeof(GreaterThan), OpCodes.GT },
160      { typeof(LessThan), OpCodes.LT },
161      { typeof(And), OpCodes.AND },
162      { typeof(Or), OpCodes.OR },
163      { typeof(Not), OpCodes.NOT},
164      { typeof(AverageTrade), OpCodes.Average},
165      { typeof(MACD), OpCodes.MACD},
166      { typeof(RSI), OpCodes.RSI},
167      { typeof(EMA), OpCodes.EMA},
168      { typeof(Max), OpCodes.Max},
169      { typeof(Min), OpCodes.Min},
170      { typeof(Lag), OpCodes.Lag},
171      { typeof(HeuristicLab.Problems.DataAnalysis.Symbolic.Variable), OpCodes.Variable },
172    };
173
174        public override bool CanChangeName
175        {
176            get { return false; }
177        }
178        public override bool CanChangeDescription
179        {
180            get { return false; }
181        }
182
183        #region parameter properties
184        public IValueParameter<BoolValue> CheckExpressionsWithIntervalArithmeticParameter
185        {
186            get { return (IValueParameter<BoolValue>)Parameters[CheckExpressionsWithIntervalArithmeticParameterName]; }
187        }
188
189        public IValueParameter<IntValue> EvaluatedSolutionsParameter
190        {
191            get { return (IValueParameter<IntValue>)Parameters[EvaluatedSolutionsParameterName]; }
192        }
193        #endregion
194
195        #region properties
196        public BoolValue CheckExpressionsWithIntervalArithmetic
197        {
198            get { return CheckExpressionsWithIntervalArithmeticParameter.Value; }
199            set { CheckExpressionsWithIntervalArithmeticParameter.Value = value; }
200        }
201
202        public IntValue EvaluatedSolutions
203        {
204            get { return EvaluatedSolutionsParameter.Value; }
205            set { EvaluatedSolutionsParameter.Value = value; }
206        }
207        #endregion
208
209
210       
211
212        private double Evaluate(Dataset dataset, ref int row, InterpreterState state)
213        {
214            Instruction currentInstr = state.NextInstruction();
215
216            switch (currentInstr.opCode)
217            {
218                case OpCodes.Add:
219                    {
220                        double s = Evaluate(dataset, ref row, state);
221                        for (int i = 1; i < currentInstr.nArguments; i++)
222                        {
223                            s += Evaluate(dataset, ref row, state);
224                        }
225                        return s;
226                    }
227                case OpCodes.Sub:
228                    {
229                        double s = Evaluate(dataset, ref row, state);
230                        for (int i = 1; i < currentInstr.nArguments; i++)
231                        {
232                            s -= Evaluate(dataset, ref row, state);
233                        }
234                        if (currentInstr.nArguments == 1) s = -s;
235                        return s;
236                    }
237                case OpCodes.Mul:
238                    {
239                        double p = Evaluate(dataset, ref row, state);
240                        for (int i = 1; i < currentInstr.nArguments; i++)
241                        {
242                            p *= Evaluate(dataset, ref row, state);
243                        }
244                        return p;
245                    }
246                case OpCodes.Average:
247                    {
248                        double sum = Evaluate(dataset, ref row, state);
249                        int integerValue = (int) Math.Floor(sum);
250                        if (integerValue > 100) integerValue = 100;
251                        if (row < integerValue)
252                        {
253                            string variableName = dataset.GetValue(row, 2);
254                            double inferiorValue = Convert.ToDouble(variableName);
255                            return inferiorValue/(row+1);
256                        }
257                        else
258                        {
259                            string variableName = dataset.GetValue(row, 2);
260                            double meanValue1 = Convert.ToDouble(variableName);
261                            string variableName2 = dataset.GetValue((row - integerValue), 2);
262                            double meanValue2 = Convert.ToDouble(variableName2);
263                            return (meanValue1 - meanValue2) / integerValue;
264                        }
265                    }
266                case OpCodes.MACD:
267                    {                 
268                        /*double firstMean = Evaluate(dataset, ref row, state);
269                        double secondMean = Evaluate(dataset, ref row, state);
270                        double signal = Evaluate(dataset, ref row, state);
271                        double firstEMA = 0.0;
272                        double secondEMA = 0.0;
273                        double lastFirstEMA = 0.0;
274                        double lastSecondEMA = 0.0;
275                        double macd = 0.0;
276                        double firstFactor = 0.0;
277                        double signalValue = Double.NegativeInfinity;
278                        if (row == (firstMean - 1))
279                        {
280                            string variableName = dataset.GetValue(row, 2);
281                            double meanValue1 = Convert.ToDouble(variableName);
282                            firstEMA = meanValue1 / firstMean;
283                        }
284                        if (row == (secondMean-1))
285                        {
286                            string variableName = dataset.GetValue(row, 2);
287                            double meanValue2 = Convert.ToDouble(variableName);
288                            secondEMA = meanValue2 / secondMean;
289                        }
290                        string variableName2 = dataset.GetValue(row, 1);
291                        double intValue = Convert.ToDouble(variableName2);
292                        if (row > (firstMean-1))
293                        {
294                              firstFactor = 2 / (firstMean + 1);                         
295                              firstEMA = (intValue * firstFactor) + (lastFirstEMA * (1-firstFactor));
296                        }
297                            if (row > (secondMean-1))
298                            {
299                                double secondFactor = 2 / (secondMean + 1);
300                                secondEMA = (intValue * secondFactor) + (lastSecondEMA * (1 - secondFactor));     
301                            }
302                            lastFirstEMA = firstEMA;
303                            lastSecondEMA = secondEMA;
304                            macd = firstEMA - secondEMA;
305                        if ((row < (firstMean-1)) || (row < (secondMean-1))) return 0.0;
306                        else return macd;*/
307                        return 0.0;
308
309                        //macd = firstEMA - secondEMA;
310                        //if (row == Math.Max(firstMean, secondMean)) signalValue = macd;
311                        //if (row > firstMean && row > secondMean)
312                        //{
313                        //    double factor = 2 / (signal + 1);
314                        //    signalValue = (macd * factor) + (lastSignal * (1 - factor));
315                        //}
316                        //if (!Double.IsNegativeInfinity(signalValue)) lastSignal = signalValue;
317                        //return signalValue > macd ? 1.0 : -1.0;
318                    }
319                case OpCodes.AND:
320                    {
321                        double result = Evaluate(dataset, ref row, state);
322                        for (int i = 1; i < currentInstr.nArguments; i++)
323                        {
324                            if (result > 0.0) result = Evaluate(dataset, ref row, state);
325                            else
326                            {
327                                SkipInstructions(state);
328                            }
329                        }
330                        return result > 0.0 ? 1.0 : -1.0;
331                    }
332                case OpCodes.OR:
333                    {
334                        double result = Evaluate(dataset, ref row, state);
335                        for (int i = 1; i < currentInstr.nArguments; i++)
336                        {
337                            if (result <= 0.0) result = Evaluate(dataset, ref row, state);
338                            else
339                            {
340                                SkipInstructions(state);
341                            }
342                        }
343                        return result > 0.0 ? 1.0 : -1.0;
344                    }
345                case OpCodes.NOT:
346                    {
347                        return Evaluate(dataset, ref row, state) > 0.0 ? -1.0 : 1.0;
348                    }
349
350                case OpCodes.BOOLEAN:
351                    {
352                        var booleanTreeNode = currentInstr.dynamicNode as BoolConstantTreeNode;
353                        return booleanTreeNode.Value;
354                    }
355                case OpCodes.GT:
356                    {
357                        double x = Evaluate(dataset, ref row, state);
358                        double y = Evaluate(dataset, ref row, state);
359                        if (x > y) return 1.0;
360                        else return -1.0;
361                    }
362                case OpCodes.LT:
363                    {
364                        double x = Evaluate(dataset, ref row, state);
365                        double y = Evaluate(dataset, ref row, state);
366                        if (x < y) return 1.0;
367                        else return -1.0;
368                    }
369                case OpCodes.Variable:
370                    {
371                        if (row < 0 || row >= dataset.Rows)
372                            return double.NaN;
373                        var variableTreeNode = (VariableTreeNode)currentInstr.dynamicNode;
374                        return ((IList<double>)currentInstr.iArg0)[row] * variableTreeNode.Weight;
375                    }
376                case OpCodes.EMA:
377                    {
378                        //I assume you want to calculate the EMA as defined at http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
379            //for the mean value saved at position 1 of the dataset
380            //and timevalue specifies how many days you want to include in your EMA
381            double timeValue = (int)Evaluate(dataset, ref row, state);
382
383            // with caching
384            // if (lastEmaCache.ContainsKey(currentInst)
385            //   lastEma = lastEmaCache[currentInstr]
386            // else
387            //   lastEma = 1;
388           
389            //get all mean values from row - timeValue up to the actual row
390            double[] meanValues = dataset.GetDoubleValues("insert variable name", Enumerable.Range(row - (int)timeValue, timeValue)).ToArray();
391
392            double EMA = meanValues[0];
393            double factor = 2.0 / (timeValue + 1.0);
394
395            for (int i = 1; i < timeValue; i++) {
396              EMA = meanValues[i] * factor + (1 - factor) * EMA;
397            }
398           
399            // save in cache for next evaluation
400            // lastEmaCache[currentInstr] = EMA;
401           
402            return EMA;
403                    }
404                case OpCodes.Constant:
405                    {
406                        var constTreeNode = currentInstr.dynamicNode as ConstantTreeNode;
407                        return constTreeNode.Value;
408                    }
409                case OpCodes.BoolConstant:
410                    {
411                        var boolConstTreeNode = currentInstr.dynamicNode as BoolConstantTreeNode;
412                        return boolConstTreeNode.Value;
413                    }
414                case OpCodes.ConstantInt:
415                    {
416                        var constIntTreeNode = currentInstr.dynamicNode as ConstantIntTreeNode;
417                        return constIntTreeNode.Value;
418                    }
419                case OpCodes.Max:
420                    {
421                        int n = (int) Evaluate(dataset, ref row, state);
422                        double max = Double.NegativeInfinity;
423                        int i = Math.Min(n,row);
424                        while(i>=0)
425                        {
426                            int position = row - i;
427                            string variableName = dataset.GetValue(position, 1);
428                            double intValue = Convert.ToDouble(variableName);
429                            if (intValue>max) max = intValue;
430                        i--;
431                        }
432                        return max;
433                    }
434                case OpCodes.Min:
435                    {
436                        int n = (int)Evaluate(dataset, ref row, state);
437                        double min = Double.NegativeInfinity;
438                        int i = Math.Min(n, row);
439                        while (i >= 0)
440                        {
441                            int position = row - i;
442                            string variableName = dataset.GetValue(position, 1);
443                            double intValue = Convert.ToDouble(variableName);
444                            if (intValue < min) min = intValue;
445                            i--;
446                        }
447                          return min;
448                    }
449                case OpCodes.Lag:
450                    {
451                        int n = (int) Evaluate(dataset, ref row, state);
452                        if (n>row) return 0;
453                        int position = row - n;
454                        string variableName = dataset.GetValue(position, 1);
455                        double intValue = Convert.ToDouble(variableName);
456                        return intValue;
457                    }
458                case OpCodes.RSI:
459                    {
460                        /*
461                        double numberOfDays = Evaluate(dataset, ref row, state);
462                        double todayRSI=0.0;
463                        if (numberOfDays > row) return 0.0;
464                        else
465                        {
466                            double addUp = 0;
467                            double addDown = 0;
468                            double change=0;
469                            if (row == (numberOfDays))
470                            {
471                                for (int k = 1; k < (numberOfDays+1); k++)
472                                {
473                                    string variableName = dataset.GetValue(k, 1);
474                                    double intValue = Convert.ToDouble(variableName);
475                                    variableName = dataset.GetValue((k - 1), 1);
476                                    double intValue2 = Convert.ToDouble(variableName);
477                                     change = intValue - intValue2;
478                                    if (change > 0) addUp = addUp + change;
479                                    else addDown = addDown - change;
480                                 
481                                }
482                                meanUp = addUp / numberOfDays;
483                                meanDown = addDown / numberOfDays;
484                                if (meanDown != 0) todayRSI = 100 - (100 / (1 + (meanUp / meanDown)));
485                                else todayRSI = 100;
486                             }
487                            else
488                            {
489                                string variableName = dataset.GetValue(row, 1);
490                                double intValue = Convert.ToDouble(variableName);
491                                variableName = dataset.GetValue((row - 1), 1);
492                                double intValue2 = Convert.ToDouble(variableName);
493                                change = intValue - intValue2;
494
495                                addUp = meanUp * (numberOfDays - 1);
496                                addDown = meanDown * (numberOfDays - 1);
497                                if (change > 0) addUp = addUp + change;
498                                else addDown = addDown - change;
499                                meanUp = addUp / numberOfDays;
500                                meanDown = addDown / numberOfDays;
501                                if (meanDown != 0) todayRSI = 100 - (100 / (1 + (meanUp / meanDown)));
502                                else todayRSI = 100;
503                            }
504                        }
505                        if ((lastRSI < 70) && (todayRSI >= 70)) inOut = 1;
506                        else if((lastRSI > 30) && (todayRSI <= 30)) inOut=-1;
507                        lastRSI = todayRSI;*/
508                        return 0.0;
509                       
510                    }
511               
512                default: throw new NotSupportedException();
513            }
514        }
515
516        private byte MapSymbolToOpCode(ISymbolicExpressionTreeNode treeNode)
517        {
518            if (symbolToOpcode.ContainsKey(treeNode.Symbol.GetType()))
519                return symbolToOpcode[treeNode.Symbol.GetType()];
520            else
521                throw new NotSupportedException("Symbol: " + treeNode.Symbol);
522        }
523
524        // skips a whole branch
525        private void SkipInstructions(InterpreterState state)
526        {
527            int i = 1;
528            while (i > 0)
529            {
530                i += state.NextInstruction().nArguments;
531                i--;
532            }
533        }
534        [StorableConstructor]
535        private Interpreter(bool deserializing) : base(deserializing) { }
536        private Interpreter(Interpreter original, Cloner cloner) : base(original, cloner) { }
537        public override IDeepCloneable Clone(Cloner cloner)
538        {
539            return new Interpreter(this, cloner);
540        }
541
542        public Interpreter()
543            : base("SymbolicDataAnalysisExpressionTreeInterpreter", "Interpreter for symbolic expression trees including automatically defined functions.")
544        {
545            Parameters.Add(new ValueParameter<BoolValue>(CheckExpressionsWithIntervalArithmeticParameterName, "Switch that determines if the interpreter checks the validity of expressions with interval arithmetic before evaluating the expression.", new BoolValue(false)));
546            Parameters.Add(new ValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
547        }
548
549        [StorableHook(HookType.AfterDeserialization)]
550        private void AfterDeserialization()
551        {
552            if (!Parameters.ContainsKey(EvaluatedSolutionsParameterName))
553                Parameters.Add(new ValueParameter<IntValue>(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the interpreter has evaluated", new IntValue(0)));
554        }
555       
556        //Take the symbolic expression values of the tree
557        public IEnumerable<double> GetSymbolicExpressionTreeValues(ISymbolicExpressionTree tree, Dataset dataset, IEnumerable<int> rows)
558        {
559            if (CheckExpressionsWithIntervalArithmetic.Value)
560                throw new NotSupportedException("Interval arithmetic is not yet supported in the symbolic data analysis interpreter.");
561            EvaluatedSolutions.Value++; // increment the evaluated solutions counter
562            var compiler = new SymbolicExpressionTreeCompiler();
563            Instruction[] code = compiler.Compile(tree, MapSymbolToOpCode);//Take the type of symbol
564            int necessaryArgStackSize = 0;
565            for (int i = 0; i < code.Length; i++)
566            {
567                Instruction instr = code[i];
568                if (instr.opCode == OpCodes.Variable)
569                {
570                    var variableTreeNode = instr.dynamicNode as VariableTreeNode;
571                    instr.iArg0 = dataset.GetReadOnlyDoubleValues(variableTreeNode.VariableName);
572                    code[i] = instr;
573                }
574                else if (instr.opCode == OpCodes.EMA)
575                {
576                    instr.iArg0 = EMAValue;
577                }
578            }
579     
580      // with caching
581      // if (lastEmaCache == null) lastEmaCache = new Dictionary<Instruction, double>();
582      // else lastEmaCache.Clear();
583     
584            var state = new InterpreterState(code, necessaryArgStackSize);
585            //Evaluate each row of the datase
586            foreach (var rowEnum in rows)
587            {
588                int row = rowEnum;
589                state.Reset();
590                yield return Evaluate(dataset, ref row, state);
591            }
592        }
593
594    }
595}
Note: See TracBrowser for help on using the repository browser.