536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
1175 lines
41 KiB
C#
1175 lines
41 KiB
C#
//-------------------------------------------------------------
|
||
// <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
|
||
}
|
||
}
|