Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / Formulas / TechGeneralIndicators.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               TechGeneralIndicators.cs
9 //
10 //  Namespace:  System.Web.UI.WebControls[Windows.Forms].Charting.Formulas
11 //
12 //      Classes:        TechGeneralIndicators
13 //
14 //  Purpose:    This class is used for calculations of 
15 //                              general technical analyses indicators.
16 //
17 //      Reviewed:       GS - August 7, 2002
18 //                              AG - August 7, 2002
19 //
20 //===================================================================
21
22
23 using System;
24 using System.Globalization;
25
26
27
28 #if Microsoft_CONTROL
29         namespace System.Windows.Forms.DataVisualization.Charting.Formulas
30 #else
31         namespace System.Web.UI.DataVisualization.Charting.Formulas
32 #endif
33 {
34         /// <summary>
35         /// This class is used for calculations of general 
36         /// technical analyses indicators.
37         /// </summary>
38         internal class GeneralTechnicalIndicators : PriceIndicators
39         {
40                 #region Properties
41
42                 /// <summary>
43                 /// Formula Module name
44                 /// </summary>
45         override public string Name { get { return SR.FormulaNameGeneralTechnicalIndicators; } }
46
47                 #endregion
48
49                 #region Formulas
50
51                 /// <summary>
52                 /// Standard Deviation is a statistical measure of volatility. 
53                 /// Standard Deviation is typically used as a component of 
54                 /// other indicators, rather than as a stand-alone indicator. 
55                 /// For example, Bollinger Bands are calculated by adding 
56                 /// a security's Standard Deviation to a moving average. 
57                 /// High Standard Deviation values occur when the data item 
58                 /// being analyzed (e.g., prices or an indicator) is changing 
59                 /// dramatically. Similarly, low Standard Deviation values 
60                 /// occur when prices are stable.
61                 /// ---------------------------------------------------------
62                 /// Input: 
63                 ///             - 1 Y value.
64                 /// Output: 
65                 ///             - 1 Y value Standard Deviation
66                 /// Parameters: 
67                 ///             - Periods for standard deviation ( used for moving average )
68                 ///     Extra Parameters: 
69                 ///             -
70                 /// </summary>
71                 /// <param name="inputValues">Arrays of doubles - Input values</param>
72                 /// <param name="outputValues">Arrays of doubles - Output values</param>
73                 /// <param name="parameterList">Array of strings - Parameters</param>
74                 /// <param name="extraParameterList">Array of strings - Extra parameters</param>
75                 private void StandardDeviation(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
76                 {
77                         int length = inputValues.Length;
78
79                         // Period for standard deviation ( used for moving average )
80                         int period;
81                         try
82                         {period = int.Parse( parameterList[0], System.Globalization.CultureInfo.InvariantCulture );}
83                         catch( Exception e )
84                         {
85                 if (e.Message == SR.ExceptionObjectReferenceIsNull)
86                     throw new InvalidOperationException(SR.ExceptionPriceIndicatorsPeriodMissing);
87                                 else
88                     throw new InvalidOperationException(SR.ExceptionPriceIndicatorsPeriodMissing + e.Message);
89                         }
90
91                         if( period <= 0 )
92                 throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
93
94                         // Starting average from the first data point or after period.
95                         bool startFromFirst = bool.Parse( extraParameterList[0] );
96
97                         // There is no enough series
98                         if( length != 2 )
99                 throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
100
101                         // Different number of x and y values
102                         if( inputValues[0].Length != inputValues[1].Length )
103                 throw new ArgumentException(SR.ExceptionPriceIndicatorsSameXYNumber);
104
105                         // Not enough values for moving average in Standard deviation.
106                         if( inputValues[0].Length < period )
107                 throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
108
109                         outputValues = new double [2][];
110
111                         StandardDeviation( inputValues[1], out outputValues[1], period, startFromFirst );
112
113                         // Set X values
114                         outputValues[0] = new double [outputValues[1].Length];
115                         for( int index = 0; index < outputValues[1].Length; index++ )
116                         {
117                                 if( startFromFirst )
118                                         outputValues[0][index] = inputValues[0][index];
119                                 else
120                                         outputValues[0][index] = inputValues[0][index+period-1];
121                         }
122                 }
123
124                 /// <summary>
125                 /// The Average True Range ("ATR") is a measure of volatility. It was introduced 
126                 /// by Welles Wilder in his book, New Concepts in Technical Trading Systems, and 
127                 /// has since been used as a component of many indicators and trading systems. Wilder 
128                 /// has found that high ATR values often occur at market bottoms following a "panic" 
129                 /// sell-off. Low Average True Range values are often found during extended sideways 
130                 /// periods, such as those found at tops and after consolidation periods. The Average 
131                 /// True Range can be interpreted using the same techniques that are used with 
132                 /// the other volatility indicators.
133                 /// ---------------------------------------------------------
134                 /// Input: 
135                 ///             - 3 Y values ( High, Low, Close ).
136                 /// Output: 
137                 ///             - 1 Y value AverageTrueRange
138                 /// Parameters: 
139                 ///             - Periods (Default 14) = is used to configure the number of periods to calculate the ATR
140                 /// </summary>
141                 /// <param name="inputValues">Arrays of doubles - Input values</param>
142                 /// <param name="outputValues">Arrays of doubles - Output values</param>
143                 /// <param name="parameterList">Array of strings - Parameters</param>
144                 private void AverageTrueRange(double [][] inputValues, out double [][] outputValues, string [] parameterList)
145                 {
146                         // There is no enough input series
147                         if( inputValues.Length != 4 )
148                                 throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
149                         
150                         // Different number of x and y values
151                         CheckNumOfValues( inputValues, 3 );
152                                                 
153                         // Period
154                         int period;
155             if (parameterList.Length < 1 || 
156                 !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
157             {
158                 period = 14;
159             }
160
161                         if( period <= 0 )
162                 throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
163
164                         // The distance from today's high to today's low
165                         double distanceOne;
166
167                         // The distance from yesterday's close to today's high
168                         double distanceTwo;
169
170                         // The distance from yesterday's close to today's low
171                         double distanceTree;
172
173                         double [] trueRange = new double [inputValues[0].Length - 1];
174
175                         // True Range
176                         for( int index = 1; index < inputValues[0].Length; index++ )
177                         {
178                                 // The distance from today's high to today's low
179                                 distanceOne = Math.Abs( inputValues[1][index] - inputValues[2][index] );
180
181                                 // The distance from yesterday's close to today's high
182                                 distanceTwo = Math.Abs( inputValues[3][index-1] - inputValues[1][index] );
183
184                                 // The distance from yesterday's close to today's low
185                                 distanceTree = Math.Abs( inputValues[3][index-1] - inputValues[2][index] );
186
187                                 // True Range
188                                 trueRange[index-1] = Math.Max( Math.Max( distanceOne, distanceTwo ), distanceTree );
189                         }
190
191                         outputValues = new double [2][];
192
193                         outputValues[0] = new double [inputValues[0].Length-period];
194
195                         // Moving average of true range
196                         MovingAverage( trueRange, out outputValues[1], period, false );
197
198                         // Set X values
199                         for( int index = period; index < inputValues[0].Length; index++ )
200                         {
201                                 outputValues[0][index-period] = inputValues[0][index];
202                         }
203                 }
204
205                 /// <summary>
206                 /// The Ease of Movement indicator shows the relationship between volume and price 
207                 /// change. This indicator shows how much volume is required to move prices. The Ease 
208                 /// of Movement indicator was developed Richard W. Arms, Jr., the creator of Equivolume.
209                 /// High Ease of Movement values occur when prices are moving upward on lightStyle volume. 
210                 /// Low Ease of Movement values occur when prices are moving downward on lightStyle volume. 
211                 /// If prices are not moving, or if heavy volume is required to move prices, then 
212                 /// indicator will also be near zero.
213                 /// ---------------------------------------------------------
214                 /// Input: 
215                 ///             - 3 Y values ( High, Low, Volume ).
216                 /// Output: 
217                 ///             - 1 Y value Ease Of Movement
218                 /// </summary>
219                 /// <param name="inputValues">Arrays of doubles - Input values</param>
220                 /// <param name="outputValues">Arrays of doubles - Output values</param>
221                 private void EaseOfMovement(double [][] inputValues, out double [][] outputValues)
222                 {
223                         // There is no enough input series
224                         if( inputValues.Length != 4 )
225                                 throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
226                         
227                         // Different number of x and y values
228                         CheckNumOfValues( inputValues, 3 );
229                                                                         
230                         double MidPointMove;
231                         double BoxRattio;
232
233                         outputValues = new double [2][];
234
235                         outputValues[0] = new double [inputValues[0].Length - 1];
236                         outputValues[1] = new double [inputValues[0].Length - 1];
237
238                         // Ease Of Movement
239                         for( int index = 1; index < inputValues[0].Length; index++ )
240                         {
241                                 // Set X values
242                                 outputValues[0][index - 1] = inputValues[0][index];
243
244                                 // Calculate the Mid-point Move for each day:                          
245                                 MidPointMove = ( inputValues[1][index] + inputValues[2][index] ) / 2 - ( inputValues[1][index - 1] + inputValues[2][index - 1] ) / 2;
246
247                                 // The Box Ratio determines the ratio between height and width of the Equivolume box:    
248                                 BoxRattio = ( inputValues[3][index] ) / (( inputValues[1][index] - inputValues[2][index] ) );
249
250                                 // Ease of Movement is then calculated as:
251                                 outputValues[1][index - 1] = MidPointMove / BoxRattio;
252                         }
253                 }
254
255                 /// <summary>
256                 /// The Mass Index was designed to identify trend reversals by measuring the narrowing 
257                 /// and widening of the range between the high and low prices. As this range widens, the 
258                 /// Mass Index increases; as the range narrows the Mass Index decreases.
259                 /// The Mass Index was developed by Donald Dorsey. According to Mr. Dorsey, the most 
260                 /// significant pattern to watch for is a "reversal bulge." A reversal bulge occurs when 
261                 /// a 25-period Mass Index rises above 27.0 and subsequently falls below 26.5. A reversal 
262                 /// in price is then likely. The overall price trend (i.e., trending or trading range) 
263                 /// is unimportant.
264                 /// ---------------------------------------------------------
265                 /// Input: 
266                 ///             - 2 Y values ( High, Low ).
267                 /// Output: 
268                 ///             - 1 Y value Mass Index
269                 /// Parameters: 
270                 ///             - Period = is used to calculate the accumulation, By default this property is set to 25.
271                 ///             - AveragePeriod = is used to calculate Simple Moving Avg, By default this property is set to 9.
272                 /// </summary>
273                 /// <param name="inputValues">Arrays of doubles - Input values</param>
274                 /// <param name="outputValues">Arrays of doubles - Output values</param>
275                 /// <param name="parameterList">Array of strings - Parameters</param>
276                 private void MassIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
277                 {
278                         // There is no enough input series
279                         if( inputValues.Length != 3 )
280                                 throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays);
281                         
282                         // Different number of x and y values
283                         CheckNumOfValues( inputValues, 2 );
284                         
285                         // Period
286                         int period;
287             if (parameterList.Length < 1 || 
288                 !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
289             {
290                 period = 25;
291             }
292
293                         if( period <= 0 )
294                 throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
295
296                         // Average Period
297                         int averagePeriod;
298             if (parameterList.Length < 2 || 
299                 !int.TryParse(parameterList[1], NumberStyles.Any, CultureInfo.InvariantCulture, out averagePeriod))
300             {
301                 averagePeriod = 9;
302             }
303
304                         if( period <= 0 )
305                 throw new InvalidOperationException(SR.ExceptionPeriodAverageParameterIsNegative);
306                                                 
307                         double [] highLow = new double [inputValues[0].Length];
308                         double [] average;
309                         double [] secondAverage;
310
311                         for( int index = 0; index < inputValues[0].Length; index++ )
312                         {
313                                 highLow[index] = inputValues[1][index] - inputValues[2][index];
314                         }
315
316                         // Find exponential moving average
317                         ExponentialMovingAverage( highLow, out average, averagePeriod, false );
318
319                         // Find exponential moving average of exponential moving average
320                         ExponentialMovingAverage( average, out secondAverage, averagePeriod, false );
321
322                         outputValues = new double [2][];
323
324                         outputValues[0] = new double [secondAverage.Length - period + 1];
325                         outputValues[1] = new double [secondAverage.Length - period + 1];
326
327                         // Mass Index
328                         int outIndex = 0;
329                         double sum = 0;
330                         for( int index = 2 * averagePeriod - 3 + period; index < inputValues[0].Length; index++ )
331                         {
332                                 // Set X values
333                                 outputValues[0][outIndex] = inputValues[0][index];
334
335                                 sum = 0;
336                                 for( int indexSum = index - period + 1; indexSum <= index; indexSum++ )
337                                 {
338                                         sum += average[indexSum - averagePeriod + 1] / secondAverage[indexSum - 2 * averagePeriod + 2];
339                                 }
340
341                                 // Set Y values
342                                 outputValues[1][outIndex] = sum;
343
344                                 outIndex++;
345                         }
346                 }
347
348                 /// <summary>
349                 /// The Performance indicator displays a security's price performance as 
350                 /// a percentage. This is sometimes called a "normalized" chart. The 
351                 /// Performance indicator displays the percentage that the security 
352                 /// has increased since the first period displayed. For example, if 
353                 /// the Performance indicator is 10, it means that the security's 
354                 /// price has increased 10% since the first period displayed on the 
355                 /// left side of the chart. Similarly, a value of -10% means that 
356                 /// the security's price has fallen by 10% since the first period 
357                 /// displayed.
358                 /// ---------------------------------------------------------
359                 /// Input: 
360                 ///             - 1 Y value ( Close ).
361                 /// Output: 
362                 ///             - 1 Y value Performance
363                 /// </summary>
364                 /// <param name="inputValues">Arrays of doubles - Input values</param>
365                 /// <param name="outputValues">Arrays of doubles - Output values</param>
366                 private void Performance(double [][] inputValues, out double [][] outputValues)
367                 {
368                         // There is no enough input series
369                         if( inputValues.Length != 2 )
370                 throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
371                         
372                         // Different number of x and y values
373                         CheckNumOfValues( inputValues, 1 );
374                         
375                         outputValues = new double [2][];
376
377                         outputValues[0] = new double [inputValues[0].Length];
378                         outputValues[1] = new double [inputValues[0].Length];
379                                                 
380                         // Performance indicator
381                         for( int index = 0; index < inputValues[0].Length; index++ )
382                         {
383                                 // Set X values
384                                 outputValues[0][index] = inputValues[0][index];
385
386                                 // Set Y values
387                                 outputValues[1][index] = ( inputValues[1][index] - inputValues[1][0] ) / inputValues[1][0] * 100;
388                         }
389                 }
390
391                 /// <summary>
392                 /// Rate of Change is used to monitor momentum by making direct comparisons between current 
393                 /// and past prices on a continual basis. The results can be used to determine the strength 
394                 /// of price trends. Note: This study is the same as the Momentum except that Momentum uses 
395                 /// subtraction in its calculations while Rate of Change uses division. The resulting lines 
396                 /// of these two studies operated over the same data will look exactly the same - only the 
397                 /// scale values will differ. The Price Rate-of-Change ("----") indicator displays the 
398                 /// difference between the current price and the price x-time periods ago. The difference 
399                 /// can be displayed in either points or as a percentage. The Momentum indicator displays 
400                 /// the same information, but expresses it as a ratio. When the Rate-of-Change displays 
401                 /// the price change in points, it subtracts the price x-time periods ago from today\92s price.
402                 /// When the Rate-of-Change displays the price change as a percentage, it divides 
403                 /// the price change by price x-time period\92s ago.
404                 /// ---------------------------------------------------------
405                 /// Input: 
406                 ///             - 1 Y value ( Close ).
407                 /// Output: 
408                 ///             - 1 Y value Rate of Change
409                 /// Parameters: 
410                 ///             - Periods = is used to configure the number of periods to calculate the rate of Change. By default the Periods property is set to 10.
411                 /// </summary>
412                 /// <param name="inputValues">Arrays of doubles - Input values</param>
413                 /// <param name="outputValues">Arrays of doubles - Output values</param>
414                 /// <param name="parameterList">Array of strings - Parameters</param>
415                 private void RateOfChange(double [][] inputValues, out double [][] outputValues, string [] parameterList)
416                 {
417                         // There is no enough input series
418                         if( inputValues.Length != 2 )
419                 throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
420                         
421                         // Different number of x and y values
422                         CheckNumOfValues( inputValues, 1 );
423                         
424                         // Period
425                         int period;
426             if (parameterList.Length < 1 || 
427                 !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
428             {
429                 period = 10;
430             }
431
432                         if( period <= 0 )
433                 throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
434
435                         outputValues = new double [2][];
436
437                         outputValues[0] = new double [inputValues[0].Length - period];
438                         outputValues[1] = new double [inputValues[0].Length - period];
439                                                 
440                         // Rate Of Change
441                         for( int index = period; index < inputValues[0].Length; index++ )
442                         {
443                                 // Set X values
444                                 outputValues[0][index - period] = inputValues[0][index];
445
446                                 // Set Y values
447                                 outputValues[1][index - period] = ( inputValues[1][index] - inputValues[1][index - period] ) / inputValues[1][index - period] * 100;
448                         }
449                 }
450
451                 /// <summary>
452                 /// This indicator was developed by Welles Wilder Jr. Relative Strength is often 
453                 /// used to identify price tops and bottoms by keying on specific levels 
454                 /// (usually "30" and "70") on the RSI chart which is scaled from from 0-100. 
455                 /// The study is also useful to detect the following: 
456                 ///             - Movement which might not be as readily apparent on the bar chart
457                 ///             - Failure swings above 70 or below 30 which can warn of coming reversals
458                 ///             - Support and resistance levels 
459                 ///             - Divergence between the RSI and price which is often a useful reversal indicator 
460                 /// ---------------------------------------------------------
461                 /// Input: 
462                 ///             - 1 Y value ( Close ).
463                 /// Output: 
464                 ///             - 1 Y value RelativeStrengthIndex 
465                 /// Parameters: 
466                 ///             - Periods = is used to configure the number of periods to calculate the RSI indicator. By default the Periods property is set to 10.
467                 /// </summary>
468                 /// <param name="inputValues">Arrays of doubles - Input values</param>
469                 /// <param name="outputValues">Arrays of doubles - Output values</param>
470                 /// <param name="parameterList">Array of strings - Parameters</param>
471                 private void RelativeStrengthIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
472                 {
473                         // There is no enough input series
474                         if( inputValues.Length != 2 )
475                 throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
476                         
477                         // Different number of x and y values
478                         CheckNumOfValues( inputValues, 1 );
479                         
480                         // Period
481                         int period;
482             if (parameterList.Length < 1 || 
483                 !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
484             {
485                 period = 10;
486             }
487
488                         if( period <= 0 )
489                 throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
490                         
491                         double [] upward = new double[inputValues[0].Length-1];
492                         double [] downward = new double[inputValues[0].Length-1];
493
494                         for( int index = 1; index < inputValues[0].Length; index++ )
495                         {
496                                 // Upward - price is going up
497                                 if( inputValues[1][index - 1] < inputValues[1][index] )
498                                 {
499                                         upward[index-1] = inputValues[1][index] - inputValues[1][index - 1];
500                                         downward[index-1] = 0.0;
501                                 }
502                                 // Downward - price is going down
503                                 if( inputValues[1][index - 1] > inputValues[1][index] )
504                                 {
505                                         upward[index-1] = 0.0;
506                                         downward[index-1] = inputValues[1][index - 1] - inputValues[1][index];
507                                 }
508                         }
509
510                         double [] averageUpward = new double[inputValues[0].Length];
511                         double [] averageDownward = new double[inputValues[0].Length];
512
513                         ExponentialMovingAverage(downward, out averageDownward, period, false );
514                         ExponentialMovingAverage(upward, out averageUpward, period, false );
515
516                         outputValues = new double [2][];
517
518                         outputValues[0] = new double [averageDownward.Length];
519                         outputValues[1] = new double [averageDownward.Length];
520
521                         // Find RSI
522                         for( int index = 0; index < averageDownward.Length; index++ )
523                         {
524                                 // Set X values
525                                 outputValues[0][index] = inputValues[0][index + period];
526
527                                 // Calculate the Relative Strength Index (RSI):
528                                 outputValues[1][index] = 100 - 100 / ( 1 + averageUpward[index] / averageDownward[index] ); 
529                         }
530                 }
531
532                 /// <summary>
533                 /// TripleExponentialMovingAverage is a momentum indicator that displays the percent rate-of-change of a triple 
534                 /// exponentially smoothed moving average of the security's closing price. It is designed 
535                 /// to keep you in trends equal to or shorter than the number of periods you specify. 
536                 /// The TripleExponentialMovingAverage indicator oscillates around a zero line. Its triple exponential smoothing is 
537                 /// designed to filter out "insignificant" cycles (i.e., those that are shorter than 
538                 /// the number of periods you specify). Trades should be placed when the indicator changes 
539                 /// direction (i.e., buy when it turns up and sell when it turns down). You may want to 
540                 /// plot a 9-period moving average of the TripleExponentialMovingAverage to create a "signal" line (similar to the 
541                 /// MovingAverageConvergenceDivergence indicator, and then buy when the TripleExponentialMovingAverage rises above its signal, and sell when it 
542                 /// falls below its signal. Divergences between the security and the TripleExponentialMovingAverage can also help 
543                 /// identify turning points.
544                 /// ---------------------------------------------------------
545                 /// Input: 
546                 ///             - 1 Y values ( Close ).
547                 /// Output: 
548                 ///             - 1 Y value ( TripleExponentialMovingAverage ).
549                 /// Parameters: 
550                 ///             - Period = is used to calculate the Exponential Moving Avg, By default this property is set to 12.
551                 /// </summary>
552                 /// <param name="inputValues">Arrays of doubles - Input values</param>
553                 /// <param name="outputValues">Arrays of doubles - Output values</param>
554                 /// <param name="parameterList">Array of strings - Parameters</param>
555                 private void Trix(double [][] inputValues, out double [][] outputValues, string [] parameterList)
556                 {
557                         // There is no enough input series
558                         if( inputValues.Length != 2 )
559                 throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
560                         
561                         // Different number of x and y values
562                         CheckNumOfValues( inputValues, 1 );
563                                                 
564                         // Period
565                         int period;
566             if (parameterList.Length < 1 || 
567                 !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
568             {
569                 period = 12;
570             }
571
572                         if( period <= 0 )
573                 throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
574                         
575                         double [] exp1; // Exponential Moving average of input values
576                         double [] exp2; // Exponential Moving average of exp1
577                         double [] exp3; // Exponential Moving average of exp2
578
579                         // Find exponential moving average
580                         ExponentialMovingAverage( inputValues[1], out exp1, period, false );
581
582                         // Find exponential moving average
583                         ExponentialMovingAverage( exp1, out exp2, period, false );
584
585                         // Find exponential moving average
586                         ExponentialMovingAverage( exp2, out exp3, period, false );
587
588                         outputValues = new double [2][];
589
590                         outputValues[0] = new double [inputValues[0].Length - period * 3 + 2];
591                         outputValues[1] = new double [inputValues[0].Length - period * 3 + 2];
592
593                         // Calculate TripleExponentialMovingAverage
594                         int outIndex = 0;
595                         for( int index = period * 3 - 2; index < inputValues[0].Length; index++ )
596                         {
597                                 // set X value
598                                 outputValues[0][outIndex] = inputValues[0][index];
599
600                                 // set Y value
601                                 outputValues[1][outIndex] = ( exp3[outIndex+1] - exp3[outIndex] ) / exp3[outIndex];
602
603                                 outIndex++;
604                         }
605                 }
606
607                 /// <summary>
608                 /// The MovingAverageConvergenceDivergence is used to determine overbought or oversold conditions in the market. Written 
609                 /// for stocks and stock indices, MovingAverageConvergenceDivergence can be used for commodities as well. The MovingAverageConvergenceDivergence line 
610                 /// is the difference between the long and short exponential moving averages of the chosen 
611                 /// item. The signal line is an exponential moving average of the MovingAverageConvergenceDivergence line. Signals are 
612                 /// generated by the relationship of the two lines. As with RSI and Stochastics, 
613                 /// divergences between the MovingAverageConvergenceDivergence and prices may indicate an upcoming trend reversal. The MovingAverageConvergenceDivergence  
614                 /// is a trend following momentum indicator that shows the relationship between two 
615                 /// moving averages of prices. The MovingAverageConvergenceDivergence is the difference between a 26-day and 12-day 
616                 /// exponential moving average. A 9-day exponential moving average, called the "signal" 
617                 /// (or "trigger") line is plotted on top of the MovingAverageConvergenceDivergence to show buy/sell opportunities. The 
618                 /// MovingAverageConvergenceDivergence is calculated by subtracting the value of a 26-day exponential moving average 
619                 /// from a 12-day exponential moving average. A 9-day dotted exponential moving average of 
620                 /// the MovingAverageConvergenceDivergence (the "signal" line) is then plotted on top of the MovingAverageConvergenceDivergence.
621                 /// ---------------------------------------------------------
622                 /// Input: 
623                 ///             - 1 Y value ( Close ).
624                 /// Output: 
625                 ///             - 1 Y value ( MovingAverageConvergenceDivergence ).
626                 /// Parameters: 
627                 ///             - ShortPeriod = is used to configure the short Exponential Moving Average, By default this property is set to 12.
628                 ///             - LongPeriod = is used to configure the Int64 Exponential Moving Average, By default this property is set to 26.
629                 /// </summary>
630                 /// <param name="inputValues">Arrays of doubles - Input values</param>
631                 /// <param name="outputValues">Arrays of doubles - Output values</param>
632                 /// <param name="parameterList">Array of strings - Parameters</param>
633                 private void Macd(double [][] inputValues, out double [][] outputValues, string [] parameterList)
634                 {
635                         // There is no enough input series
636                         if( inputValues.Length != 2 )
637                 throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray);
638                         
639                         // Different number of x and y values
640                         CheckNumOfValues( inputValues, 1 );
641                                                                         
642                         // Short Period
643                         int shortPeriod;
644             if (parameterList.Length < 1 || 
645                 !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out shortPeriod))
646             {
647                 shortPeriod = 12;
648             }
649
650                         if( shortPeriod <= 0 )
651                 throw new InvalidOperationException(SR.ExceptionPeriodShortParameterIsNegative);
652
653                         // Int64 Period
654                         int longPeriod;
655             if (parameterList.Length < 2 || 
656                 !int.TryParse(parameterList[1], NumberStyles.Any, CultureInfo.InvariantCulture, out longPeriod))
657             {
658                 longPeriod = 26;
659             }
660
661                         if( longPeriod <= 0 )
662                 throw new InvalidOperationException(SR.ExceptionPeriodLongParameterIsNegative);
663
664                         if( longPeriod <= shortPeriod )
665                 throw new InvalidOperationException(SR.ExceptionIndicatorsLongPeriodLessThenShortPeriod);
666                         
667                         double [] longAverage; // Int64 Average
668                         double [] shortAverage; // Short Average
669                         
670                         // Find Int64 exponential moving average
671                         ExponentialMovingAverage( inputValues[1], out longAverage, longPeriod, false );
672
673                         // Find Short exponential moving average
674                         ExponentialMovingAverage( inputValues[1], out shortAverage, shortPeriod, false );
675                         
676                         outputValues = new double [2][];
677
678                         outputValues[0] = new double [inputValues[0].Length - longPeriod + 1];
679                         outputValues[1] = new double [inputValues[0].Length - longPeriod + 1];
680                         
681                         // Calculate MovingAverageConvergenceDivergence
682                         int outIndex = 0;
683                         for( int index = longPeriod - 1; index < inputValues[0].Length; index++ )
684                         {
685                                 // set X value
686                                 outputValues[0][outIndex] = inputValues[0][index];
687
688                                 // set Y value
689                                 outputValues[1][outIndex] = shortAverage[ outIndex + longPeriod - shortPeriod ] - longAverage[outIndex];
690
691                                 outIndex++;
692                         }
693                 }
694
695                 /// <summary>
696                 /// The CCI is a timing system that is best applied to commodity contracts which 
697                 /// have cyclical or seasonal tendencies. CCI does not determine the length of 
698                 /// cycles - it is designed to detect when such cycles begin and end through 
699                 /// the use of a statistical analysis which incorporates a moving average and a divisor 
700                 /// reflecting both the possible and actual trading ranges. Although developed primarily 
701                 /// for commodities, the CCI could conceivably be used to analyze stocks as well. The 
702                 /// Commodity Channel Index ("CCI") measures the variation of a security\92s price from 
703                 /// its statistical mean. High values show that prices are unusually high compared to 
704                 /// average prices whereas low values indicate that prices are unusually low. 
705                 /// 1. Calculate today's Typical Price (TP) = (H+L+C)/3 where H = high; L = low, and C = close. 
706                 /// 2. Calculate today's 20-day Simple Moving Average of the Typical Price (SMATP). 
707                 /// 3. Calculate today's Mean Deviation. First, calculate the absolute value of the difference 
708                 ///    between today's SMATP and the typical price for each of the past 20 days. 
709                 ///    Add all of these absolute values together and divide by 20 to find the Mean Deviation. 
710                 /// 4. The final step is to apply the Typical Price (TP), the Simple Moving Average of the 
711                 ///    Typical Price (SMATP), the Mean Deviation and a Constant (.015).
712                 /// ---------------------------------------------------------
713                 /// Input: 
714                 ///             - 3 Y values ( Hi, Low, Close ).
715                 /// Output: 
716                 ///             - 1 Y value ( CCI ).
717                 /// Parameters: 
718                 ///             - Periods = is used to configure the number of periods to calculate the CCI. By default the Periods property is set to 10.
719                 /// </summary>
720                 /// <param name="inputValues">Arrays of doubles - Input values</param>
721                 /// <param name="outputValues">Arrays of doubles - Output values</param>
722                 /// <param name="parameterList">Array of strings - Parameters</param>
723                 private void CommodityChannelIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
724                 {
725                         // There is no enough input series
726                         if( inputValues.Length != 4 )
727                                 throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
728                         
729                         // Different number of x and y values
730                         CheckNumOfValues( inputValues, 3 );
731                                                                         
732                         // Period
733                         int period;
734             if (parameterList.Length < 1 || 
735                 !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period))
736             {
737                 period = 10;
738             }
739
740                         if( period <= 0 )
741                 throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative);
742
743                         // Typical Price
744                         double [] typicalPrice = new double[inputValues[0].Length];
745                         
746                         // Typical Price loop
747                         for( int index = 0; index < inputValues[0].Length; index++ )
748                         {
749                                 typicalPrice[index] = ( inputValues[1][index] + inputValues[2][index] + inputValues[3][index] ) / 3.0;
750                         }
751
752                         // Moving Average
753                         double [] movingAverage;
754                                                 
755                         // Simple Moving Average of the Typical Price 
756                         MovingAverage( typicalPrice, out movingAverage, period, false );
757
758                         // Calculate today's Mean Deviation. First, calculate the absolute value 
759                         // of the difference between today's SMATP and the typical price for each 
760                         // of the past 20 days. Add all of these absolute values together and 
761                         // divide by 20 to find the Mean Deviation. 
762
763                         // Mean Deviation
764                         double [] meanDeviation = new double[movingAverage.Length];
765
766                         double sum =0;
767                         for( int index = 0; index < movingAverage.Length; index++ )
768                         {
769                                 sum = 0;
770                                 for( int indexSum = index; indexSum < index + period; indexSum++ )
771                                 {
772                                         sum += Math.Abs( movingAverage[index] - typicalPrice[indexSum] );
773                                 }
774                                 meanDeviation[index] = sum / period;
775                         }
776
777                         outputValues = new double [2][];
778
779                         outputValues[0] = new double [meanDeviation.Length];
780                         outputValues[1] = new double [meanDeviation.Length];
781                         
782
783                         for( int index = 0; index < meanDeviation.Length; index++ )
784                         {
785                                 // Set X values
786                                 outputValues[0][index] = inputValues[0][index + period - 1];
787
788                                 // Set Y values
789                                 outputValues[1][index] = ( typicalPrice[index + period - 1] - movingAverage[index] ) / ( 0.015 * meanDeviation[index] );
790                                 
791                         }
792                 }
793
794                 #endregion
795
796                 #region Methods
797
798                 /// <summary>
799                 /// Default constructor
800                 /// </summary>
801                 public GeneralTechnicalIndicators()
802                 {
803                 }
804
805         /// <summary>
806         /// The first method in the module, which converts a formula 
807         /// name to the corresponding private method.
808         /// </summary>
809         /// <param name="formulaName">String which represent a formula name</param>
810         /// <param name="inputValues">Arrays of doubles - Input values</param>
811         /// <param name="outputValues">Arrays of doubles - Output values</param>
812         /// <param name="parameterList">Array of strings - Formula parameters</param>
813         /// <param name="extraParameterList">Array of strings - Extra Formula parameters from DataManipulator object</param>
814         /// <param name="outLabels">Array of strings - Used for Labels. Description for output results.</param>
815                 override public void Formula( string formulaName, double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList, out string [][] outLabels )
816                 {
817                         string name;
818                         outputValues = null;
819
820                         name = formulaName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
821
822                         // Not used for these formulas.
823                         outLabels = null;
824
825                         try
826                         {
827                                 switch( name )
828                                 {
829                                         case "STANDARDDEVIATION":
830                                                 StandardDeviation( inputValues, out outputValues, parameterList, extraParameterList );
831                                                 break;
832                                         case "AVERAGETRUERANGE":
833                                                 AverageTrueRange( inputValues, out outputValues, parameterList );
834                                                 break;
835                                         case "EASEOFMOVEMENT":
836                                                 EaseOfMovement( inputValues, out outputValues );
837                                                 break;
838                                         case "MASSINDEX":
839                                                 MassIndex( inputValues, out outputValues, parameterList );
840                                                 break;
841                                         case "PERFORMANCE":
842                                                 Performance( inputValues, out outputValues );
843                                                 break;
844                                         case "RATEOFCHANGE":
845                                                 RateOfChange( inputValues, out outputValues, parameterList );
846                                                 break;
847                                         case "RELATIVESTRENGTHINDEX":
848                                                 RelativeStrengthIndex( inputValues, out outputValues, parameterList );
849                                                 break;
850                                         case "TRIPLEEXPONENTIALMOVINGAVERAGE":
851                                                 Trix( inputValues, out outputValues, parameterList );
852                                                 break;
853                                         case "MOVINGAVERAGECONVERGENCEDIVERGENCE":
854                                                 Macd( inputValues, out outputValues, parameterList );
855                                                 break;
856                                         case "COMMODITYCHANNELINDEX":
857                                                 CommodityChannelIndex( inputValues, out outputValues, parameterList );
858                                                 break;
859                                         default:
860                                                 outputValues = null;
861                                                 break;
862                                 }
863                         }
864                         catch( IndexOutOfRangeException )
865                         {
866                                 throw new InvalidOperationException( SR.ExceptionFormulaInvalidPeriod( name ) );
867                         }
868                         catch( OverflowException )
869                         {
870                                 throw new InvalidOperationException( SR.ExceptionFormulaNotEnoughDataPoints( name ) );
871                         }
872                 }
873
874                 #endregion
875         }
876 }