//------------------------------------------------------------- // // Copyright © Microsoft Corporation. All Rights Reserved. // //------------------------------------------------------------- // @owner=alexgor, deliant //================================================================= // File: TechGeneralIndicators.cs // // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting.Formulas // // Classes: TechGeneralIndicators // // Purpose: This class is used for calculations of // general technical analyses indicators. // // Reviewed: GS - August 7, 2002 // AG - August 7, 2002 // //=================================================================== using System; using System.Globalization; #if Microsoft_CONTROL namespace System.Windows.Forms.DataVisualization.Charting.Formulas #else namespace System.Web.UI.DataVisualization.Charting.Formulas #endif { /// /// This class is used for calculations of general /// technical analyses indicators. /// internal class GeneralTechnicalIndicators : PriceIndicators { #region Properties /// /// Formula Module name /// override public string Name { get { return SR.FormulaNameGeneralTechnicalIndicators; } } #endregion #region Formulas /// /// Standard Deviation is a statistical measure of volatility. /// Standard Deviation is typically used as a component of /// other indicators, rather than as a stand-alone indicator. /// For example, Bollinger Bands are calculated by adding /// a security's Standard Deviation to a moving average. /// High Standard Deviation values occur when the data item /// being analyzed (e.g., prices or an indicator) is changing /// dramatically. Similarly, low Standard Deviation values /// occur when prices are stable. /// --------------------------------------------------------- /// Input: /// - 1 Y value. /// Output: /// - 1 Y value Standard Deviation /// Parameters: /// - Periods for standard deviation ( used for moving average ) /// Extra Parameters: /// - /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters /// Array of strings - Extra parameters private void StandardDeviation(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList) { int length = inputValues.Length; // Period for standard deviation ( used for moving average ) int period; try {period = int.Parse( parameterList[0], System.Globalization.CultureInfo.InvariantCulture );} catch( Exception e ) { if (e.Message == SR.ExceptionObjectReferenceIsNull) throw new InvalidOperationException(SR.ExceptionPriceIndicatorsPeriodMissing); else throw new InvalidOperationException(SR.ExceptionPriceIndicatorsPeriodMissing + e.Message); } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative); // Starting average from the first data point or after period. bool startFromFirst = bool.Parse( extraParameterList[0] ); // There is no enough series if( length != 2 ) throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray); // Different number of x and y values if( inputValues[0].Length != inputValues[1].Length ) throw new ArgumentException(SR.ExceptionPriceIndicatorsSameXYNumber); // Not enough values for moving average in Standard deviation. if( inputValues[0].Length < period ) throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints); outputValues = new double [2][]; StandardDeviation( inputValues[1], out outputValues[1], period, startFromFirst ); // Set X values outputValues[0] = new double [outputValues[1].Length]; for( int index = 0; index < outputValues[1].Length; index++ ) { if( startFromFirst ) outputValues[0][index] = inputValues[0][index]; else outputValues[0][index] = inputValues[0][index+period-1]; } } /// /// The Average True Range ("ATR") is a measure of volatility. It was introduced /// by Welles Wilder in his book, New Concepts in Technical Trading Systems, and /// has since been used as a component of many indicators and trading systems. Wilder /// has found that high ATR values often occur at market bottoms following a "panic" /// sell-off. Low Average True Range values are often found during extended sideways /// periods, such as those found at tops and after consolidation periods. The Average /// True Range can be interpreted using the same techniques that are used with /// the other volatility indicators. /// --------------------------------------------------------- /// Input: /// - 3 Y values ( High, Low, Close ). /// Output: /// - 1 Y value AverageTrueRange /// Parameters: /// - Periods (Default 14) = is used to configure the number of periods to calculate the ATR /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters private void AverageTrueRange(double [][] inputValues, out double [][] outputValues, string [] parameterList) { // There is no enough input series if( inputValues.Length != 4 ) throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays); // Different number of x and y values CheckNumOfValues( inputValues, 3 ); // Period int period; if (parameterList.Length < 1 || !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period)) { period = 14; } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative); // The distance from today's high to today's low double distanceOne; // The distance from yesterday's close to today's high double distanceTwo; // The distance from yesterday's close to today's low double distanceTree; double [] trueRange = new double [inputValues[0].Length - 1]; // True Range for( int index = 1; index < inputValues[0].Length; index++ ) { // The distance from today's high to today's low distanceOne = Math.Abs( inputValues[1][index] - inputValues[2][index] ); // The distance from yesterday's close to today's high distanceTwo = Math.Abs( inputValues[3][index-1] - inputValues[1][index] ); // The distance from yesterday's close to today's low distanceTree = Math.Abs( inputValues[3][index-1] - inputValues[2][index] ); // True Range trueRange[index-1] = Math.Max( Math.Max( distanceOne, distanceTwo ), distanceTree ); } outputValues = new double [2][]; outputValues[0] = new double [inputValues[0].Length-period]; // Moving average of true range MovingAverage( trueRange, out outputValues[1], period, false ); // Set X values for( int index = period; index < inputValues[0].Length; index++ ) { outputValues[0][index-period] = inputValues[0][index]; } } /// /// The Ease of Movement indicator shows the relationship between volume and price /// change. This indicator shows how much volume is required to move prices. The Ease /// of Movement indicator was developed Richard W. Arms, Jr., the creator of Equivolume. /// High Ease of Movement values occur when prices are moving upward on lightStyle volume. /// Low Ease of Movement values occur when prices are moving downward on lightStyle volume. /// If prices are not moving, or if heavy volume is required to move prices, then /// indicator will also be near zero. /// --------------------------------------------------------- /// Input: /// - 3 Y values ( High, Low, Volume ). /// Output: /// - 1 Y value Ease Of Movement /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values private void EaseOfMovement(double [][] inputValues, out double [][] outputValues) { // There is no enough input series if( inputValues.Length != 4 ) throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays); // Different number of x and y values CheckNumOfValues( inputValues, 3 ); double MidPointMove; double BoxRattio; outputValues = new double [2][]; outputValues[0] = new double [inputValues[0].Length - 1]; outputValues[1] = new double [inputValues[0].Length - 1]; // Ease Of Movement for( int index = 1; index < inputValues[0].Length; index++ ) { // Set X values outputValues[0][index - 1] = inputValues[0][index]; // Calculate the Mid-point Move for each day: MidPointMove = ( inputValues[1][index] + inputValues[2][index] ) / 2 - ( inputValues[1][index - 1] + inputValues[2][index - 1] ) / 2; // The Box Ratio determines the ratio between height and width of the Equivolume box: BoxRattio = ( inputValues[3][index] ) / (( inputValues[1][index] - inputValues[2][index] ) ); // Ease of Movement is then calculated as: outputValues[1][index - 1] = MidPointMove / BoxRattio; } } /// /// The Mass Index was designed to identify trend reversals by measuring the narrowing /// and widening of the range between the high and low prices. As this range widens, the /// Mass Index increases; as the range narrows the Mass Index decreases. /// The Mass Index was developed by Donald Dorsey. According to Mr. Dorsey, the most /// significant pattern to watch for is a "reversal bulge." A reversal bulge occurs when /// a 25-period Mass Index rises above 27.0 and subsequently falls below 26.5. A reversal /// in price is then likely. The overall price trend (i.e., trending or trading range) /// is unimportant. /// --------------------------------------------------------- /// Input: /// - 2 Y values ( High, Low ). /// Output: /// - 1 Y value Mass Index /// Parameters: /// - Period = is used to calculate the accumulation, By default this property is set to 25. /// - AveragePeriod = is used to calculate Simple Moving Avg, By default this property is set to 9. /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters private void MassIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList) { // There is no enough input series if( inputValues.Length != 3 ) throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays); // Different number of x and y values CheckNumOfValues( inputValues, 2 ); // Period int period; if (parameterList.Length < 1 || !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period)) { period = 25; } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative); // Average Period int averagePeriod; if (parameterList.Length < 2 || !int.TryParse(parameterList[1], NumberStyles.Any, CultureInfo.InvariantCulture, out averagePeriod)) { averagePeriod = 9; } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodAverageParameterIsNegative); double [] highLow = new double [inputValues[0].Length]; double [] average; double [] secondAverage; for( int index = 0; index < inputValues[0].Length; index++ ) { highLow[index] = inputValues[1][index] - inputValues[2][index]; } // Find exponential moving average ExponentialMovingAverage( highLow, out average, averagePeriod, false ); // Find exponential moving average of exponential moving average ExponentialMovingAverage( average, out secondAverage, averagePeriod, false ); outputValues = new double [2][]; outputValues[0] = new double [secondAverage.Length - period + 1]; outputValues[1] = new double [secondAverage.Length - period + 1]; // Mass Index int outIndex = 0; double sum = 0; for( int index = 2 * averagePeriod - 3 + period; index < inputValues[0].Length; index++ ) { // Set X values outputValues[0][outIndex] = inputValues[0][index]; sum = 0; for( int indexSum = index - period + 1; indexSum <= index; indexSum++ ) { sum += average[indexSum - averagePeriod + 1] / secondAverage[indexSum - 2 * averagePeriod + 2]; } // Set Y values outputValues[1][outIndex] = sum; outIndex++; } } /// /// The Performance indicator displays a security's price performance as /// a percentage. This is sometimes called a "normalized" chart. The /// Performance indicator displays the percentage that the security /// has increased since the first period displayed. For example, if /// the Performance indicator is 10, it means that the security's /// price has increased 10% since the first period displayed on the /// left side of the chart. Similarly, a value of -10% means that /// the security's price has fallen by 10% since the first period /// displayed. /// --------------------------------------------------------- /// Input: /// - 1 Y value ( Close ). /// Output: /// - 1 Y value Performance /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values private void Performance(double [][] inputValues, out double [][] outputValues) { // There is no enough input series if( inputValues.Length != 2 ) throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray); // Different number of x and y values CheckNumOfValues( inputValues, 1 ); outputValues = new double [2][]; outputValues[0] = new double [inputValues[0].Length]; outputValues[1] = new double [inputValues[0].Length]; // Performance indicator for( int index = 0; index < inputValues[0].Length; index++ ) { // Set X values outputValues[0][index] = inputValues[0][index]; // Set Y values outputValues[1][index] = ( inputValues[1][index] - inputValues[1][0] ) / inputValues[1][0] * 100; } } /// /// Rate of Change is used to monitor momentum by making direct comparisons between current /// and past prices on a continual basis. The results can be used to determine the strength /// of price trends. Note: This study is the same as the Momentum except that Momentum uses /// subtraction in its calculations while Rate of Change uses division. The resulting lines /// of these two studies operated over the same data will look exactly the same - only the /// scale values will differ. The Price Rate-of-Change ("----") indicator displays the /// difference between the current price and the price x-time periods ago. The difference /// can be displayed in either points or as a percentage. The Momentum indicator displays /// the same information, but expresses it as a ratio. When the Rate-of-Change displays /// the price change in points, it subtracts the price x-time periods ago from today’s price. /// When the Rate-of-Change displays the price change as a percentage, it divides /// the price change by price x-time period’s ago. /// --------------------------------------------------------- /// Input: /// - 1 Y value ( Close ). /// Output: /// - 1 Y value Rate of Change /// Parameters: /// - Periods = is used to configure the number of periods to calculate the rate of Change. By default the Periods property is set to 10. /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters private void RateOfChange(double [][] inputValues, out double [][] outputValues, string [] parameterList) { // There is no enough input series if( inputValues.Length != 2 ) throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray); // Different number of x and y values CheckNumOfValues( inputValues, 1 ); // Period int period; if (parameterList.Length < 1 || !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period)) { period = 10; } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative); outputValues = new double [2][]; outputValues[0] = new double [inputValues[0].Length - period]; outputValues[1] = new double [inputValues[0].Length - period]; // Rate Of Change for( int index = period; index < inputValues[0].Length; index++ ) { // Set X values outputValues[0][index - period] = inputValues[0][index]; // Set Y values outputValues[1][index - period] = ( inputValues[1][index] - inputValues[1][index - period] ) / inputValues[1][index - period] * 100; } } /// /// This indicator was developed by Welles Wilder Jr. Relative Strength is often /// used to identify price tops and bottoms by keying on specific levels /// (usually "30" and "70") on the RSI chart which is scaled from from 0-100. /// The study is also useful to detect the following: /// - Movement which might not be as readily apparent on the bar chart /// - Failure swings above 70 or below 30 which can warn of coming reversals /// - Support and resistance levels /// - Divergence between the RSI and price which is often a useful reversal indicator /// --------------------------------------------------------- /// Input: /// - 1 Y value ( Close ). /// Output: /// - 1 Y value RelativeStrengthIndex /// Parameters: /// - Periods = is used to configure the number of periods to calculate the RSI indicator. By default the Periods property is set to 10. /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters private void RelativeStrengthIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList) { // There is no enough input series if( inputValues.Length != 2 ) throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray); // Different number of x and y values CheckNumOfValues( inputValues, 1 ); // Period int period; if (parameterList.Length < 1 || !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period)) { period = 10; } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative); double [] upward = new double[inputValues[0].Length-1]; double [] downward = new double[inputValues[0].Length-1]; for( int index = 1; index < inputValues[0].Length; index++ ) { // Upward - price is going up if( inputValues[1][index - 1] < inputValues[1][index] ) { upward[index-1] = inputValues[1][index] - inputValues[1][index - 1]; downward[index-1] = 0.0; } // Downward - price is going down if( inputValues[1][index - 1] > inputValues[1][index] ) { upward[index-1] = 0.0; downward[index-1] = inputValues[1][index - 1] - inputValues[1][index]; } } double [] averageUpward = new double[inputValues[0].Length]; double [] averageDownward = new double[inputValues[0].Length]; ExponentialMovingAverage(downward, out averageDownward, period, false ); ExponentialMovingAverage(upward, out averageUpward, period, false ); outputValues = new double [2][]; outputValues[0] = new double [averageDownward.Length]; outputValues[1] = new double [averageDownward.Length]; // Find RSI for( int index = 0; index < averageDownward.Length; index++ ) { // Set X values outputValues[0][index] = inputValues[0][index + period]; // Calculate the Relative Strength Index (RSI): outputValues[1][index] = 100 - 100 / ( 1 + averageUpward[index] / averageDownward[index] ); } } /// /// TripleExponentialMovingAverage is a momentum indicator that displays the percent rate-of-change of a triple /// exponentially smoothed moving average of the security's closing price. It is designed /// to keep you in trends equal to or shorter than the number of periods you specify. /// The TripleExponentialMovingAverage indicator oscillates around a zero line. Its triple exponential smoothing is /// designed to filter out "insignificant" cycles (i.e., those that are shorter than /// the number of periods you specify). Trades should be placed when the indicator changes /// direction (i.e., buy when it turns up and sell when it turns down). You may want to /// plot a 9-period moving average of the TripleExponentialMovingAverage to create a "signal" line (similar to the /// MovingAverageConvergenceDivergence indicator, and then buy when the TripleExponentialMovingAverage rises above its signal, and sell when it /// falls below its signal. Divergences between the security and the TripleExponentialMovingAverage can also help /// identify turning points. /// --------------------------------------------------------- /// Input: /// - 1 Y values ( Close ). /// Output: /// - 1 Y value ( TripleExponentialMovingAverage ). /// Parameters: /// - Period = is used to calculate the Exponential Moving Avg, By default this property is set to 12. /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters private void Trix(double [][] inputValues, out double [][] outputValues, string [] parameterList) { // There is no enough input series if( inputValues.Length != 2 ) throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray); // Different number of x and y values CheckNumOfValues( inputValues, 1 ); // Period int period; if (parameterList.Length < 1 || !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period)) { period = 12; } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative); double [] exp1; // Exponential Moving average of input values double [] exp2; // Exponential Moving average of exp1 double [] exp3; // Exponential Moving average of exp2 // Find exponential moving average ExponentialMovingAverage( inputValues[1], out exp1, period, false ); // Find exponential moving average ExponentialMovingAverage( exp1, out exp2, period, false ); // Find exponential moving average ExponentialMovingAverage( exp2, out exp3, period, false ); outputValues = new double [2][]; outputValues[0] = new double [inputValues[0].Length - period * 3 + 2]; outputValues[1] = new double [inputValues[0].Length - period * 3 + 2]; // Calculate TripleExponentialMovingAverage int outIndex = 0; for( int index = period * 3 - 2; index < inputValues[0].Length; index++ ) { // set X value outputValues[0][outIndex] = inputValues[0][index]; // set Y value outputValues[1][outIndex] = ( exp3[outIndex+1] - exp3[outIndex] ) / exp3[outIndex]; outIndex++; } } /// /// The MovingAverageConvergenceDivergence is used to determine overbought or oversold conditions in the market. Written /// for stocks and stock indices, MovingAverageConvergenceDivergence can be used for commodities as well. The MovingAverageConvergenceDivergence line /// is the difference between the long and short exponential moving averages of the chosen /// item. The signal line is an exponential moving average of the MovingAverageConvergenceDivergence line. Signals are /// generated by the relationship of the two lines. As with RSI and Stochastics, /// divergences between the MovingAverageConvergenceDivergence and prices may indicate an upcoming trend reversal. The MovingAverageConvergenceDivergence /// is a trend following momentum indicator that shows the relationship between two /// moving averages of prices. The MovingAverageConvergenceDivergence is the difference between a 26-day and 12-day /// exponential moving average. A 9-day exponential moving average, called the "signal" /// (or "trigger") line is plotted on top of the MovingAverageConvergenceDivergence to show buy/sell opportunities. The /// MovingAverageConvergenceDivergence is calculated by subtracting the value of a 26-day exponential moving average /// from a 12-day exponential moving average. A 9-day dotted exponential moving average of /// the MovingAverageConvergenceDivergence (the "signal" line) is then plotted on top of the MovingAverageConvergenceDivergence. /// --------------------------------------------------------- /// Input: /// - 1 Y value ( Close ). /// Output: /// - 1 Y value ( MovingAverageConvergenceDivergence ). /// Parameters: /// - ShortPeriod = is used to configure the short Exponential Moving Average, By default this property is set to 12. /// - LongPeriod = is used to configure the Int64 Exponential Moving Average, By default this property is set to 26. /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters private void Macd(double [][] inputValues, out double [][] outputValues, string [] parameterList) { // There is no enough input series if( inputValues.Length != 2 ) throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresOneArray); // Different number of x and y values CheckNumOfValues( inputValues, 1 ); // Short Period int shortPeriod; if (parameterList.Length < 1 || !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out shortPeriod)) { shortPeriod = 12; } if( shortPeriod <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodShortParameterIsNegative); // Int64 Period int longPeriod; if (parameterList.Length < 2 || !int.TryParse(parameterList[1], NumberStyles.Any, CultureInfo.InvariantCulture, out longPeriod)) { longPeriod = 26; } if( longPeriod <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodLongParameterIsNegative); if( longPeriod <= shortPeriod ) throw new InvalidOperationException(SR.ExceptionIndicatorsLongPeriodLessThenShortPeriod); double [] longAverage; // Int64 Average double [] shortAverage; // Short Average // Find Int64 exponential moving average ExponentialMovingAverage( inputValues[1], out longAverage, longPeriod, false ); // Find Short exponential moving average ExponentialMovingAverage( inputValues[1], out shortAverage, shortPeriod, false ); outputValues = new double [2][]; outputValues[0] = new double [inputValues[0].Length - longPeriod + 1]; outputValues[1] = new double [inputValues[0].Length - longPeriod + 1]; // Calculate MovingAverageConvergenceDivergence int outIndex = 0; for( int index = longPeriod - 1; index < inputValues[0].Length; index++ ) { // set X value outputValues[0][outIndex] = inputValues[0][index]; // set Y value outputValues[1][outIndex] = shortAverage[ outIndex + longPeriod - shortPeriod ] - longAverage[outIndex]; outIndex++; } } /// /// The CCI is a timing system that is best applied to commodity contracts which /// have cyclical or seasonal tendencies. CCI does not determine the length of /// cycles - it is designed to detect when such cycles begin and end through /// the use of a statistical analysis which incorporates a moving average and a divisor /// reflecting both the possible and actual trading ranges. Although developed primarily /// for commodities, the CCI could conceivably be used to analyze stocks as well. The /// Commodity Channel Index ("CCI") measures the variation of a security’s price from /// its statistical mean. High values show that prices are unusually high compared to /// average prices whereas low values indicate that prices are unusually low. /// 1. Calculate today's Typical Price (TP) = (H+L+C)/3 where H = high; L = low, and C = close. /// 2. Calculate today's 20-day Simple Moving Average of the Typical Price (SMATP). /// 3. Calculate today's Mean Deviation. First, calculate the absolute value of the difference /// between today's SMATP and the typical price for each of the past 20 days. /// Add all of these absolute values together and divide by 20 to find the Mean Deviation. /// 4. The final step is to apply the Typical Price (TP), the Simple Moving Average of the /// Typical Price (SMATP), the Mean Deviation and a Constant (.015). /// --------------------------------------------------------- /// Input: /// - 3 Y values ( Hi, Low, Close ). /// Output: /// - 1 Y value ( CCI ). /// Parameters: /// - Periods = is used to configure the number of periods to calculate the CCI. By default the Periods property is set to 10. /// /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Parameters private void CommodityChannelIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList) { // There is no enough input series if( inputValues.Length != 4 ) throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays); // Different number of x and y values CheckNumOfValues( inputValues, 3 ); // Period int period; if (parameterList.Length < 1 || !int.TryParse(parameterList[0], NumberStyles.Any, CultureInfo.InvariantCulture, out period)) { period = 10; } if( period <= 0 ) throw new InvalidOperationException(SR.ExceptionPeriodParameterIsNegative); // Typical Price double [] typicalPrice = new double[inputValues[0].Length]; // Typical Price loop for( int index = 0; index < inputValues[0].Length; index++ ) { typicalPrice[index] = ( inputValues[1][index] + inputValues[2][index] + inputValues[3][index] ) / 3.0; } // Moving Average double [] movingAverage; // Simple Moving Average of the Typical Price MovingAverage( typicalPrice, out movingAverage, period, false ); // Calculate today's Mean Deviation. First, calculate the absolute value // of the difference between today's SMATP and the typical price for each // of the past 20 days. Add all of these absolute values together and // divide by 20 to find the Mean Deviation. // Mean Deviation double [] meanDeviation = new double[movingAverage.Length]; double sum =0; for( int index = 0; index < movingAverage.Length; index++ ) { sum = 0; for( int indexSum = index; indexSum < index + period; indexSum++ ) { sum += Math.Abs( movingAverage[index] - typicalPrice[indexSum] ); } meanDeviation[index] = sum / period; } outputValues = new double [2][]; outputValues[0] = new double [meanDeviation.Length]; outputValues[1] = new double [meanDeviation.Length]; for( int index = 0; index < meanDeviation.Length; index++ ) { // Set X values outputValues[0][index] = inputValues[0][index + period - 1]; // Set Y values outputValues[1][index] = ( typicalPrice[index + period - 1] - movingAverage[index] ) / ( 0.015 * meanDeviation[index] ); } } #endregion #region Methods /// /// Default constructor /// public GeneralTechnicalIndicators() { } /// /// The first method in the module, which converts a formula /// name to the corresponding private method. /// /// String which represent a formula name /// Arrays of doubles - Input values /// Arrays of doubles - Output values /// Array of strings - Formula parameters /// Array of strings - Extra Formula parameters from DataManipulator object /// Array of strings - Used for Labels. Description for output results. override public void Formula( string formulaName, double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList, out string [][] outLabels ) { string name; outputValues = null; name = formulaName.ToUpper(System.Globalization.CultureInfo.InvariantCulture); // Not used for these formulas. outLabels = null; try { switch( name ) { case "STANDARDDEVIATION": StandardDeviation( inputValues, out outputValues, parameterList, extraParameterList ); break; case "AVERAGETRUERANGE": AverageTrueRange( inputValues, out outputValues, parameterList ); break; case "EASEOFMOVEMENT": EaseOfMovement( inputValues, out outputValues ); break; case "MASSINDEX": MassIndex( inputValues, out outputValues, parameterList ); break; case "PERFORMANCE": Performance( inputValues, out outputValues ); break; case "RATEOFCHANGE": RateOfChange( inputValues, out outputValues, parameterList ); break; case "RELATIVESTRENGTHINDEX": RelativeStrengthIndex( inputValues, out outputValues, parameterList ); break; case "TRIPLEEXPONENTIALMOVINGAVERAGE": Trix( inputValues, out outputValues, parameterList ); break; case "MOVINGAVERAGECONVERGENCEDIVERGENCE": Macd( inputValues, out outputValues, parameterList ); break; case "COMMODITYCHANNELINDEX": CommodityChannelIndex( inputValues, out outputValues, parameterList ); break; default: outputValues = null; break; } } catch( IndexOutOfRangeException ) { throw new InvalidOperationException( SR.ExceptionFormulaInvalidPeriod( name ) ); } catch( OverflowException ) { throw new InvalidOperationException( SR.ExceptionFormulaNotEnoughDataPoints( name ) ); } } #endregion } }