//------------------------------------------------------------- // // 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 }