//------------------------------------------------------------- // // Copyright © Microsoft Corporation. All Rights Reserved. // //------------------------------------------------------------- // @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 { /// /// Price indicator is module with mathematical calculations /// that apply to a security's price. /// 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 /// /// Formula Module name /// virtual public string Name { get { return SR.FormulaNamePriceIndicators; } } #endregion #region Formulas /// /// 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 /// /// /// Array of doubles: Y values /// Arrays of doubles: Moving average /// Period /// Start from first value 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]; } /// /// 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 /// /// /// Arrays of doubles: 1. row - X values, 2. row - Y values /// Arrays of doubles: 1. row - X values, 2. row - Moving average /// Array of strings: 1. Period /// Array of strings: 1. Start from zero 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]; } } } } /// /// 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 /// /// /// Array of doubles: Y values /// Arrays of doubles: Exponential Moving average /// Period /// Start from first value 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]; } /// /// 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 /// /// /// Arrays of doubles: 1. row - X values, 2. row - Y values /// Arrays of doubles: 1. row - X values, 2. row - Moving average /// Array of strings: 1. Period /// Array of strings: 1. Start from zero 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; } } } /// /// 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 /// /// /// Arrays of doubles: 1. row - X values, 2. row - Y values /// Arrays of doubles: 1. row - X values, 2. row - Moving average /// Array of strings: 1. Period /// Array of strings: 1. Start from zero 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]; } } /// /// 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 /// /// /// Arrays of doubles: 1. row - X values, 2. row - Y values /// Arrays of doubles: 1. row - X values, 2. row - Moving average /// Array of strings: 1. Period /// Array of strings: 1. Start from zero 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; } } } /// /// 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 /// /// /// Arrays of doubles: 1. row - X values, 2. row - Y values /// Arrays of doubles: 1. row - X values, 2. row - Bollinger Band Up, 3. row - Bollinger Band Down /// Array of strings: 1. Period /// Array of strings: 1. Start from zero 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; } } } /// /// 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. /// /// Arrays of doubles: 1. row - X values, 2. row - Y values (Close), 3. row - Y values (High), 4. row - Y values (Low) /// Arrays of doubles: 1. row - X values, 2. row - Weighted Close 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; } } /// /// 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. /// /// Arrays of doubles: 1. row - X values, 2. row - Y values (High), 3. row - Y values (Low) /// Arrays of doubles: 1. row - X values, 2. row - Median Price 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; } } /// /// 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. /// /// Arrays of doubles: 1. row - X values, 2. row - Y values (Close), 3. row - Y values (High), 4. row - Y values (Low) /// Arrays of doubles: 1. row - X values, 2. row - Weighted Close 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; } } /// /// 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 /// /// /// Arrays of doubles: 1. row - X values, 2. row - Y values /// Arrays of doubles: 1. row - X values, 2. row - Envelopes Up, 3. row - Envelopes Down /// Array of strings: parameters /// Array of strings: Extra parameters 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; } } /// /// 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 Y values /// Output standard deviation /// Period /// Start calculation from the first Y value 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 /// /// Default constructor /// public PriceIndicators() { } /// /// This methods checks the number of X and Y values and /// fire exception if the numbers are different. /// /// Input X and Y values /// The number of Y values 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 ); } } } /// /// 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. 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 } }