//-------------------------------------------------------------
//
// Copyright © Microsoft Corporation. All Rights Reserved.
//
//-------------------------------------------------------------
// @owner=victark, alexgor, deliant
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Diagnostics;
#if Microsoft_CONTROL
namespace System.Windows.Forms.DataVisualization.Charting.Formulas
#else
namespace System.Web.UI.DataVisualization.Charting.Formulas
#endif
{
#region class FormulaHelper
///
/// Formula helper is a static utility class implementing common formula related routines.
///
internal static class FormulaHelper
{
#region Static
///
/// Gets the formula info instance.
///
/// The formula.
/// FomulaInfo instance
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
internal static FormulaInfo GetFormulaInfo(FinancialFormula formula)
{
switch (formula)
{
//Price indicators
case FinancialFormula.MovingAverage:
return new MovingAverageFormulaInfo();
case FinancialFormula.ExponentialMovingAverage:
return new ExponentialMovingAverageFormulaInfo();
case FinancialFormula.WeightedMovingAverage:
return new WeightedMovingAverageFormulaInfo();
case FinancialFormula.TriangularMovingAverage:
return new TriangularMovingAverageFormulaInfo();
case FinancialFormula.TripleExponentialMovingAverage:
return new TripleExponentialMovingAverageFormulaInfo();
case FinancialFormula.BollingerBands:
return new BollingerBandsFormulaInfo();
case FinancialFormula.TypicalPrice:
return new TypicalPriceFormulaInfo();
case FinancialFormula.WeightedClose:
return new WeightedCloseFormulaInfo();
case FinancialFormula.MedianPrice:
return new MedianPriceFormulaInfo();
case FinancialFormula.Envelopes:
return new EnvelopesFormulaInfo();
case FinancialFormula.StandardDeviation:
return new StandardDeviationFormulaInfo();
// Oscilators
case FinancialFormula.ChaikinOscillator:
return new ChaikinOscillatorFormulaInfo();
case FinancialFormula.DetrendedPriceOscillator:
return new DetrendedPriceOscillatorFormulaInfo();
case FinancialFormula.VolatilityChaikins:
return new VolatilityChaikinsFormulaInfo();
case FinancialFormula.VolumeOscillator:
return new VolumeOscillatorFormulaInfo();
case FinancialFormula.StochasticIndicator:
return new StochasticIndicatorFormulaInfo();
case FinancialFormula.WilliamsR:
return new WilliamsRFormulaInfo();
// General technical indicators
case FinancialFormula.AverageTrueRange:
return new AverageTrueRangeFormulaInfo();
case FinancialFormula.EaseOfMovement:
return new EaseOfMovementFormulaInfo();
case FinancialFormula.MassIndex:
return new MassIndexFormulaInfo();
case FinancialFormula.Performance:
return new PerformanceFormulaInfo();
case FinancialFormula.RateOfChange:
return new RateOfChangeFormulaInfo();
case FinancialFormula.RelativeStrengthIndex:
return new RelativeStrengthIndexFormulaInfo();
case FinancialFormula.MovingAverageConvergenceDivergence:
return new MovingAverageConvergenceDivergenceFormulaInfo();
case FinancialFormula.CommodityChannelIndex:
return new CommodityChannelIndexFormulaInfo();
// Forecasting
case FinancialFormula.Forecasting:
return new ForecastingFormulaInfo();
// Volume Indicators
case FinancialFormula.MoneyFlow:
return new MoneyFlowFormulaInfo();
case FinancialFormula.PriceVolumeTrend:
return new PriceVolumeTrendFormulaInfo();
case FinancialFormula.OnBalanceVolume:
return new OnBalanceVolumeFormulaInfo();
case FinancialFormula.NegativeVolumeIndex:
return new NegativeVolumeIndexFormulaInfo();
case FinancialFormula.PositiveVolumeIndex:
return new PositiveVolumeIndexFormulaInfo();
case FinancialFormula.AccumulationDistribution:
return new AccumulationDistributionFormulaInfo();
default:
Debug.Fail(String.Format(CultureInfo.InvariantCulture, "{0} case is not defined", formula));
return null;
}
}
///
/// Gets the data fields of the specified chart type.
///
/// Type of the chart.
/// Data fields
internal static IList GetDataFields(SeriesChartType chartType)
{
switch (chartType)
{
case SeriesChartType.BoxPlot:
return new DataField[] {
DataField.LowerWisker, DataField.UpperWisker,
DataField.LowerBox, DataField.UpperBox,
DataField.Average, DataField.Median };
case SeriesChartType.Bubble:
return new DataField[] {
DataField.Bubble, DataField.BubbleSize };
case SeriesChartType.Candlestick:
case SeriesChartType.Stock:
return new DataField[] {
DataField.High, DataField.Low,
DataField.Open, DataField.Close };
case SeriesChartType.ErrorBar:
return new DataField[] {
DataField.Center,
DataField.LowerError, DataField.UpperError};
case SeriesChartType.RangeBar:
case SeriesChartType.Range:
case SeriesChartType.RangeColumn:
case SeriesChartType.SplineRange:
return new DataField[] {
DataField.Top, DataField.Bottom };
default:
return new DataField[] { DataField.Y };
}
}
///
/// Gets the default type of the chart associated with this field name.
///
/// The field.
///
internal static SeriesChartType GetDefaultChartType(DataField field)
{
switch (field)
{
default:
case DataField.Y:
return SeriesChartType.Line;
case DataField.LowerWisker:
case DataField.UpperWisker:
case DataField.LowerBox:
case DataField.UpperBox:
case DataField.Average:
case DataField.Median:
return SeriesChartType.BoxPlot;
case DataField.Bubble:
case DataField.BubbleSize:
return SeriesChartType.Bubble;
case DataField.High:
case DataField.Low:
case DataField.Open:
case DataField.Close:
return SeriesChartType.Stock;
case DataField.Center:
case DataField.LowerError:
case DataField.UpperError:
return SeriesChartType.ErrorBar;
case DataField.Top:
case DataField.Bottom:
return SeriesChartType.Range;
}
}
///
/// Maps formula data field to a chart type specific data field.
///
/// Type of the chart.
/// The formula field to be mapped.
/// The series field
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
internal static DataField? MapFormulaDataField(SeriesChartType chartType, DataField formulaField)
{
switch (formulaField)
{
case DataField.Top:
case DataField.High:
switch (chartType)
{
default: return null;
case SeriesChartType.BoxPlot: return DataField.UpperBox;
case SeriesChartType.Candlestick:
case SeriesChartType.Stock: return DataField.High;
case SeriesChartType.ErrorBar: return DataField.UpperError;
case SeriesChartType.RangeBar:
case SeriesChartType.Range:
case SeriesChartType.RangeColumn:
case SeriesChartType.SplineRange: return DataField.Top;
}
case DataField.Bottom:
case DataField.Low:
switch (chartType)
{
default: return null;
case SeriesChartType.BoxPlot: return DataField.LowerBox;
case SeriesChartType.Candlestick:
case SeriesChartType.Stock: return DataField.Low;
case SeriesChartType.ErrorBar: return DataField.LowerError;
case SeriesChartType.RangeBar:
case SeriesChartType.Range:
case SeriesChartType.RangeColumn:
case SeriesChartType.SplineRange: return DataField.Bottom;
}
case DataField.Open:
switch (chartType)
{
default: return null;
case SeriesChartType.BoxPlot: return DataField.Average;
case SeriesChartType.Candlestick:
case SeriesChartType.Stock: return DataField.Open;
case SeriesChartType.ErrorBar: return DataField.Center;
case SeriesChartType.RangeBar:
case SeriesChartType.Range:
case SeriesChartType.RangeColumn:
case SeriesChartType.SplineRange: return DataField.Bottom;
}
case DataField.Close:
case DataField.Y:
switch (chartType)
{
default: return DataField.Y;
case SeriesChartType.BoxPlot: return DataField.Average;
case SeriesChartType.Bubble: return DataField.Bubble;
case SeriesChartType.Candlestick:
case SeriesChartType.Stock: return DataField.Close;
case SeriesChartType.ErrorBar: return DataField.Center;
case SeriesChartType.RangeBar:
case SeriesChartType.Range:
case SeriesChartType.RangeColumn:
case SeriesChartType.SplineRange: return DataField.Top;
}
default:
return null;
}
}
#endregion
}
#endregion
#region class FormulaInfo and inherited FormulaSpecific classes
///
/// This a base class of the formula metainfo classes.
///
internal abstract class FormulaInfo
{
#region Fields
DataField[] _inputFields;
DataField[] _outputFields;
object[] _parameters;
#endregion
#region Properties
///
/// Gets the input data fields of the formula.
///
/// The input fields.
public DataField[] InputFields
{
get { return _inputFields; }
}
///
/// Gets the output data fields of the formula.
///
/// The output fields.
public DataField[] OutputFields
{
get { return _outputFields; }
}
///
/// Gets the parameters of the formula.
///
/// The parameters.
public object[] Parameters
{
get { return _parameters; }
}
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The input data fields.
/// The output data fields.
/// The default formula params.
public FormulaInfo(DataField[] inputFields, DataField[] outputFields, params object[] defaultParams)
{
_inputFields = inputFields;
_outputFields = outputFields;
_parameters = defaultParams;
}
#endregion
#region Methods
///
/// Saves the formula parameters to a string.
///
/// Csv string with parameters
internal virtual string SaveParametersToString()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < _parameters.Length; i++)
{
if (i > 0) sb.Append(',');
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", _parameters[i]);
}
return sb.ToString();
}
///
/// Loads the formula parameters from string.
///
/// Csv string with parameters.
internal virtual void LoadParametersFromString(string parameters)
{
if (String.IsNullOrEmpty(parameters))
return;
string[] paramStringList = parameters.Split(',');
int paramStringIndex = 0;
for (int i = 0; i < _parameters.Length && paramStringIndex < paramStringList.Length; i++)
{
string newParamValue = paramStringList[paramStringIndex++];
if (!String.IsNullOrEmpty(newParamValue))
{
_parameters[i] = ParseParameter(i, newParamValue);
}
}
}
///
/// Parses the formula parameter.
///
/// The param index.
/// The parameter value string.
/// Parameter value.
internal virtual object ParseParameter(int index, string newParamValue)
{
object param = _parameters[index];
if (param is int)
{
return Convert.ToInt32(newParamValue, CultureInfo.InvariantCulture);
}
else if (param is bool)
{
return Convert.ToBoolean(newParamValue, CultureInfo.InvariantCulture);
}
else if (param is double)
{
return Convert.ToDouble(newParamValue, CultureInfo.InvariantCulture);
}
return null;
}
///
/// Checks the formula parameter string.
///
/// The parameters.
internal virtual void CheckParameterString(string parameters)
{
if (String.IsNullOrEmpty(parameters))
return;
string[] paramStringList = parameters.Split(',');
int paramStringIndex = 0;
for (int i = 0; i < _parameters.Length && paramStringIndex < paramStringList.Length; i++)
{
string newParamValue = paramStringList[paramStringIndex++];
if (!String.IsNullOrEmpty(newParamValue))
{
try
{
ParseParameter(i, newParamValue);
}
catch (FormatException)
{
throw new ArgumentException(SR.ExceptionFormulaDataFormatInvalid(parameters));
}
}
}
}
#endregion
}
///
/// MovingAverage FormulaInfo
///
internal class MovingAverageFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public MovingAverageFormulaInfo()
: this(2, false) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// if set to true [start from first].
public MovingAverageFormulaInfo(int period, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, startFromFirst)
{
}
}
///
/// ExponentialMoving AverageFormulaInfo
///
internal class ExponentialMovingAverageFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public ExponentialMovingAverageFormulaInfo()
: this(2, false) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// if set to true [start from first].
public ExponentialMovingAverageFormulaInfo(int period, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, startFromFirst)
{
}
}
///
/// WeightedMovingAverageFormulaInfo
///
internal class WeightedMovingAverageFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public WeightedMovingAverageFormulaInfo()
: this(2, false) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// if set to true [start from first].
public WeightedMovingAverageFormulaInfo(int period, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, startFromFirst)
{
}
}
///
/// TriangularMovingAverage FormulaInfo
///
internal class TriangularMovingAverageFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public TriangularMovingAverageFormulaInfo()
: this(2, false) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// if set to true [start from first].
public TriangularMovingAverageFormulaInfo(int period, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, startFromFirst)
{
}
}
///
/// TripleExponentialMovingAverage FormulaInfo
///
internal class TripleExponentialMovingAverageFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public TripleExponentialMovingAverageFormulaInfo()
: this(12) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
public TripleExponentialMovingAverageFormulaInfo(int period)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period)
{
}
}
///
/// BollingerBands FormulaInfo
///
internal class BollingerBandsFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public BollingerBandsFormulaInfo()
: this(3, 2, true) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// The deviation.
/// if set to true [start from first].
public BollingerBandsFormulaInfo(int period, double deviation, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Top, DataField.Bottom }, //Output fields
period, deviation, startFromFirst)
{
}
}
///
/// TypicalPrice FormulaInfo
///
internal class TypicalPriceFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public TypicalPriceFormulaInfo()
: base(
new DataField[] { DataField.Close, DataField.High, DataField.Low }, //Input fields
new DataField[] { DataField.Y }) //Output fields
{
}
}
///
/// WeightedClose FormulaInfo
///
internal class WeightedCloseFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public WeightedCloseFormulaInfo()
: base(
new DataField[] { DataField.Close, DataField.High, DataField.Low }, //Input fields
new DataField[] { DataField.Y }) //Output fields
{
}
}
///
/// MedianPrice FormulaInfo
///
internal class MedianPriceFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public MedianPriceFormulaInfo()
: base(
new DataField[] { DataField.High, DataField.Low }, //Input fields
new DataField[] { DataField.Y }) //Output fields
{
}
}
///
/// Envelopes FormulaInfo
///
internal class EnvelopesFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public EnvelopesFormulaInfo()
: this(2, 10, true) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// The shift percentage.
/// if set to true [start from first].
public EnvelopesFormulaInfo(int period, double shiftPercentage, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Top, DataField.Bottom }, //Output fields
period, shiftPercentage, startFromFirst)
{
}
}
///
/// StandardDeviation FormulaInfo
///
internal class StandardDeviationFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public StandardDeviationFormulaInfo()
: this(2, false) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// if set to true [start from first].
public StandardDeviationFormulaInfo(int period, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, startFromFirst)
{
}
}
///
/// ChaikinOscillatorFormulaInfo
///
internal class ChaikinOscillatorFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public ChaikinOscillatorFormulaInfo()
: this(3, 10, false) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The short period.
/// The long period.
/// if set to true [start from first].
public ChaikinOscillatorFormulaInfo(int shortPeriod, int longPeriod, bool startFromFirst)
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close, DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
shortPeriod, longPeriod, startFromFirst)
{
}
}
///
/// DetrendedPriceOscillator FormulaInfo
///
internal class DetrendedPriceOscillatorFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public DetrendedPriceOscillatorFormulaInfo()
: this(2, false) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// if set to true [start from first].
public DetrendedPriceOscillatorFormulaInfo(int period, bool startFromFirst)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, startFromFirst)
{
}
}
///
/// VolatilityChaikins FormulaInfo
///
internal class VolatilityChaikinsFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public VolatilityChaikinsFormulaInfo()
: this(10, 10) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// The signal period.
public VolatilityChaikinsFormulaInfo(int period, int signalPeriod)
: base(
new DataField[] { DataField.High, DataField.Low }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, signalPeriod)
{
}
}
///
/// VolumeOscillator FormulaInfo
///
internal class VolumeOscillatorFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public VolumeOscillatorFormulaInfo()
: this(5, 10, true) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The short period.
/// The long period.
/// if set to true [percentage].
public VolumeOscillatorFormulaInfo(int shortPeriod, int longPeriod, bool percentage)
: base(
new DataField[] { DataField.Y }, //Input fields
new DataField[] { DataField.Y }, //Output fields
shortPeriod, longPeriod, percentage)
{
}
}
///
/// StochasticIndicatorFormulaInfo
///
internal class StochasticIndicatorFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public StochasticIndicatorFormulaInfo()
: this(10, 10) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period D.
/// The period K.
public StochasticIndicatorFormulaInfo(int periodD, int periodK)
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
new DataField[] { DataField.Y, DataField.Y }, //Output fields
periodD, periodK)
{
}
}
///
/// WilliamsRFormulaInfo
///
internal class WilliamsRFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public WilliamsRFormulaInfo()
: this(14) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
public WilliamsRFormulaInfo(int period)
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period)
{
}
}
///
/// AverageTrueRange FormulaInfo
///
internal class AverageTrueRangeFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public AverageTrueRangeFormulaInfo()
: this(14) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
public AverageTrueRangeFormulaInfo(int period)
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period)
{
}
}
///
/// EaseOfMovement FormulaInfo
///
internal class EaseOfMovementFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public EaseOfMovementFormulaInfo()
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
new DataField[] { DataField.Y }) //Output fields
{
}
}
///
/// MassIndex FormulaInfo
///
internal class MassIndexFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public MassIndexFormulaInfo()
: this(25, 9) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
/// The average period.
public MassIndexFormulaInfo(int period, int averagePeriod)
: base(
new DataField[] { DataField.High, DataField.Low }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period, averagePeriod)
{
}
}
///
/// Performance FormulaInfo
///
internal class PerformanceFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public PerformanceFormulaInfo()
: base(
new DataField[] { DataField.Close }, //Input fields
new DataField[] { DataField.Y }) //Output fields
{
}
}
///
/// RateOfChange FormulaInfo
///
internal class RateOfChangeFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public RateOfChangeFormulaInfo()
: this(10) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
public RateOfChangeFormulaInfo(int period)
: base(
new DataField[] { DataField.Close }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period)
{
}
}
///
/// RelativeStrengthIndex FormulaInfo
///
internal class RelativeStrengthIndexFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public RelativeStrengthIndexFormulaInfo()
: this(10) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
public RelativeStrengthIndexFormulaInfo(int period)
: base(
new DataField[] { DataField.Close }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period)
{
}
}
///
/// MovingAverageConvergenceDivergence FormulaInfo
///
internal class MovingAverageConvergenceDivergenceFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public MovingAverageConvergenceDivergenceFormulaInfo()
: this(12, 26) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The short period.
/// The long period.
public MovingAverageConvergenceDivergenceFormulaInfo(int shortPeriod, int longPeriod)
: base(
new DataField[] { DataField.Close }, //Input fields
new DataField[] { DataField.Y }, //Output fields
shortPeriod, longPeriod)
{
}
}
///
/// CommodityChannelIndex FormulaInfo
///
internal class CommodityChannelIndexFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public CommodityChannelIndexFormulaInfo()
: this(10) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
public CommodityChannelIndexFormulaInfo(int period)
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
new DataField[] { DataField.Y }, //Output fields
period)
{
}
}
///
/// Forecasting FormulaInfo
///
internal class ForecastingFormulaInfo : FormulaInfo
{
//Fields
string _parameters;
//Constructor
///
/// Initializes a new instance of the class.
///
public ForecastingFormulaInfo()
: this(TimeSeriesAndForecasting.RegressionType.Polynomial, 2, 0, true, true) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// Type of the regression.
/// The polynomial degree.
/// The forecasting period.
/// if set to true [return approximation error].
/// if set to true [return forecasting error].
public ForecastingFormulaInfo(TimeSeriesAndForecasting.RegressionType regressionType, int polynomialDegree, int forecastingPeriod, bool returnApproximationError, bool returnForecastingError)
: base(
new DataField[] { DataField.Close }, //Input fields
new DataField[] { DataField.Close, DataField.High, DataField.Low }, //Output fields
regressionType, polynomialDegree, forecastingPeriod, returnApproximationError, returnForecastingError)
{
}
//Methods
///
/// Loads the formula parameters from string.
///
/// Csv string with parameters.
internal override void LoadParametersFromString(string parameters)
{
_parameters = parameters;
}
///
/// Checks the formula parameter string.
///
/// The parameters.
internal override void CheckParameterString(string parameters)
{
if (String.IsNullOrEmpty(parameters))
return;
string[] paramStringList = parameters.Split(',');
int paramStringIndex = 1;
//Don't check the first param
for (int i = 2; i < Parameters.Length && paramStringIndex < paramStringList.Length; i++)
{
string newParamValue = paramStringList[paramStringIndex++];
if (!String.IsNullOrEmpty(newParamValue))
{
try
{
ParseParameter(i, newParamValue);
}
catch (FormatException)
{
throw new ArgumentException(SR.ExceptionFormulaDataFormatInvalid(parameters));
}
}
}
}
///
/// Saves the formula parameters to a string.
///
/// Csv string with parameters
internal override string SaveParametersToString()
{
if (String.IsNullOrEmpty(_parameters))
return _parameters;
else
return "2,0,true,true";
}
}
///
/// MoneyFlow FormulaInfo
///
internal class MoneyFlowFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public MoneyFlowFormulaInfo()
: this(2) //Defaults
{
}
///
/// Initializes a new instance of the class.
///
/// The period.
public MoneyFlowFormulaInfo(int period)
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close, DataField.Y }, //Input fields: High,Low,Close,Volume
new DataField[] { DataField.Y }, //Output fields
period)
{
}
}
///
/// PriceVolumeTrend FormulaInfo
///
internal class PriceVolumeTrendFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public PriceVolumeTrendFormulaInfo()
: base(
new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
new DataField[] { DataField.Y }) //Output fields
{
}
}
///
/// OnBalanceVolume FormulaInfo
///
internal class OnBalanceVolumeFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public OnBalanceVolumeFormulaInfo()
: base(
new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
new DataField[] { DataField.Y }) //Output fields
{
}
}
///
/// NegativeVolumeIndex FormulaInfo
///
internal class NegativeVolumeIndexFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public NegativeVolumeIndexFormulaInfo() //Note about parameters: Start value is mandatory so we don't provide the default
: this(double.NaN)
{
}
///
/// Initializes a new instance of the class.
///
/// The start value.
public NegativeVolumeIndexFormulaInfo(double startValue)
: base(
new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
new DataField[] { DataField.Y },
startValue) //Output fields
{
}
}
///
/// PositiveVolumeIndex FormulaInfo
///
internal class PositiveVolumeIndexFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public PositiveVolumeIndexFormulaInfo() //Note about parameters: Start value is mandatory so we don't provide the default
: this(double.NaN)
{
}
///
/// Initializes a new instance of the class.
///
/// The start value.
public PositiveVolumeIndexFormulaInfo(double startValue)
: base(
new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
new DataField[] { DataField.Y },
startValue) //Output fields
{
}
}
///
/// AccumulationDistribution FormulaInfo
///
internal class AccumulationDistributionFormulaInfo : FormulaInfo
{
//Constructor
///
/// Initializes a new instance of the class.
///
public AccumulationDistributionFormulaInfo() //Note about parameters: Start value is mandatory so we don't provide the default
: base(
new DataField[] { DataField.High, DataField.Low, DataField.Close, DataField.Y }, //Input=High, Low, Close, Volume
new DataField[] { DataField.Y }) //Output fields
{
}
}
#endregion
#region enum DataField
///
/// Chart data fields
///
internal enum DataField
{
X,
Y,
LowerWisker,
UpperWisker,
LowerBox,
UpperBox,
Average,
Median,
Bubble,
BubbleSize,
High,
Low,
Open,
Close,
Center,
LowerError,
UpperError,
Top,
Bottom
}
#endregion
#region class SeriesFieldInfo
///
/// SeriesFieldInfo class is a OO representation formula input/output data params ("Series1:Y2")
///
internal class SeriesFieldInfo
{
#region Fields
private Series _series;
private string _seriesName;
private DataField _dataField;
#endregion
#region Properties
///
/// Gets the series.
///
/// The series.
public Series Series
{
get { return _series; }
}
///
/// Gets the name of the series.
///
/// The name of the series.
public string SeriesName
{
get { return _series != null ? _series.Name : _seriesName; }
}
///
/// Gets the data field.
///
/// The data field.
public DataField DataField
{
get { return _dataField; }
}
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The series.
/// The data field.
public SeriesFieldInfo(Series series, DataField dataField)
{
_series = series;
_dataField = dataField;
}
///
/// Initializes a new instance of the class.
///
/// Name of the series.
/// The data field.
public SeriesFieldInfo(string seriesName, DataField dataField)
{
_seriesName = seriesName;
_dataField = dataField;
}
#endregion
}
#endregion
#region class SeriesFieldList
///
/// SeriesFieldInfo class is a OO representation formula input/output data params ("Series1:Y2,Series2.Y4")
///
internal class SeriesFieldList : List
{
///
/// Returns a that represents the current .
///
///
/// A that represents the current .
///
public override string ToString()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < this.Count; i++)
{
SeriesFieldInfo info = this[i];
if (i > 0)
sb.Append(',');
SeriesChartType seriesChartType = info.Series != null ?
info.Series.ChartType :
FormulaHelper.GetDefaultChartType(info.DataField);
IList dataFields = FormulaHelper.GetDataFields(seriesChartType);
int dataFieldIndex = dataFields.IndexOf(info.DataField);
if (dataFieldIndex == 0)
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}:Y", info.SeriesName); //The string field descriptor is 1 based ;-(
else
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}:Y{1}", info.SeriesName, dataFieldIndex + 1); //The string field descriptor is 1 based ;-(
}
return sb.ToString();
}
//Static
///
/// Parse the string defining the formula's input/output series and fields.
///
/// The chart.
/// The series fields list. The series name can be followed by the field names. For example: "Series1:Y,Series1:Y3,Series2:Close"
/// The formula fields list.
///
public static SeriesFieldList FromString(Chart chart, string seriesFields, IList formulaFields)
{
SeriesFieldList result = new SeriesFieldList();
if (String.IsNullOrEmpty(seriesFields))
{
return result;
}
List unmappedFormulaFields = new List(formulaFields);
//Loop through the series/field pairs
foreach (string seriesField in seriesFields.Split(','))
{
//Stop processing if all the formula fields are mapped
if (unmappedFormulaFields.Count == 0)
break;
//Split a pair into a series + field
string[] seriesFieldParts = seriesField.Split(':');
if (seriesFieldParts.Length > 2)
{
throw new ArgumentException(SR.ExceptionFormulaDataFormatInvalid(seriesField));
}
//Get the series and series fields
string seriesName = seriesFieldParts[0].Trim();
Series series = chart.Series.FindByName(seriesName);
if (series != null)
{
switch (seriesFieldParts.Length)
{
case 1: //Only series name is specified: "Series1"
AddSeriesFieldInfo(result, series, unmappedFormulaFields);
break;
case 2: //Series and field names are provided: "Series1:Y3"
AddSeriesFieldInfo(result, series, unmappedFormulaFields, seriesFieldParts[1]);
break;
}
}
else
{
switch (seriesFieldParts.Length)
{
case 1: //Only series name is specified: "Series1"
AddSeriesFieldInfo(result, seriesName, unmappedFormulaFields);
break;
case 2: //Series and field names are provided: "Series1:Y3"
AddSeriesFieldInfo(result, seriesName, unmappedFormulaFields, seriesFieldParts[1]);
break;
}
}
}
return result;
}
///
/// Adds the series field info.
///
/// The result.
/// The series.
/// The unmapped formula fields.
private static void AddSeriesFieldInfo(SeriesFieldList result, Series series, IList unmappedFormulaFields)
{
List seriesFields = new List(FormulaHelper.GetDataFields(series.ChartType));
for (int i = 0; i < unmappedFormulaFields.Count && seriesFields.Count > 0; )
{
DataField formulaField = unmappedFormulaFields[i];
DataField? seriesField = null;
// Case 1. Check if the formulaField is valid for this chart type
if (seriesFields.Contains(formulaField))
{
seriesField = formulaField;
}
// Case 2. Try to map the formula field to the series field
if (seriesField == null)
{
seriesField = FormulaHelper.MapFormulaDataField(series.ChartType, formulaField);
}
// If the seriesField is found - add it to the results
if (seriesField != null)
{
result.Add(new SeriesFieldInfo(series, (DataField)seriesField));
seriesFields.Remove((DataField)formulaField);
unmappedFormulaFields.Remove(formulaField);
}
else
{
i++;
}
}
}
///
/// Adds the series field info.
///
/// The result.
/// The series.
/// The unmapped formula fields.
/// The series field id.
private static void AddSeriesFieldInfo(SeriesFieldList result, Series series, IList unmappedFormulaFields, string seriesFieldId)
{
IList seriesFields = FormulaHelper.GetDataFields(series.ChartType);
DataField? seriesField = null;
seriesFieldId = seriesFieldId.ToUpperInvariant().Trim();
if (seriesFieldId == "Y")
{
seriesField = seriesFields[0];
}
else if (seriesFieldId.StartsWith("Y", StringComparison.Ordinal))
{
int id = 0;
if (int.TryParse(seriesFieldId.Substring(1), out id))
if (id - 1 < seriesFields.Count)
{
seriesField = seriesFields[id - 1];
}
else
{
throw (new ArgumentException(SR.ExceptionFormulaYIndexInvalid, seriesFieldId));
}
}
else
{
seriesField = (DataField)Enum.Parse(typeof(DataField), seriesFieldId, true);
}
// Add the seriesField to the results
if (seriesField != null)
{
result.Add(new SeriesFieldInfo(series, (DataField)seriesField));
if (unmappedFormulaFields.Contains((DataField)seriesField))
unmappedFormulaFields.Remove((DataField)seriesField);
else
unmappedFormulaFields.RemoveAt(0);
}
else
{
throw new ArgumentException(SR.ExceptionDataPointValueNameInvalid, seriesFieldId);
}
}
///
/// Adds the series field info.
///
/// The result.
/// Name of the series.
/// The unmapped formula fields.
private static void AddSeriesFieldInfo(SeriesFieldList result, string seriesName, IList unmappedFormulaFields)
{
SeriesChartType chartType = FormulaHelper.GetDefaultChartType(unmappedFormulaFields[0]);
List seriesFields = new List(FormulaHelper.GetDataFields(chartType));
for (int i = 0; i < unmappedFormulaFields.Count && seriesFields.Count > 0; )
{
DataField formulaField = unmappedFormulaFields[i];
DataField? seriesField = null;
// Check if the formulaField is valid for this chart type
if (seriesFields.Contains(formulaField))
{
seriesField = formulaField;
}
// If the seriesField is found - add it to the results
if (seriesField != null)
{
result.Add(new SeriesFieldInfo(seriesName, (DataField)seriesField));
seriesFields.Remove((DataField)formulaField);
unmappedFormulaFields.Remove(formulaField);
}
else
{
i++;
}
}
}
///
/// Adds the series field info.
///
/// The result.
/// Name of the series.
/// The unmapped formula fields.
/// The series field id.
private static void AddSeriesFieldInfo(SeriesFieldList result, string seriesName, IList unmappedFormulaFields, string seriesFieldId)
{
SeriesChartType chartType = FormulaHelper.GetDefaultChartType(unmappedFormulaFields[0]);
IList seriesFields = FormulaHelper.GetDataFields(chartType);
//Find the field
DataField? seriesField = null;
seriesFieldId = seriesFieldId.ToUpperInvariant().Trim();
if (seriesFieldId == "Y")
{
seriesField = seriesFields[0];
}
else if (seriesFieldId.StartsWith("Y", StringComparison.Ordinal))
{
int seriesFieldIndex = 0;
if (int.TryParse(seriesFieldId.Substring(1), out seriesFieldIndex))
if (seriesFieldIndex < seriesFields.Count)
{
seriesField = seriesFields[seriesFieldIndex - 1];
}
else
{
throw (new ArgumentException(SR.ExceptionFormulaYIndexInvalid, seriesFieldId));
}
}
else
{
//Try parse the field name
try
{
seriesField = (DataField)Enum.Parse(typeof(DataField), seriesFieldId, true);
}
catch (ArgumentException)
{ }
}
if (seriesField != null)
{
result.Add(new SeriesFieldInfo(seriesName, (DataField)seriesField));
unmappedFormulaFields.Remove((DataField)seriesField);
}
else
{
throw new ArgumentException(SR.ExceptionDataPointValueNameInvalid, seriesFieldId);
}
}
}
#endregion
}