File: Common\Formulas\PriceIndicators.cs
Project: ndp\fx\src\DataVisualization\System.Windows.Forms.DataVisualization.csproj (System.Windows.Forms.DataVisualization)
//-------------------------------------------------------------
// <copyright company=’Microsoft Corporation’>
//   Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor, deliant
//=================================================================
//  File:		PriceIndicators.cs
//
//  Namespace:	System.Web.UI.WebControls[Windows.Forms].Charting.Formulas
//
//	Classes:	PriceIndicators
//
//  Purpose:	This class is used to calculate Price 
//				indicators used in Technical Analyses.
//
//	Reviewed:	GS - August 7, 2002
//				AG - August 7, 2002
//
//===================================================================
 
 
using System;
 
 
#if Microsoft_CONTROL
    namespace System.Windows.Forms.DataVisualization.Charting.Formulas
#else
	namespace System.Web.UI.DataVisualization.Charting.Formulas
#endif
{
	/// <summary>
	/// Price indicator is module with mathematical calculations 
	/// that apply to a security's price.
	/// </summary>
	internal class PriceIndicators : IFormula
	{
		#region Error strings
 
		// Error strings
		//internal string inputArrayStart = "Formula requires";
		//internal string inputArrayEnd = "arrays";
		//internal string SR.ExceptionPriceIndicatorsSameYNumber = "Formula requires the same number of Y values for each input data point";
		//internal string SR.ExceptionPriceIndicatorsFormulaRequiresFourArrays = "Formula requires the same number of X and Y values for each input data point";
		//internal string periodMissing = "Formula error - Period parameter is missing. ";
		//internal string SR.ExceptionPriceIndicatorsFormulaRequiresFourArrays = "Formula error - There are not enough data points for the Period. ";
 
		#endregion
 
		#region Properties
 
		/// <summary>
		/// Formula Module name
		/// </summary>
        virtual public string Name { get { return SR.FormulaNamePriceIndicators; } }
 
		#endregion
 
		#region Formulas
 
		/// <summary>
		/// A Moving Average is an indicator that shows the average 
		/// value of a security's price over a period of time. When 
		/// calculating a moving average, a mathematical analysis of 
		/// the security's average value over a predetermined time 
		/// period is made. As the security's price changes, 
		/// its average price moves up or down.
		/// A simple, or arithmetic, moving average is calculated by 
		/// adding the closing price of the security for a number of 
		/// time periods (e.g., 12 days) and then dividing this total 
		/// by the number of time periods. The result is the average 
		/// price of the security over the time period. Simple moving 
		/// averages give equal weight to each daily price.
		/// ---------------------------------------------------------
		/// Input: 
		///		- Y values.
		/// Output: 
		///		- Moving Average.
		/// Parameters: 
		///		- Period
		///	Extra Parameters: 
		///		- Start from First 
		/// 
		/// </summary>
		/// <param name="inputValues">Array of doubles: Y values</param>
		/// <param name="outputValues">Arrays of doubles: Moving average</param>
		/// <param name="period">Period</param>
		/// <param name="FromFirst">Start from first value</param>
		internal void MovingAverage(double [] inputValues, out double [] outputValues, int period, bool FromFirst )
		{
			double [][] tempInput = new double [2][];
			double [][] tempOutput = new double [2][];
			string [] parList = new string [1];
			string [] extList = new string [1];
 
			parList[0] = period.ToString(System.Globalization.CultureInfo.InvariantCulture);
			extList[0] = FromFirst.ToString(System.Globalization.CultureInfo.InvariantCulture);
 
			tempInput[0] = new double[inputValues.Length];
			tempInput[1] = inputValues;
			
			MovingAverage( tempInput, out tempOutput, parList, extList );
		
			outputValues = tempOutput[1];
		}
	
		/// <summary>
		/// A Moving Average is an indicator that shows the average 
		/// value of a security's price over a period of time. When 
		/// calculating a moving average, a mathematical analysis of 
		/// the security's average value over a predetermined time 
		/// period is made. As the security's price changes, 
		/// its average price moves up or down.
		/// A simple, or arithmetic, moving average is calculated by 
		/// adding the closing price of the security for a number of 
		/// time periods (e.g., 12 days) and then dividing this total 
		/// by the number of time periods. The result is the average 
		/// price of the security over the time period. Simple moving 
		/// averages give equal weight to each daily price.
		/// ---------------------------------------------------------
		/// Input: 
		///		- Y values.
		/// Output: 
		///		- Moving Average.
		/// Parameters: 
		///		- Period
		///	Extra Parameters: 
		///		- Start from First 
		/// 
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Moving average</param>
		/// <param name="parameterList">Array of strings: 1. Period</param>
		/// <param name="extraParameterList">Array of strings: 1. Start from zero</param>
		private void MovingAverage(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
		{
			int length = inputValues.Length;
 
			// Period 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.
			if( inputValues[0].Length < period )
                throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
 
			outputValues = new double [2][];
 
			if( startFromFirst )
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length];
			
				for( int point = 0; point < inputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point];
 
					// Find sum of Y values
					double sum = 0;
					int startSum = 0;
 
					// Find the begining of the period
					if( point - period + 1 > 0 )
					{
						startSum = point - period + 1;
					}
 
					// Find sum fro real period.
					for( int pointSum = startSum; pointSum <= point; pointSum++ )
					{
						sum += inputValues[1][pointSum];
					}
 
					// Find real period if start from first data point.
					int realPeriod = period;
					if( period > point + 1 )
					{
						realPeriod = point + 1;
					}
 
					outputValues[1][point] = sum / realPeriod;
				}
			}
			else
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length - period + 1];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length - period + 1];
			
				// Find sum of Y values for the period
				double sum = 0;
				for( int pointSum = 0; pointSum < period; pointSum++ )
				{
					sum += inputValues[1][pointSum];
				}
 
				for( int point = 0; point < outputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point + period - 1];
 
					outputValues[1][point] = sum / period;
 
					// Change Sum
					if( point < outputValues[0].Length - 1 )
					{
						sum -= inputValues[1][point];
						sum += inputValues[1][point + period];
					}
				}
			}
		}
 
		/// <summary>
		/// An exponential (or exponentially weighted) moving average 
		/// is calculated by applying a percentage of today’s closing 
		/// price to yesterday’s moving average value. Exponential 
		/// moving averages place more weight on recent prices.	For 
		/// example, to calculate a 9% exponential moving average 
		/// of IBM, you would first take today’s closing price and 
		/// multiply it by 9%. Next, you would add this product to 
		/// the value of yesterday’s moving average multiplied by 
		/// 91% (100% - 9% = 91%).
		/// ---------------------------------------------------------
		/// Input: 
		///		- Y values.
		/// Output: 
		///		- Exponential Moving Average.
		/// Parameters: 
		///		- Period
		///	Extra Parameters: 
		///		- Start from First 
		/// 
		/// </summary>
		/// <param name="inputValues">Array of doubles: Y values</param>
		/// <param name="outputValues">Arrays of doubles: Exponential Moving average</param>
		/// <param name="period">Period</param>
		/// <param name="startFromFirst">Start from first value</param>
		internal void ExponentialMovingAverage(double []inputValues, out double []outputValues, int period, bool startFromFirst)
		{
			double [][] tempInput = new double [2][];
			double [][] tempOutput = new double [2][];
			string [] parList = new string [1];
			string [] extList = new string [1];
 
			parList[0] = period.ToString(System.Globalization.CultureInfo.InvariantCulture);
			extList[0] = startFromFirst.ToString(System.Globalization.CultureInfo.InvariantCulture);
 
			tempInput[0] = new double[inputValues.Length];
			tempInput[1] = inputValues;
			
			ExponentialMovingAverage( tempInput, out tempOutput, parList, extList );
		
			outputValues = tempOutput[1];
		}
 
		/// <summary>
		/// An exponential (or exponentially weighted) moving average 
		/// is calculated by applying a percentage of today’s closing 
		/// price to yesterday’s moving average value. Exponential 
		/// moving averages place more weight on recent prices.	For 
		/// example, to calculate a 9% exponential moving average 
		/// of IBM, you would first take today’s closing price and 
		/// multiply it by 9%. Next, you would add this product to 
		/// the value of yesterday’s moving average multiplied by 
		/// 91% (100% - 9% = 91%).
		/// ---------------------------------------------------------
		/// Input: 
		///		- Y values.
		/// Output: 
		///		- Exponential Moving Average.
		/// Parameters: 
		///		- Period
		///	Extra Parameters: 
		///		- Start from First 
		/// 
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Moving average</param>
		/// <param name="parameterList">Array of strings: 1. Period</param>
		/// <param name="extraParameterList">Array of strings: 1. Start from zero</param>
		private void ExponentialMovingAverage(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
		{
			int length = inputValues.Length;
 
			// Period 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);
 
			// Formula for converting period to percentage
			double exponentialPercentage = 2.0 / ( period + 1.0 );
 
			// 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.
			if( inputValues[0].Length < period )
                throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
 
			outputValues = new double [2][];
 
			if( startFromFirst )
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length];
				
				for( int point = 0; point < inputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point];
 
					// Find sum of Y values
					double sum = 0;
					int startSum = 0;
 
					if( point - period + 1 > 0 )
					{
						startSum = point - period + 1;
					}
 
					for( int pointSum = startSum; pointSum < point; pointSum++ )
					{
						sum += inputValues[1][pointSum];
					}
 
					int realPeriod = period;
					if( period > point + 1 )
						realPeriod = point + 1;
 
					double movingAvr;
 
					// Find real period if start from first data point.
					if( realPeriod <= 1 )
						movingAvr = 0;
					else
						movingAvr = sum / ( realPeriod - 1 );
 
					// Formula for converting period to percentage
					exponentialPercentage = 2.0 / ( realPeriod + 1.0 );
 
					// Exponential influence
					outputValues[1][point] = movingAvr * (1 - exponentialPercentage ) + inputValues[1][point] * exponentialPercentage;
 
				}
			}
			else
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length - period + 1];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length - period + 1];
				
				for( int point = 0; point < outputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point + period - 1];
 
					double movingAvr;
					// if point is less than period calulate simple moving average
					if(  point == 0  )
					{
						// Find sum of Y values
						double sum = 0;
						for( int pointSum = point; pointSum < point + period; pointSum++ )
						{
							sum += inputValues[1][pointSum];
						}
 
						movingAvr = sum / ( period );
					}
						// else use previos day exponential moving average
					else
						movingAvr = outputValues[1][point-1];
 
					// Exponential influence
					outputValues[1][point] = movingAvr * (1 - exponentialPercentage ) + inputValues[1][point + period - 1] * exponentialPercentage;
 
				}
			}
		}
 
		/// <summary>
		/// Triangular moving averages place the majority of the weight 
		/// on the middle portion of the price series. They are actually 
		/// double-smoothed simple moving averages. The periods used 
		/// in the simple moving averages varies depending on if you 
		/// specify an odd or even number of time periods. 
		/// ---------------------------------------------------------
		/// Input: 
		///		- Y values.
		/// Output: 
		///		- Moving Average.
		/// Parameters: 
		///		- Period
		///	Extra Parameters: 
		///		- Start from First 
		/// 
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Moving average</param>
		/// <param name="parameterList">Array of strings: 1. Period</param>
		/// <param name="extraParameterList">Array of strings: 1. Start from zero</param>
		private void TriangularMovingAverage(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
		{
			int length = inputValues.Length;
 
			// Period 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.
			if( inputValues[0].Length < period )
                throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
 
			outputValues = new double [2][];
		
			// Find triangular period
			double tempPeriod = ((double)period + 1.0) / 2.0;
			tempPeriod = Math.Round(tempPeriod);
			double [] tempOut;
			double [] tempIn = inputValues[1];
 
			// Call moving averages first time
			MovingAverage( tempIn, out tempOut, (int)tempPeriod, startFromFirst );
			// Call moving averages second time (Moving average of moving average)
			MovingAverage( tempOut, out tempOut, (int)tempPeriod, startFromFirst );
 
			outputValues[1] = tempOut;
 
			// X values
			outputValues[0] = new double [outputValues[1].Length];
			
			// Set X values
			if( startFromFirst )
				outputValues[0] = inputValues[0];
			else
			{
				for( int index = 0; index < outputValues[1].Length; index++ )
					outputValues[0][index] = inputValues[0][((int)(tempPeriod)-1) * 2 + index];
			}
		}
 
		/// <summary>
		/// A weighted moving average is designed to put more weight on 
		/// recent data and less weight on past data. A weighted moving 
		/// average is calculated by multiplying each of the previous 
		/// day’s data by a weight. The following table shows the calculation 
		/// of a 5-day weighted moving average.
		/// ---------------------------------------------------------
		/// Input: 
		///		- Y values.
		/// Output: 
		///		- Moving Average.
		/// Parameters: 
		///		- Period
		///	Extra Parameters: 
		///		- Start from First 
		/// 
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Moving average</param>
		/// <param name="parameterList">Array of strings: 1. Period</param>
		/// <param name="extraParameterList">Array of strings: 1. Start from zero</param>
		private void WeightedMovingAverage(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
		{
			int length = inputValues.Length;
 
			// Period 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.
			if( inputValues[0].Length < period )
                throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
 
			outputValues = new double [2][];
 
			if( startFromFirst )
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length];
				
				for( int point = 0; point < inputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point];
 
					// Find sum of Y values
					double sum = 0;
					int startSum = 0;
 
					if( point - period + 1 > 0 )
					{
						startSum = point - period + 1;
					}
 
					int index = 1;
					int indexSum = 0;
					for( int pointSum = startSum; pointSum <= point; pointSum++ )
					{
						sum += inputValues[1][pointSum] * index;
						indexSum += index;
						index++;
					}
 
					double movingAvr;
 
					// Avoid division by zero.
					if( point == 0 )
						movingAvr = inputValues[1][0];
					else
						movingAvr = sum / indexSum;
 
					// Weighted average
					outputValues[1][point] = movingAvr;
 
				}
			}
			else
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length - period + 1];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length - period + 1];
				
				for( int point = 0; point < outputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point + period - 1];
 
					// Find sum of Y values
					double sum = 0;
					
					int index = 1;
					int indexSum = 0;
					for( int pointSum = point; pointSum < point + period; pointSum++ )
					{
						sum += inputValues[1][pointSum] * index;
						indexSum += index;
						index++;
					}
 
					double movingAvr = sum / indexSum;
 
					// Weighted average
					outputValues[1][point] = movingAvr;
 
				}
			}
		}
 
		/// <summary>
		/// Bollinger Bands plot trading bands above and below 
		/// a simple moving average. The standard deviation of 
		/// closing prices for a period equal to the moving 
		/// average employed is used to determine the band width. 
		/// This causes the bands to tighten in quiet markets and 
		/// loosen in volatile markets. The bands can be used to 
		/// determine overbought and oversold levels, locate 
		/// reversal areas, project targets for market moves, and 
		/// determine appropriate stop levels. The bands are used 
		/// in conjunction with indicators such as RSI, MovingAverageConvergenceDivergence 
		/// histogram, CCI and Rate of Change. Divergences between 
		/// Bollinger bands and other indicators show potential 
		/// action points. 
		/// ---------------------------------------------------------
		/// Input: 
		///		- 1 Y value.
		/// Output: 
		///		- 2 Y values (Bollinger Band Hi and Low).
		/// Parameters: 
		///		- period
		///	Extra Parameters: 
		///		- startFromFirst
		/// 
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Bollinger Band Up, 3. row - Bollinger Band Down</param>
		/// <param name="parameterList">Array of strings: 1. Period</param>
		/// <param name="extraParameterList">Array of strings: 1. Start from zero</param>
		private void BollingerBands(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
		{
			int length = inputValues.Length;
 
			// Period 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);
 
			// Standard deviation
			double deviation;
			try
			{deviation = double.Parse( parameterList[1], System.Globalization.CultureInfo.InvariantCulture );}
			catch(System.Exception)
            { throw new InvalidOperationException(SR.ExceptionIndicatorsDeviationMissing); }
			
			// 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.
			if( inputValues[0].Length < period )
                throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
 
			outputValues = new double [3][];
 
			if( startFromFirst )
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length];
				outputValues[2] = new double [inputValues[1].Length];
 
				// average
				double [] average = new double [inputValues[1].Length];
 
				MovingAverage( inputValues[1], out average, period, true );
		
				for( int point = 0; point < outputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point];
 
					// Find sum of Y values
					double sum = 0;
					int startSum = 0;
 
					// Find the begining of the period
					if( point - period + 1 > 0 )
					{
						startSum = point - period + 1;
					}
								
					for( int pointSum = startSum; pointSum <= point; pointSum++ )
					{
						sum += ((inputValues[1][pointSum] - average[point])*(inputValues[1][pointSum] - average[point]));
					}
 
					outputValues[1][point] = average[point] + Math.Sqrt(sum / period) * deviation;
					outputValues[2][point] = average[point] - Math.Sqrt(sum / period) * deviation;
				}
			}
			else
			{
				// X values
				outputValues[0] = new double [inputValues[0].Length - period + 1];
 
				// Y values
				outputValues[1] = new double [inputValues[1].Length - period + 1];
				outputValues[2] = new double [inputValues[1].Length - period + 1];
 
				// average
				double [] average = new double [inputValues[1].Length - period + 1];
 
				MovingAverage( inputValues[1], out average, period, false );
			
				for( int point = 0; point < outputValues[0].Length; point++ )
				{
					// Set X value
					outputValues[0][point] = inputValues[0][point + period - 1];
 
					// Find sum of Y values
					double sum = 0;
									
					for( int pointSum = point; pointSum < point + period; pointSum++ )
					{
						sum += ((inputValues[1][pointSum] - average[point])*(inputValues[1][pointSum] - average[point]));
					}
 
					outputValues[1][point] = average[point] + Math.Sqrt(sum / period) * deviation;
					outputValues[2][point] = average[point] - Math.Sqrt(sum / period) * deviation;
				}
			}
		}
 
		/// <summary>
		/// The Typical Price indicator is simply an average of each 
		/// day's price. The Median Price and Weighted Close are 
		/// similar indicators. The Typical Price indicator provides 
		/// a simple, single-line plot of the day's average price. 
		/// Some investors use the Typical Price rather than the 
		/// closing price when creating moving average ----ion 
		/// systems.
		/// ---------------------------------------------------------
		/// Input: 
		///		- 3 Y values ( Close, High, Low ).
		/// Output: 
		///		- 1 Y value Weighted Close Indicator.
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values (Close), 3. row - Y values (High), 4. row - Y values (Low)</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Weighted Close</param>
		private void TypicalPrice(double [][] inputValues, out double [][] outputValues)
		{
			int length = inputValues.Length;
						
			// There is no enough series
			if( length != 4 )
				throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
 
			// Different number of x and y values
			CheckNumOfValues( inputValues, 3 );
			
			outputValues = new double [2][];
 
			outputValues[0] = new double [inputValues[0].Length];
			outputValues[1] = new double [inputValues[1].Length];
 
			for( int index = 0; index < inputValues[1].Length; index++ )
			{
				// Set X values
				outputValues[0][index] = inputValues[0][index];
 
				// Set median price
				outputValues[1][index] = (inputValues[1][index] + inputValues[2][index] + inputValues[3][index])/3.0;
			}
 
		}
 
		/// <summary>
		/// The Median Price indicator is simply the midpoint of each 
		/// day's price. The Typical Price and Weighted Close are 
		/// similar indicators. The Median Price indicator provides 
		/// a simple, single-line chart of the day's "average price." 
		/// This average price is useful when you want a simpler 
		/// scaleView of prices.
		/// ---------------------------------------------------------
		/// Input: 
		///		- 2 Y values ( High, Low ).
		/// Output: 
		///		- 1 Y value Median Price Indicator.
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values (High), 3. row - Y values (Low)</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Median Price</param>
		private void MedianPrice(double [][] inputValues, out double [][] outputValues)
		{
			int length = inputValues.Length;
						
			// There is no enough series
			if( length != 3 )
				throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays);
 
			// Different number of x and y values
			CheckNumOfValues( inputValues, 2 );
			
			outputValues = new double [2][];
 
			outputValues[0] = new double [inputValues[0].Length];
			outputValues[1] = new double [inputValues[1].Length];
 
			for( int index = 0; index < inputValues[1].Length; index++ )
			{
				// Set X values
				outputValues[0][index] = inputValues[0][index];
 
				// Set median price
				outputValues[1][index] = (inputValues[1][index] + inputValues[2][index])/2.0;
			}
 
		}
 
		/// <summary>
		/// The Weighted Close indicator is simply an average of each day's 
		/// price. It gets its name from the fact that extra weight is 
		/// given to the closing price. The Median Price and Typical Price 
		/// are similar indicators. When plotting and back-testing moving 
		/// averages, indicators, trendlines, etc, some investors like 
		/// the simplicity that a line chart offers. However, line 
		/// charts that only show the closing price can be misleading 
		/// since they ignore the high and low price. A Weighted Close 
		/// chart combines the simplicity of the line chart with the 
		/// scope of a bar chart, by plotting a single point for each 
		/// day that includes the high, low, and closing price.
		/// ---------------------------------------------------------
		/// Input: 
		///		- 3 Y values ( Close, High, Low ).
		/// Output: 
		///		- 1 Y value Weighted Close Indicator.
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values (Close), 3. row - Y values (High), 4. row - Y values (Low)</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Weighted Close</param>
		private void WeightedClose(double [][] inputValues, out double [][] outputValues)
		{
			int length = inputValues.Length;
						
			// There is no enough series
			if( length != 4 )
				throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresThreeArrays);
 
			// Different number of x and y values
			CheckNumOfValues( inputValues, 3 );
			
			outputValues = new double [2][];
 
			outputValues[0] = new double [inputValues[0].Length];
			outputValues[1] = new double [inputValues[1].Length];
 
			for( int index = 0; index < inputValues[1].Length; index++ )
			{
				// Set X values
				outputValues[0][index] = inputValues[0][index];
 
				// Set median price
				outputValues[1][index] = (inputValues[1][index] + inputValues[2][index] + inputValues[3][index] * 2)/4.0;
			}
 
		}
 
		/// <summary>
		/// An envelope is comprised of two moving averages. One moving 
		/// average is shifted upward and the second moving average 
		/// is shifted downward. Envelopes define the upper and lower 
		/// boundaries of a security's normal trading range. A sell 
		/// signal is generated when the security reaches the upper 
		/// band whereas a buy signal is generated at the lower band. 
		/// The optimum percentage shift depends on the volatility of 
		/// the security--the more volatile, the larger the percentage.
		/// The logic behind envelopes is that overzealous buyers and 
		/// sellers push the price to the extremes (i.e., the upper 
		/// and lower bands), at which point the prices often stabilize 
		/// by moving to more realistic levels. This is similar to the 
		/// interpretation of Bollinger Bands.
		/// ---------------------------------------------------------
		/// Input: 
		///		- 1 Y value.
		/// Output: 
		///		- 2 Y values (Envelope Hi and Low).
		/// Parameters: 
		///		- period
		///		- shift in percentages
		///	Extra Parameters: 
		///		- startFromFirst
		/// 
		/// </summary>
		/// <param name="inputValues">Arrays of doubles: 1. row - X values, 2. row - Y values</param>
		/// <param name="outputValues">Arrays of doubles: 1. row - X values, 2. row - Envelopes Up, 3. row - Envelopes Down</param>
		/// <param name="parameterList">Array of strings: parameters</param>
		/// <param name="extraParameterList">Array of strings: Extra parameters </param>
		private void Envelopes(double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList)
		{
			int length = inputValues.Length;
 
			// Period 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);
 
			// Shift
			double shift;
			try
			{shift = double.Parse( parameterList[1], System.Globalization.CultureInfo.InvariantCulture );}
			catch(System.Exception)
            { throw new InvalidOperationException(SR.ExceptionPriceIndicatorsShiftParameterMissing); }
						
			// 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);
 
			double [][] movingAverage;
 
			MovingAverage( inputValues, out movingAverage, parameterList, extraParameterList );
 
			outputValues = new double[3][];
			outputValues[0] = new double[movingAverage[0].Length];
			outputValues[1] = new double[movingAverage[0].Length];
			outputValues[2] = new double[movingAverage[0].Length];
 
			for( int index = 0; index < movingAverage[0].Length; index++ )
			{
				outputValues[0][index] = movingAverage[0][index];
				outputValues[1][index] = movingAverage[1][index] + shift*movingAverage[1][index]/100.0;
				outputValues[2][index] = movingAverage[1][index] - shift*movingAverage[1][index]/100.0;
 
			}
		}
		
		/// <summary>
		/// 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.
		/// </summary>
		/// <param name="inputValues">Input Y values</param>
		/// <param name="outputValues">Output standard deviation</param>
		/// <param name="period">Period</param>
		/// <param name="startFromFirst">Start calculation from the first Y value</param>
		internal void StandardDeviation(double [] inputValues, out double [] outputValues, int period, bool startFromFirst )
		{
			double [] movingOut;
 
			// Start calculation from the first Y value
			if( startFromFirst )
			{
				outputValues = new double[inputValues.Length];
				double sum;
				MovingAverage( inputValues, out movingOut, period, startFromFirst );
				int outIndex = 0;
				for( int index = 0; index < inputValues.Length; index++ )
				{
					sum = 0;
					int startSum = 0;
 
					// Find the begining of the period
					if( index - period + 1 > 0 )
					{
						startSum = index - period + 1;
					}
					for( int indexDev = startSum; indexDev <= index; indexDev++ )
					{
						sum += (inputValues[indexDev] - movingOut[outIndex])*(inputValues[indexDev] - movingOut[outIndex]);
					}
					outputValues[outIndex] = Math.Sqrt( sum / period );
					outIndex++;
				}
			}
				// Do not start calculation from the first Y value
			else
			{
				outputValues = new double[inputValues.Length - period + 1];
				double sum;
				MovingAverage( inputValues, out movingOut, period, startFromFirst );
				int outIndex = 0;
				for( int index = period - 1; index < inputValues.Length; index++ )
				{
					sum = 0;
					for( int indexDev = index - period + 1; indexDev <= index; indexDev++ )
					{
						sum += (inputValues[indexDev] - movingOut[outIndex])*(inputValues[indexDev] - movingOut[outIndex]);
					}
					outputValues[outIndex] = Math.Sqrt( sum / period );
					outIndex++;
				}
			}
		}
 
		#endregion
 
		#region Methods
 
		/// <summary>
		/// Default constructor
		/// </summary>
		public PriceIndicators()
		{
		}
	
		/// <summary>
		/// This methods checks the number of X and Y values and 
		/// fire exception if the numbers are different.
		/// </summary>
		/// <param name="inputValues">Input X and Y values</param>
		/// <param name="numOfYValues">The number of Y values</param>
		public void CheckNumOfValues( double [][] inputValues, int numOfYValues )
		{
			// Different number of x and y values
			if( inputValues[0].Length != inputValues[1].Length )
			{
                throw new ArgumentException(SR.ExceptionPriceIndicatorsSameXYNumber);
			}
 
			// Different number of y values
			for( int index = 1; index < numOfYValues; index++ )
			{
				if( inputValues[index].Length != inputValues[index+1].Length )
				{
					throw new ArgumentException( SR.ExceptionPriceIndicatorsSameYNumber );
				}
			}
		}
 
        /// <summary>
        /// The first method in the module, which converts a formula 
        /// name to the corresponding private method.
        /// </summary>
        /// <param name="formulaName">String which represent a formula name</param>
        /// <param name="inputValues">Arrays of doubles - Input values</param>
        /// <param name="outputValues">Arrays of doubles - Output values</param>
        /// <param name="parameterList">Array of strings - Formula parameters</param>
        /// <param name="extraParameterList">Array of strings - Extra Formula parameters from DataManipulator object</param>
        /// <param name="outLabels">Array of strings - Used for Labels. Description for output results.</param>
		virtual public void Formula( string formulaName, double [][] inputValues, out double [][] outputValues, string [] parameterList, string [] extraParameterList, out string [][] outLabels )
		{
			string name;
 
			name = formulaName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
 
			// Not used for these formulas.
			outLabels = null;
 
			try
			{
				switch( name )
				{
					case "MOVINGAVERAGE":
						MovingAverage( inputValues, out outputValues, parameterList, extraParameterList );
						break;
					case "EXPONENTIALMOVINGAVERAGE":
						ExponentialMovingAverage( inputValues, out outputValues, parameterList, extraParameterList );
						break;
					case "TRIANGULARMOVINGAVERAGE":
						TriangularMovingAverage( inputValues, out outputValues, parameterList, extraParameterList );
						break;
					case "WEIGHTEDMOVINGAVERAGE":
						WeightedMovingAverage( inputValues, out outputValues, parameterList, extraParameterList );
						break;
					case "BOLLINGERBANDS":
						BollingerBands( inputValues, out outputValues, parameterList, extraParameterList );
						break;
					case "MEDIANPRICE":
						MedianPrice( inputValues, out outputValues );
						break;
					case "TYPICALPRICE":
						TypicalPrice( inputValues, out outputValues );
						break;
					case "WEIGHTEDCLOSE":
						WeightedClose( inputValues, out outputValues );
						break;
					case "ENVELOPES":
						Envelopes( inputValues, out outputValues, parameterList, extraParameterList );
						break;
					default:
						outputValues = null; 
						break;
				}
			}
			catch( IndexOutOfRangeException )
			{
                throw new InvalidOperationException(SR.ExceptionFormulaInvalidPeriod( name ) );
			}
			catch( OverflowException )
			{
				throw new InvalidOperationException( SR.ExceptionFormulaNotEnoughDataPoints( name ) );
			}
		}
 
		#endregion
	}
}