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

528 lines
19 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: VolumeIndicator.cs
//
// Namespace: System.Web.UI.WebControls[Windows.Forms].Charting.Formulas
//
// Classes: VolumeIndicators
//
// Purpose: This class is used for calculations of
// technical analyses volume indicators.
//
// 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>
/// This class is used for calculations of
/// technical analyses volume indicators.
/// </summary>
internal class VolumeIndicators : PriceIndicators
{
#region Properties
/// <summary>
/// Formula Module name
/// </summary>
override public string Name { get { return SR.FormulaNameVolumeIndicators; } }
#endregion
#region Methods
/// <summary>
/// Default Constructor
/// </summary>
public VolumeIndicators()
{
}
/// <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>
override 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 "MONEYFLOW":
MoneyFlow( inputValues, out outputValues, parameterList );
break;
case "ONBALANCEVOLUME":
OnBalanceVolume( inputValues, out outputValues );
break;
case "NEGATIVEVOLUMEINDEX":
NegativeVolumeIndex( inputValues, out outputValues, parameterList );
break;
case "POSITIVEVOLUMEINDEX":
PositiveVolumeIndex( inputValues, out outputValues, parameterList );
break;
case "PRICEVOLUMETREND":
PriceVolumeTrend( inputValues, out outputValues );
break;
case "ACCUMULATIONDISTRIBUTION":
AccumulationDistribution( inputValues, out outputValues );
break;
default:
outputValues = null;
break;
}
}
catch( IndexOutOfRangeException )
{
throw new InvalidOperationException( SR.ExceptionFormulaInvalidPeriod( name ) );
}
catch( OverflowException )
{
throw new InvalidOperationException( SR.ExceptionFormulaNotEnoughDataPoints( name ) );
}
}
#endregion
#region Formulas
/// <summary>
/// The Money Flow Index ("MFI") is a momentum indicator that
/// measures the strength of money flowing in and out of
/// a security. It is related to the Relative Strength Index,
/// but where the RSI only incorporates prices, the Money Flow
/// Index accounts for volume.
/// ---------------------------------------------------------
/// Input:
/// - 4 Y values ( High, Low, Close, Volume ).
/// Output:
/// - 1 Y value Money Flow Indicator.
/// Parameters:
/// - Period
/// </summary>
/// <param name="inputValues">Arrays of doubles</param>
/// <param name="outputValues">Arrays of doubles</param>
/// <param name="parameterList">Array of strings</param>
private void MoneyFlow(double [][] inputValues, out double [][] outputValues, string [] parameterList)
{
int length = inputValues.Length;
// There is no enough series
if( length != 5 )
throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresFourArrays);
// Different number of x and y values
CheckNumOfValues( inputValues, 4 );
// 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);
// Not enough values for Money Flow.
if( inputValues[0].Length < period )
throw new ArgumentException(SR.ExceptionPriceIndicatorsNotEnoughPoints);
outputValues = new double [2][];
outputValues[0] = new double [inputValues[0].Length - period + 1];
outputValues[1] = new double [inputValues[0].Length - period + 1];
double [] TypicalPrice = new double [inputValues[1].Length];
double [] MoneyFlow = new double [inputValues[1].Length];
double [] PositiveMoneyFlow = new double [inputValues[1].Length];
double [] NegativeMoneyFlow = new double [inputValues[1].Length];
// Find Money Flow
for( int index = 0; index < inputValues[1].Length; index++ )
{
// Find Typical Price
TypicalPrice[index] = (inputValues[1][index] + inputValues[2][index] + inputValues[3][index])/3.0;
// Find Money Flow
MoneyFlow[index] = (inputValues[1][index] + inputValues[2][index] + inputValues[3][index])/3.0 * inputValues[4][index];
}
// Find Money Flow
for( int index = 1; index < inputValues[1].Length; index++ )
{
// Positive Typical Price
if( TypicalPrice[index] > TypicalPrice[index - 1] )
{
PositiveMoneyFlow[index] = MoneyFlow[index];
NegativeMoneyFlow[index] = 0;
}
// Negative Typical Price
if( TypicalPrice[index] < TypicalPrice[index - 1] )
{
NegativeMoneyFlow[index] = MoneyFlow[index];
PositiveMoneyFlow[index] = 0;
}
}
double PosMoney = 0;
double NegMoney = 0;
for( int index = period - 1; index < inputValues[1].Length; index++ )
{
PosMoney = 0;
NegMoney = 0;
// Find Money flow using period
for( int periodIndex = index - period + 1; periodIndex <= index; periodIndex++ )
{
NegMoney += NegativeMoneyFlow[periodIndex];
PosMoney += PositiveMoneyFlow[periodIndex];
}
// X value
outputValues[0][index - period + 1] = inputValues[0][index];
// Money Flow Index
outputValues[1][index - period + 1] = 100.0 - 100.0 / ( 1.0 + (PosMoney / NegMoney) );
}
}
/// <summary>
/// The Price and Volume Trend ("PVT") is similar to
/// On Balance Volume ("OBV,") in that it is a cumulative
/// total of volume that is adjusted depending on changes
/// in closing prices. But where OBV adds all volume on days
/// when prices close higher and subtracts all volume on days
/// when prices close lower, the PVT adds/subtracts only
/// a portion of the daily volume. The amount of volume
/// added to the PVT is determined by the amount that prices
/// rose or fell relative to the previous days close.
/// The PVT is calculated by multiplying the days volume
/// by the percent that the securitys price changed, and
/// adding this value to a cumulative total.
/// ---------------------------------------------------------
/// Input:
/// - 2 Y values ( Close, Volume ).
/// Output:
/// - 1 Y value Price Volume Trend Indicator.
/// </summary>
/// <param name="inputValues">Arrays of doubles - Input values</param>
/// <param name="outputValues">Arrays of doubles - Output values</param>
private void PriceVolumeTrend(double [][] inputValues, out double [][] outputValues)
{
// There is no enough input series
if( inputValues.Length != 3 )
throw new ArgumentException(SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays);
// Different number of x and y values
CheckNumOfValues( inputValues, 2 );
outputValues = new double [2][];
outputValues[0] = new double [inputValues[0].Length];
outputValues[1] = new double [inputValues[0].Length];
// Set X and Y zero values
outputValues[0][0] = inputValues[0][0];
outputValues[1][0] = 0;
double yesterdayClose;
double todayClose;
// Price Volume Trend Indicator
for( int index = 1; index < inputValues[1].Length; index++ )
{
// Set X values
outputValues[0][index] = inputValues[0][index];
// Set Y values
yesterdayClose = inputValues[1][index-1];
todayClose = inputValues[1][index];
// Price Volume Trend for one point
outputValues[1][index] = ( todayClose - yesterdayClose ) / yesterdayClose * inputValues[2][index] + outputValues[1][index-1];
}
}
/// <summary>
/// On Balance Volume ("OBV") is a momentum indicator that
/// relates volume to price change. OBV is one of the most
/// popular volume indicators and was developed by
/// Joseph Granville. Constructing an OBV line is very
/// simple: The total volume for each day is assigned a
/// positive or negative value depending on whether prices
/// closed higher or lower that day. A higher close results
/// in the volume for that day to get a positive value, while
/// a lower close results in negative value. A running total
/// is kept by adding or subtracting each day's volume based
/// on the direction of the close. The direction of the OBV
/// line is the thing to watch, not the actual volume numbers.
/// ---------------------------------------------------------
/// Input:
/// - 2 Y values ( Close, Volume ).
/// Output:
/// - 1 Y value On Balance Volume Indicator.
/// </summary>
/// <param name="inputValues">Arrays of doubles - Input values</param>
/// <param name="outputValues">Arrays of doubles - Output values</param>
private void OnBalanceVolume(double [][] inputValues, out double [][] outputValues)
{
// There is no enough input series
if( inputValues.Length != 3 )
throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays);
// Different number of x and y values
CheckNumOfValues( inputValues, 2 );
outputValues = new double [2][];
outputValues[0] = new double [inputValues[0].Length];
outputValues[1] = new double [inputValues[0].Length];
outputValues[0][0] = inputValues[0][0];
outputValues[1][0] = inputValues[2][0];
// Find On Balance Volume
for( int index = 1; index < inputValues[1].Length; index++ )
{
// Set X values
outputValues[0][index] = inputValues[0][index];
// Set Y Values
// If todays close is greater than yesterdays close then
if( inputValues[1][index - 1] < inputValues[1][index] )
outputValues[1][index] = outputValues[1][index - 1] + inputValues[2][index];
// If todays close is less than yesterdays close then
else if( inputValues[1][index - 1] > inputValues[1][index] )
outputValues[1][index] = outputValues[1][index - 1] - inputValues[2][index];
// If todays close is equal to yesterdays close then
else
outputValues[1][index] = outputValues[1][index - 1];
}
}
/// <summary>
/// The Negative Volume Index ("NVI") focuses on days where
/// the volume decreases from the previous day. The premise
/// being that the "smart money" takes positions on days when
/// volume decreases.
/// ---------------------------------------------------------
/// Input:
/// - 2 Y values ( Close, Volume ).
/// Output:
/// - 1 Y value Negative Volume index.
/// Parameters:
/// - StartValue : double
/// </summary>
/// <param name="inputValues">Arrays of doubles - Input values</param>
/// <param name="outputValues">Arrays of doubles - Output values</param>
/// <param name="parameterList">Array of strings - Parameters</param>
private void NegativeVolumeIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
{
// There is no enough input series
if( inputValues.Length != 3 )
throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays);
// Different number of x and y values
CheckNumOfValues( inputValues, 2 );
// Start Value
double startValue;
try
{startValue = double.Parse( parameterList[0], System.Globalization.CultureInfo.InvariantCulture );}
catch(System.Exception)
{ throw new InvalidOperationException(SR.ExceptionVolumeIndicatorStartValueMissing); }
outputValues = new double [2][];
outputValues[0] = new double [inputValues[0].Length];
outputValues[1] = new double [inputValues[0].Length];
outputValues[0][0] = inputValues[0][0];
outputValues[1][0] = startValue;
// Find Negative Volume Index
for( int index = 1; index < inputValues[1].Length; index++ )
{
// Set X values
outputValues[0][index] = inputValues[0][index];
// If todays volume is less than yesterdays volume then
if( inputValues[2][index] < inputValues[2][index-1] )
{
double yesterdayClose = inputValues[1][index-1];
double todayClose = inputValues[1][index];
outputValues[1][index] = ( todayClose - yesterdayClose ) / yesterdayClose * outputValues[1][index-1] + outputValues[1][index-1];
}
// If todays volume is greater than or equal to yesterdays volume then:
else
outputValues[1][index] = outputValues[1][index-1];
}
}
/// <summary>
/// The Positive Volume Index ("PVI") focuses on days where
/// the volume increased from the previous day. The premise
/// being that the "crowd" takes positions on days when
/// volume increases. Interpretation of the PVI assumes that
/// on days when volume increases, the crowd-following
/// "uninformed" investors are in the market. Conversely, on
/// days with decreased volume, the "smart money" is quietly
/// taking positions. Thus, the PVI displays what the
/// not-so-smart-money is doing. (The Negative Volume Index,
/// displays what the smart money is doing.) Note, however,
/// that the PVI is not a contrarian indicator. Even though
/// the PVI is supposed to show what the not-so-smart-money
/// is doing, it still trends in the same direction as prices.
/// ---------------------------------------------------------
/// Input:
/// - 2 Y values ( Close, Volume ).
/// Output:
/// - 1 Y value On Positive Volume index.
/// Parameters:
/// - StartValue : double
/// </summary>
/// <param name="inputValues">Arrays of doubles - Input values</param>
/// <param name="outputValues">Arrays of doubles - Output values</param>
/// <param name="parameterList">Array of strings - Parameters</param>
private void PositiveVolumeIndex(double [][] inputValues, out double [][] outputValues, string [] parameterList)
{
// There is no enough input series
if( inputValues.Length != 3 )
throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresTwoArrays);
// Different number of x and y values
CheckNumOfValues( inputValues, 2 );
// Start Value
double startValue;
try
{startValue = double.Parse( parameterList[0], System.Globalization.CultureInfo.InvariantCulture );}
catch(System.Exception)
{ throw new InvalidOperationException(SR.ExceptionVolumeIndicatorStartValueMissing); }
outputValues = new double [2][];
outputValues[0] = new double [inputValues[0].Length];
outputValues[1] = new double [inputValues[0].Length];
outputValues[0][0] = inputValues[0][0];
outputValues[1][0] = startValue;
// Find Negative Volume Index
for( int index = 1; index < inputValues[1].Length; index++ )
{
// Set X values
outputValues[0][index] = inputValues[0][index];
// If todays volume is greater than yesterdays volume then
if( inputValues[2][index] > inputValues[2][index-1] )
{
double yesterdayClose = inputValues[1][index-1];
double todayClose = inputValues[1][index];
outputValues[1][index] = ( todayClose - yesterdayClose ) / yesterdayClose * outputValues[1][index-1] + outputValues[1][index-1];
}
// If todays volume is less than or equal to yesterdays volume then:
else
outputValues[1][index] = outputValues[1][index-1];
}
}
/// <summary>
/// The Accumulation/Distribution is a momentum indicator that
/// associates changes in price and volume. The indicator is
/// based on the premise that the more volume that accompanies
/// a price move, the more significant the price move. A portion
/// of each days volume is added or subtracted from
/// a cumulative total. The nearer the closing price is to
/// the high for the day, the more volume added to
/// the cumulative total. The nearer the closing price is to
/// the low for the day, the more volume subtracted from the
/// cumulative total. If the close is exactly between the high
/// and low prices, nothing is added to the cumulative total.
/// ---------------------------------------------------------
/// Input:
/// - 4 Y values ( Hi, Low, Close, Volume ).
/// Output:
/// - 1 Y value Accumulation Distribution
/// </summary>
/// <param name="inputValues">Arrays of doubles - Input values</param>
/// <param name="outputValues">Arrays of doubles - Output values</param>
internal void AccumulationDistribution(double [][] inputValues, out double [][] outputValues)
{
// There is no enough input series
if( inputValues.Length != 5 )
throw new ArgumentException( SR.ExceptionPriceIndicatorsFormulaRequiresFourArrays);
// Different number of x and y values
CheckNumOfValues( inputValues, 4 );
outputValues = new double [2][];
outputValues[0] = new double [inputValues[0].Length];
outputValues[1] = new double [inputValues[0].Length];
double [] distribution = new double [inputValues[0].Length];
// Set X and Y zero values
outputValues[0][0] = inputValues[0][0];
outputValues[1][0] = 0;
// Accumulation Distribution
for( int index = 0; index < inputValues[1].Length; index++ )
{
// Set X values
outputValues[0][index] = inputValues[0][index];
// Distribution {(Close - Low) - (High - Close)} / (High - Low) * Volume
distribution[index] = ((inputValues[3][index] - inputValues[2][index])-(inputValues[1][index] - inputValues[3][index]))/(inputValues[1][index] - inputValues[2][index])*inputValues[4][index];
}
// The Accumulation Distribution Index is calculated as a cumulative total of each day's reading
double sum = 0;
for( int index = 0; index < inputValues[1].Length; index++ )
{
sum += distribution[index];
outputValues[1][index] = sum;
}
}
#endregion
}
}