Xamarin Public Jenkins (auto-signing) 536cd135cc Imported Upstream version 5.4.0.167
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
2017-08-21 15:34:15 +00:00

1175 lines
41 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//-------------------------------------------------------------
// <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 todays closing
/// price to yesterdays 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 todays closing price and
/// multiply it by 9%. Next, you would add this product to
/// the value of yesterdays 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 todays closing
/// price to yesterdays 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 todays closing price and
/// multiply it by 9%. Next, you would add this product to
/// the value of yesterdays 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
/// days 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
}
}