Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / Formulas / FormulaHelpers.cs
1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=victark, alexgor, deliant
7 using System;
8 using System.Collections.Generic;
9 using System.Text;
10 using System.Globalization;
11 using System.Diagnostics;
12
13 #if Microsoft_CONTROL
14 namespace System.Windows.Forms.DataVisualization.Charting.Formulas
15 #else
16 namespace System.Web.UI.DataVisualization.Charting.Formulas
17 #endif
18 {
19
20     #region class FormulaHelper
21     /// <summary>
22     /// Formula helper is a static utility class implementing common formula related routines.
23     /// </summary>
24     internal static class FormulaHelper
25     {
26         #region Static
27         /// <summary>
28         /// Gets the formula info instance.
29         /// </summary>
30         /// <param name="formula">The formula.</param>
31         /// <returns>FomulaInfo instance</returns>
32         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
33         internal static FormulaInfo GetFormulaInfo(FinancialFormula formula)
34         {
35             switch (formula)
36             {
37                 //Price indicators
38                 case FinancialFormula.MovingAverage:
39                     return new MovingAverageFormulaInfo();
40                 case FinancialFormula.ExponentialMovingAverage:
41                     return new ExponentialMovingAverageFormulaInfo();
42                 case FinancialFormula.WeightedMovingAverage:
43                     return new WeightedMovingAverageFormulaInfo();
44                 case FinancialFormula.TriangularMovingAverage:
45                     return new TriangularMovingAverageFormulaInfo();
46                 case FinancialFormula.TripleExponentialMovingAverage:
47                     return new TripleExponentialMovingAverageFormulaInfo();
48                 case FinancialFormula.BollingerBands:
49                     return new BollingerBandsFormulaInfo();
50                 case FinancialFormula.TypicalPrice:
51                     return new TypicalPriceFormulaInfo();
52                 case FinancialFormula.WeightedClose:
53                     return new WeightedCloseFormulaInfo();
54                 case FinancialFormula.MedianPrice:
55                     return new MedianPriceFormulaInfo();
56                 case FinancialFormula.Envelopes:
57                     return new EnvelopesFormulaInfo();
58                 case FinancialFormula.StandardDeviation:
59                     return new StandardDeviationFormulaInfo();
60
61                 // Oscilators
62                 case FinancialFormula.ChaikinOscillator:
63                     return new ChaikinOscillatorFormulaInfo();
64                 case FinancialFormula.DetrendedPriceOscillator:
65                     return new DetrendedPriceOscillatorFormulaInfo();
66                 case FinancialFormula.VolatilityChaikins:
67                     return new VolatilityChaikinsFormulaInfo();
68                 case FinancialFormula.VolumeOscillator:
69                     return new VolumeOscillatorFormulaInfo();
70                 case FinancialFormula.StochasticIndicator:
71                     return new StochasticIndicatorFormulaInfo();
72                 case FinancialFormula.WilliamsR:
73                     return new WilliamsRFormulaInfo();
74
75                 // General technical indicators
76                 case FinancialFormula.AverageTrueRange:
77                     return new AverageTrueRangeFormulaInfo();
78                 case FinancialFormula.EaseOfMovement:
79                     return new EaseOfMovementFormulaInfo();
80                 case FinancialFormula.MassIndex:
81                     return new MassIndexFormulaInfo();
82                 case FinancialFormula.Performance:
83                     return new PerformanceFormulaInfo();
84                 case FinancialFormula.RateOfChange:
85                     return new RateOfChangeFormulaInfo();
86                 case FinancialFormula.RelativeStrengthIndex:
87                     return new RelativeStrengthIndexFormulaInfo();
88                 case FinancialFormula.MovingAverageConvergenceDivergence:
89                     return new MovingAverageConvergenceDivergenceFormulaInfo();
90                 case FinancialFormula.CommodityChannelIndex:
91                     return new CommodityChannelIndexFormulaInfo();
92
93                 // Forecasting
94                 case FinancialFormula.Forecasting:
95                     return new ForecastingFormulaInfo();
96
97                 // Volume Indicators
98                 case FinancialFormula.MoneyFlow:
99                     return new MoneyFlowFormulaInfo();
100                 case FinancialFormula.PriceVolumeTrend:
101                     return new PriceVolumeTrendFormulaInfo();
102                 case FinancialFormula.OnBalanceVolume:
103                     return new OnBalanceVolumeFormulaInfo();
104                 case FinancialFormula.NegativeVolumeIndex:
105                     return new NegativeVolumeIndexFormulaInfo();
106                 case FinancialFormula.PositiveVolumeIndex:
107                     return new PositiveVolumeIndexFormulaInfo();
108                 case FinancialFormula.AccumulationDistribution:
109                     return new AccumulationDistributionFormulaInfo();
110
111                 default:
112                     Debug.Fail(String.Format(CultureInfo.InvariantCulture, "{0} case is not defined", formula));
113                     return null;
114             }
115         }
116
117         /// <summary>
118         /// Gets the data fields of the specified chart type.
119         /// </summary>
120         /// <param name="chartType">Type of the chart.</param>
121         /// <returns>Data fields</returns>
122         internal static IList<DataField> GetDataFields(SeriesChartType chartType)
123         {
124             switch (chartType)
125             {
126                 case SeriesChartType.BoxPlot:
127                     return new DataField[] { 
128                         DataField.LowerWisker, DataField.UpperWisker, 
129                         DataField.LowerBox, DataField.UpperBox, 
130                         DataField.Average, DataField.Median };
131                 case SeriesChartType.Bubble:
132                     return new DataField[] { 
133                         DataField.Bubble, DataField.BubbleSize };
134                 case SeriesChartType.Candlestick:
135                 case SeriesChartType.Stock:
136                     return new DataField[] { 
137                         DataField.High, DataField.Low,
138                         DataField.Open, DataField.Close };
139                 case SeriesChartType.ErrorBar:
140                     return new DataField[] { 
141                         DataField.Center, 
142                         DataField.LowerError, DataField.UpperError};
143                 case SeriesChartType.RangeBar:
144                 case SeriesChartType.Range:
145                 case SeriesChartType.RangeColumn:
146                 case SeriesChartType.SplineRange:
147                     return new DataField[] { 
148                         DataField.Top, DataField.Bottom };
149                 default:
150                     return new DataField[] { DataField.Y };
151             }
152         }
153
154         /// <summary>
155         /// Gets the default type of the chart associated with this field name.
156         /// </summary>
157         /// <param name="field">The field.</param>
158         /// <returns></returns>
159         internal static SeriesChartType GetDefaultChartType(DataField field)
160         {
161             switch (field)
162             {
163                 default:
164                 case DataField.Y:
165                     return SeriesChartType.Line;
166                 case DataField.LowerWisker:
167                 case DataField.UpperWisker:
168                 case DataField.LowerBox:
169                 case DataField.UpperBox:
170                 case DataField.Average:
171                 case DataField.Median:
172                     return SeriesChartType.BoxPlot;
173                 case DataField.Bubble:
174                 case DataField.BubbleSize:
175                     return SeriesChartType.Bubble;
176                 case DataField.High:
177                 case DataField.Low:
178                 case DataField.Open:
179                 case DataField.Close:
180                     return SeriesChartType.Stock;
181                 case DataField.Center:
182                 case DataField.LowerError:
183                 case DataField.UpperError:
184                     return SeriesChartType.ErrorBar;
185                 case DataField.Top:
186                 case DataField.Bottom:
187                     return SeriesChartType.Range;
188             }
189         }
190
191         /// <summary>
192         /// Maps formula data field to a chart type specific data field. 
193         /// </summary>
194         /// <param name="chartType">Type of the chart.</param>
195         /// <param name="formulaField">The formula field to be mapped.</param>
196         /// <returns>The series field</returns>
197         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
198         internal static DataField? MapFormulaDataField(SeriesChartType chartType, DataField formulaField)
199         {
200             switch (formulaField)
201             {
202                 case DataField.Top:
203                 case DataField.High:
204                     switch (chartType)
205                     {
206                         default: return null;
207                         case SeriesChartType.BoxPlot: return DataField.UpperBox;
208                         case SeriesChartType.Candlestick:
209                         case SeriesChartType.Stock: return DataField.High;
210                         case SeriesChartType.ErrorBar: return DataField.UpperError;
211                         case SeriesChartType.RangeBar:
212                         case SeriesChartType.Range:
213                         case SeriesChartType.RangeColumn:
214                         case SeriesChartType.SplineRange: return DataField.Top;
215                     }
216
217                 case DataField.Bottom:
218                 case DataField.Low:
219                     switch (chartType)
220                     {
221                         default: return null;
222                         case SeriesChartType.BoxPlot: return DataField.LowerBox;
223                         case SeriesChartType.Candlestick:
224                         case SeriesChartType.Stock: return DataField.Low;
225                         case SeriesChartType.ErrorBar: return DataField.LowerError;
226                         case SeriesChartType.RangeBar:
227                         case SeriesChartType.Range:
228                         case SeriesChartType.RangeColumn:
229                         case SeriesChartType.SplineRange: return DataField.Bottom;
230                     }
231
232                 case DataField.Open:
233                     switch (chartType)
234                     {
235                         default: return null;
236                         case SeriesChartType.BoxPlot: return DataField.Average;
237                         case SeriesChartType.Candlestick:
238                         case SeriesChartType.Stock: return DataField.Open;
239                         case SeriesChartType.ErrorBar: return DataField.Center;
240                         case SeriesChartType.RangeBar:
241                         case SeriesChartType.Range:
242                         case SeriesChartType.RangeColumn:
243                         case SeriesChartType.SplineRange: return DataField.Bottom;
244                     }
245
246                 case DataField.Close:
247                 case DataField.Y:
248                     switch (chartType)
249                     {
250                         default: return DataField.Y;
251                         case SeriesChartType.BoxPlot: return DataField.Average;
252                         case SeriesChartType.Bubble: return DataField.Bubble;
253                         case SeriesChartType.Candlestick:
254                         case SeriesChartType.Stock: return DataField.Close;
255                         case SeriesChartType.ErrorBar: return DataField.Center;
256                         case SeriesChartType.RangeBar:
257                         case SeriesChartType.Range:
258                         case SeriesChartType.RangeColumn:
259                         case SeriesChartType.SplineRange: return DataField.Top;
260                     }
261                 default:
262                     return null;
263             }
264         }
265
266         #endregion
267     }
268     #endregion
269
270     #region class FormulaInfo and inherited FormulaSpecific classes
271
272     /// <summary>
273     /// This a base class of the formula metainfo classes.
274     /// </summary>
275     internal abstract class FormulaInfo
276     {
277         #region Fields
278         DataField[] _inputFields;
279         DataField[] _outputFields;
280         object[] _parameters;
281         #endregion
282
283         #region Properties
284         /// <summary>
285         /// Gets the input data fields of the formula.
286         /// </summary>
287         /// <value>The input fields.</value>
288         public DataField[] InputFields 
289         { 
290             get { return _inputFields; } 
291         }
292
293         /// <summary>
294         /// Gets the output data fields of the formula.
295         /// </summary>
296         /// <value>The output fields.</value>
297         public DataField[] OutputFields 
298         { 
299             get { return _outputFields; } 
300         }
301
302         /// <summary>
303         /// Gets the parameters of the formula.
304         /// </summary>
305         /// <value>The parameters.</value>
306         public object[] Parameters 
307         { 
308             get { return _parameters; }
309         }
310         #endregion
311
312         #region Constructors
313         /// <summary>
314         /// Initializes a new instance of the <see cref="FormulaInfo"/> class.
315         /// </summary>
316         /// <param name="inputFields">The input data fields.</param>
317         /// <param name="outputFields">The output data fields.</param>
318         /// <param name="defaultParams">The default formula params.</param>
319         public FormulaInfo(DataField[] inputFields, DataField[] outputFields, params object[] defaultParams)
320         {
321             _inputFields = inputFields;
322             _outputFields = outputFields;
323             _parameters = defaultParams;
324         }
325         #endregion
326
327         #region Methods
328         /// <summary>
329         /// Saves the formula parameters to a string.
330         /// </summary>
331         /// <returns>Csv string with parameters</returns>
332         internal virtual string SaveParametersToString()
333         {
334             StringBuilder sb = new StringBuilder();
335             for (int i = 0; i < _parameters.Length; i++)
336             {
337                 if (i > 0) sb.Append(',');
338                 sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", _parameters[i]);
339             }
340             return sb.ToString();
341         }
342
343         /// <summary>
344         /// Loads the formula parameters from string.
345         /// </summary>
346         /// <param name="parameters">Csv string with parameters.</param>
347         internal virtual void LoadParametersFromString(string parameters)
348         {
349             if (String.IsNullOrEmpty(parameters))
350                 return;
351
352             string[] paramStringList = parameters.Split(',');
353             int paramStringIndex = 0;
354             for (int i = 0; i < _parameters.Length && paramStringIndex < paramStringList.Length; i++)
355             {
356                 string newParamValue = paramStringList[paramStringIndex++];
357                 if (!String.IsNullOrEmpty(newParamValue))
358                 {
359                     _parameters[i] = ParseParameter(i, newParamValue);
360                 }
361             }
362         }
363
364         /// <summary>
365         /// Parses the formula parameter.
366         /// </summary>
367         /// <param name="index">The param index.</param>
368         /// <param name="newParamValue">The parameter value string.</param>
369         /// <returns>Parameter value.</returns>
370         internal virtual object ParseParameter(int index, string newParamValue)
371         {
372             object param = _parameters[index];
373             if (param is int)
374             {
375                 return Convert.ToInt32(newParamValue, CultureInfo.InvariantCulture);
376             }
377             else if (param is bool)
378             {
379                 return Convert.ToBoolean(newParamValue, CultureInfo.InvariantCulture);
380             }
381             else if (param is double)
382             {
383                 return Convert.ToDouble(newParamValue, CultureInfo.InvariantCulture);
384             }
385             return null;
386         }
387
388         /// <summary>
389         /// Checks the formula parameter string.
390         /// </summary>
391         /// <param name="parameters">The parameters.</param>
392         internal virtual void CheckParameterString(string parameters)
393         {
394             if (String.IsNullOrEmpty(parameters))
395                 return;
396
397             string[] paramStringList = parameters.Split(',');
398             int paramStringIndex = 0;
399             for (int i = 0; i < _parameters.Length && paramStringIndex < paramStringList.Length; i++)
400             {
401                 string newParamValue = paramStringList[paramStringIndex++];
402                 if (!String.IsNullOrEmpty(newParamValue))
403                 {
404                     try
405                     {
406                         ParseParameter(i, newParamValue);
407                     }
408                     catch (FormatException)
409                     {
410                         throw new ArgumentException(SR.ExceptionFormulaDataFormatInvalid(parameters));
411                     }
412                 }
413             }
414         }
415         #endregion
416     }
417
418     /// <summary>
419     /// MovingAverage FormulaInfo
420     /// </summary>
421     internal class MovingAverageFormulaInfo : FormulaInfo
422     {
423         //Constructor
424         /// <summary>
425         /// Initializes a new instance of the <see cref="MovingAverageFormulaInfo"/> class.
426         /// </summary>
427         public MovingAverageFormulaInfo()
428             : this(2, false)                      //Defaults
429         {
430         }
431         /// <summary>
432         /// Initializes a new instance of the <see cref="MovingAverageFormulaInfo"/> class.
433         /// </summary>
434         /// <param name="period">The period.</param>
435         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
436         public MovingAverageFormulaInfo(int period, bool startFromFirst)
437             : base(
438                 new DataField[] { DataField.Y }, //Input fields
439                 new DataField[] { DataField.Y }, //Output fields
440                 period, startFromFirst)
441         {
442         }
443     }
444
445     /// <summary>
446     /// ExponentialMoving AverageFormulaInfo
447     /// </summary>
448     internal class ExponentialMovingAverageFormulaInfo : FormulaInfo
449     {
450         //Constructor
451         /// <summary>
452         /// Initializes a new instance of the <see cref="ExponentialMovingAverageFormulaInfo"/> class.
453         /// </summary>
454         public ExponentialMovingAverageFormulaInfo()
455             : this(2, false)                      //Defaults
456         {
457         }
458         /// <summary>
459         /// Initializes a new instance of the <see cref="ExponentialMovingAverageFormulaInfo"/> class.
460         /// </summary>
461         /// <param name="period">The period.</param>
462         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
463         public ExponentialMovingAverageFormulaInfo(int period, bool startFromFirst)
464             : base(
465                 new DataField[] { DataField.Y }, //Input fields
466                 new DataField[] { DataField.Y }, //Output fields
467                 period, startFromFirst)
468         {
469         }
470     }
471
472     /// <summary>
473     /// WeightedMovingAverageFormulaInfo
474     /// </summary>
475     internal class WeightedMovingAverageFormulaInfo : FormulaInfo
476     {
477         //Constructor
478         /// <summary>
479         /// Initializes a new instance of the <see cref="WeightedMovingAverageFormulaInfo"/> class.
480         /// </summary>
481         public WeightedMovingAverageFormulaInfo()
482             : this(2, false)                      //Defaults
483         {
484         }
485         /// <summary>
486         /// Initializes a new instance of the <see cref="WeightedMovingAverageFormulaInfo"/> class.
487         /// </summary>
488         /// <param name="period">The period.</param>
489         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
490         public WeightedMovingAverageFormulaInfo(int period, bool startFromFirst)
491            : base(
492                 new DataField[] { DataField.Y }, //Input fields
493                 new DataField[] { DataField.Y }, //Output fields
494                 period, startFromFirst)
495         {
496         }
497     }
498
499     /// <summary>
500     /// TriangularMovingAverage FormulaInfo
501     /// </summary>
502     internal class TriangularMovingAverageFormulaInfo : FormulaInfo
503     {
504         //Constructor
505         /// <summary>
506         /// Initializes a new instance of the <see cref="TriangularMovingAverageFormulaInfo"/> class.
507         /// </summary>
508         public TriangularMovingAverageFormulaInfo()
509             : this(2, false)                      //Defaults
510         {
511         }
512         /// <summary>
513         /// Initializes a new instance of the <see cref="TriangularMovingAverageFormulaInfo"/> class.
514         /// </summary>
515         /// <param name="period">The period.</param>
516         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
517         public TriangularMovingAverageFormulaInfo(int period, bool startFromFirst)
518             : base(
519                 new DataField[] { DataField.Y }, //Input fields
520                 new DataField[] { DataField.Y }, //Output fields
521                 period, startFromFirst)
522         {
523         }
524     }
525
526     /// <summary>
527     /// TripleExponentialMovingAverage FormulaInfo
528     /// </summary>
529     internal class TripleExponentialMovingAverageFormulaInfo : FormulaInfo
530     {
531         //Constructor
532         /// <summary>
533         /// Initializes a new instance of the <see cref="TripleExponentialMovingAverageFormulaInfo"/> class.
534         /// </summary>
535         public TripleExponentialMovingAverageFormulaInfo()
536             : this(12)                           //Defaults
537         {
538         }
539         /// <summary>
540         /// Initializes a new instance of the <see cref="TripleExponentialMovingAverageFormulaInfo"/> class.
541         /// </summary>
542         /// <param name="period">The period.</param>
543         public TripleExponentialMovingAverageFormulaInfo(int period)
544             : base(
545                 new DataField[] { DataField.Y }, //Input fields
546                 new DataField[] { DataField.Y }, //Output fields
547                 period)
548         {
549         }
550     }
551
552     /// <summary>
553     /// BollingerBands FormulaInfo
554     /// </summary>
555     internal class BollingerBandsFormulaInfo : FormulaInfo
556     {
557         //Constructor
558         /// <summary>
559         /// Initializes a new instance of the <see cref="BollingerBandsFormulaInfo"/> class.
560         /// </summary>
561         public BollingerBandsFormulaInfo()
562             : this(3, 2, true)                                          //Defaults
563         {
564         }
565         /// <summary>
566         /// Initializes a new instance of the <see cref="BollingerBandsFormulaInfo"/> class.
567         /// </summary>
568         /// <param name="period">The period.</param>
569         /// <param name="deviation">The deviation.</param>
570         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
571         public BollingerBandsFormulaInfo(int period, double deviation, bool startFromFirst)
572             : base(
573                 new DataField[] { DataField.Y },                        //Input fields
574                 new DataField[] { DataField.Top, DataField.Bottom },    //Output fields
575                 period, deviation, startFromFirst)
576         {
577         }
578     }
579
580     /// <summary>
581     /// TypicalPrice FormulaInfo
582     /// </summary>
583     internal class TypicalPriceFormulaInfo : FormulaInfo
584     {
585         //Constructor
586         /// <summary>
587         /// Initializes a new instance of the <see cref="TypicalPriceFormulaInfo"/> class.
588         /// </summary>
589         public TypicalPriceFormulaInfo()
590             : base(
591                 new DataField[] { DataField.Close, DataField.High, DataField.Low }, //Input fields
592                 new DataField[] { DataField.Y })                                    //Output fields
593         {
594         }
595     }
596
597     /// <summary>
598     /// WeightedClose FormulaInfo
599     /// </summary>
600     internal class WeightedCloseFormulaInfo : FormulaInfo
601     {
602         //Constructor
603         /// <summary>
604         /// Initializes a new instance of the <see cref="WeightedCloseFormulaInfo"/> class.
605         /// </summary>
606         public WeightedCloseFormulaInfo()
607             : base(
608                 new DataField[] { DataField.Close, DataField.High, DataField.Low }, //Input fields
609                 new DataField[] { DataField.Y })                                    //Output fields
610         {
611         }
612     }
613
614     /// <summary>
615     /// MedianPrice FormulaInfo
616     /// </summary>
617     internal class MedianPriceFormulaInfo : FormulaInfo
618     {
619         //Constructor
620         /// <summary>
621         /// Initializes a new instance of the <see cref="MedianPriceFormulaInfo"/> class.
622         /// </summary>
623         public MedianPriceFormulaInfo()
624             : base(
625                 new DataField[] { DataField.High, DataField.Low }, //Input fields
626                 new DataField[] { DataField.Y })                    //Output fields
627         {
628         }
629     }
630
631
632     /// <summary>
633     /// Envelopes FormulaInfo
634     /// </summary>
635     internal class EnvelopesFormulaInfo : FormulaInfo
636     {
637         //Constructor
638         /// <summary>
639         /// Initializes a new instance of the <see cref="EnvelopesFormulaInfo"/> class.
640         /// </summary>
641         public EnvelopesFormulaInfo()
642             : this(2, 10, true)                                          //Defaults
643         {
644         }
645         /// <summary>
646         /// Initializes a new instance of the <see cref="EnvelopesFormulaInfo"/> class.
647         /// </summary>
648         /// <param name="period">The period.</param>
649         /// <param name="shiftPercentage">The shift percentage.</param>
650         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
651         public EnvelopesFormulaInfo(int period, double shiftPercentage, bool startFromFirst)
652             : base(
653                 new DataField[] { DataField.Y },                        //Input fields
654                 new DataField[] { DataField.Top, DataField.Bottom },    //Output fields
655                 period, shiftPercentage, startFromFirst)
656         {
657         }
658     }
659
660
661     /// <summary>
662     /// StandardDeviation FormulaInfo
663     /// </summary>
664     internal class StandardDeviationFormulaInfo : FormulaInfo
665     {
666         //Constructor
667         /// <summary>
668         /// Initializes a new instance of the <see cref="StandardDeviationFormulaInfo"/> class.
669         /// </summary>
670         public StandardDeviationFormulaInfo()
671             : this(2, false)                      //Defaults
672         {
673         }
674         /// <summary>
675         /// Initializes a new instance of the <see cref="StandardDeviationFormulaInfo"/> class.
676         /// </summary>
677         /// <param name="period">The period.</param>
678         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
679         public StandardDeviationFormulaInfo(int period, bool startFromFirst)
680             : base(
681                 new DataField[] { DataField.Y }, //Input fields
682                 new DataField[] { DataField.Y }, //Output fields
683                 period, startFromFirst)
684         {
685         }
686     }
687
688     /// <summary>
689     /// ChaikinOscillatorFormulaInfo
690     /// </summary>
691     internal class ChaikinOscillatorFormulaInfo : FormulaInfo
692     {
693         //Constructor
694         /// <summary>
695         /// Initializes a new instance of the <see cref="ChaikinOscillatorFormulaInfo"/> class.
696         /// </summary>
697         public ChaikinOscillatorFormulaInfo()
698             : this(3, 10, false)                      //Defaults
699         {
700         }
701         /// <summary>
702         /// Initializes a new instance of the <see cref="ChaikinOscillatorFormulaInfo"/> class.
703         /// </summary>
704         /// <param name="shortPeriod">The short period.</param>
705         /// <param name="longPeriod">The long period.</param>
706         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
707         public ChaikinOscillatorFormulaInfo(int shortPeriod, int longPeriod, bool startFromFirst)
708             : base(
709                 new DataField[] { DataField.High, DataField.Low, DataField.Close, DataField.Y }, //Input fields
710                 new DataField[] { DataField.Y }, //Output fields
711                 shortPeriod, longPeriod, startFromFirst)
712         {
713         }
714     }
715
716     /// <summary>
717     /// DetrendedPriceOscillator FormulaInfo
718     /// </summary>
719     internal class DetrendedPriceOscillatorFormulaInfo : FormulaInfo
720     {
721         //Constructor
722         /// <summary>
723         /// Initializes a new instance of the <see cref="DetrendedPriceOscillatorFormulaInfo"/> class.
724         /// </summary>
725         public DetrendedPriceOscillatorFormulaInfo()
726             : this(2, false)                      //Defaults
727         {
728         }
729         /// <summary>
730         /// Initializes a new instance of the <see cref="DetrendedPriceOscillatorFormulaInfo"/> class.
731         /// </summary>
732         /// <param name="period">The period.</param>
733         /// <param name="startFromFirst">if set to <c>true</c> [start from first].</param>
734         public DetrendedPriceOscillatorFormulaInfo(int period, bool startFromFirst)
735             : base(
736                 new DataField[] { DataField.Y }, //Input fields
737                 new DataField[] { DataField.Y }, //Output fields
738                 period, startFromFirst)
739         {
740         }
741     }
742
743
744     /// <summary>
745     /// VolatilityChaikins FormulaInfo
746     /// </summary>
747     internal class VolatilityChaikinsFormulaInfo : FormulaInfo
748     {
749         //Constructor
750         /// <summary>
751         /// Initializes a new instance of the <see cref="VolatilityChaikinsFormulaInfo"/> class.
752         /// </summary>
753         public VolatilityChaikinsFormulaInfo()
754             : this(10, 10)                      //Defaults
755         {
756         }
757         /// <summary>
758         /// Initializes a new instance of the <see cref="VolatilityChaikinsFormulaInfo"/> class.
759         /// </summary>
760         /// <param name="period">The period.</param>
761         /// <param name="signalPeriod">The signal period.</param>
762         public VolatilityChaikinsFormulaInfo(int period, int signalPeriod)
763             : base(
764                 new DataField[] { DataField.High, DataField.Low }, //Input fields
765                 new DataField[] { DataField.Y }, //Output fields
766                 period, signalPeriod)
767         {
768         }
769     }
770
771     /// <summary>
772     /// VolumeOscillator FormulaInfo
773     /// </summary>
774     internal class VolumeOscillatorFormulaInfo : FormulaInfo
775     {
776         //Constructor
777         /// <summary>
778         /// Initializes a new instance of the <see cref="VolumeOscillatorFormulaInfo"/> class.
779         /// </summary>
780         public VolumeOscillatorFormulaInfo()
781             : this(5, 10, true)                      //Defaults
782         {
783         }
784         /// <summary>
785         /// Initializes a new instance of the <see cref="VolumeOscillatorFormulaInfo"/> class.
786         /// </summary>
787         /// <param name="shortPeriod">The short period.</param>
788         /// <param name="longPeriod">The long period.</param>
789         /// <param name="percentage">if set to <c>true</c> [percentage].</param>
790         public VolumeOscillatorFormulaInfo(int shortPeriod, int longPeriod, bool percentage)
791             : base(
792                 new DataField[] { DataField.Y }, //Input fields
793                 new DataField[] { DataField.Y }, //Output fields
794                 shortPeriod, longPeriod, percentage)
795         {
796         }
797     }
798
799     /// <summary>
800     /// StochasticIndicatorFormulaInfo
801     /// </summary>
802     internal class StochasticIndicatorFormulaInfo : FormulaInfo
803     {
804         //Constructor
805         /// <summary>
806         /// Initializes a new instance of the <see cref="StochasticIndicatorFormulaInfo"/> class.
807         /// </summary>
808         public StochasticIndicatorFormulaInfo()
809             : this(10, 10)                      //Defaults
810         {
811         }
812         /// <summary>
813         /// Initializes a new instance of the <see cref="StochasticIndicatorFormulaInfo"/> class.
814         /// </summary>
815         /// <param name="periodD">The period D.</param>
816         /// <param name="periodK">The period K.</param>
817         public StochasticIndicatorFormulaInfo(int periodD, int periodK)
818             : base(
819                 new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
820                 new DataField[] { DataField.Y, DataField.Y }, //Output fields
821                 periodD, periodK)
822         {
823         }
824     }
825
826     /// <summary>
827     /// WilliamsRFormulaInfo
828     /// </summary>
829     internal class WilliamsRFormulaInfo : FormulaInfo
830     {
831         //Constructor
832         /// <summary>
833         /// Initializes a new instance of the <see cref="WilliamsRFormulaInfo"/> class.
834         /// </summary>
835         public WilliamsRFormulaInfo()
836             : this(14)                      //Defaults
837         {
838         }
839         /// <summary>
840         /// Initializes a new instance of the <see cref="WilliamsRFormulaInfo"/> class.
841         /// </summary>
842         /// <param name="period">The period.</param>
843         public WilliamsRFormulaInfo(int period)
844             : base(
845                 new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
846                 new DataField[] { DataField.Y }, //Output fields
847                 period)
848         {
849         }
850     }
851
852     /// <summary>
853     /// AverageTrueRange FormulaInfo
854     /// </summary>
855     internal class AverageTrueRangeFormulaInfo : FormulaInfo
856     {
857         //Constructor
858         /// <summary>
859         /// Initializes a new instance of the <see cref="AverageTrueRangeFormulaInfo"/> class.
860         /// </summary>
861         public AverageTrueRangeFormulaInfo()
862             : this(14)                      //Defaults
863         {
864         }
865         /// <summary>
866         /// Initializes a new instance of the <see cref="AverageTrueRangeFormulaInfo"/> class.
867         /// </summary>
868         /// <param name="period">The period.</param>
869         public AverageTrueRangeFormulaInfo(int period)
870             : base(
871                 new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
872                 new DataField[] { DataField.Y }, //Output fields
873                 period)
874         {
875         }
876     }
877
878     /// <summary>
879     /// EaseOfMovement FormulaInfo
880     /// </summary>
881     internal class EaseOfMovementFormulaInfo : FormulaInfo
882     {
883         //Constructor
884         /// <summary>
885         /// Initializes a new instance of the <see cref="EaseOfMovementFormulaInfo"/> class.
886         /// </summary>
887         public EaseOfMovementFormulaInfo()
888             : base(
889                 new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
890                 new DataField[] { DataField.Y }) //Output fields
891         {
892         }
893     }
894
895     /// <summary>
896     /// MassIndex FormulaInfo
897     /// </summary>
898     internal class MassIndexFormulaInfo : FormulaInfo
899     {
900         //Constructor
901         /// <summary>
902         /// Initializes a new instance of the <see cref="MassIndexFormulaInfo"/> class.
903         /// </summary>
904         public MassIndexFormulaInfo()
905             : this(25, 9)                      //Defaults
906         {
907         }
908         /// <summary>
909         /// Initializes a new instance of the <see cref="MassIndexFormulaInfo"/> class.
910         /// </summary>
911         /// <param name="period">The period.</param>
912         /// <param name="averagePeriod">The average period.</param>
913         public MassIndexFormulaInfo(int period, int averagePeriod)
914             : base(
915                 new DataField[] { DataField.High, DataField.Low }, //Input fields
916                 new DataField[] { DataField.Y }, //Output fields
917                 period, averagePeriod)
918         {
919         }
920     }
921
922     /// <summary>
923     /// Performance FormulaInfo
924     /// </summary>
925     internal class PerformanceFormulaInfo : FormulaInfo
926     {
927         //Constructor
928         /// <summary>
929         /// Initializes a new instance of the <see cref="PerformanceFormulaInfo"/> class.
930         /// </summary>
931         public PerformanceFormulaInfo()
932             : base(
933                 new DataField[] { DataField.Close }, //Input fields
934                 new DataField[] { DataField.Y }) //Output fields
935         {
936         }
937     }
938
939     /// <summary>
940     /// RateOfChange FormulaInfo
941     /// </summary>
942     internal class RateOfChangeFormulaInfo : FormulaInfo
943     {
944         //Constructor
945         /// <summary>
946         /// Initializes a new instance of the <see cref="RateOfChangeFormulaInfo"/> class.
947         /// </summary>
948         public RateOfChangeFormulaInfo()
949             : this(10)                      //Defaults
950         {
951         }
952         /// <summary>
953         /// Initializes a new instance of the <see cref="RateOfChangeFormulaInfo"/> class.
954         /// </summary>
955         /// <param name="period">The period.</param>
956         public RateOfChangeFormulaInfo(int period)
957             : base(
958                 new DataField[] { DataField.Close }, //Input fields
959                 new DataField[] { DataField.Y }, //Output fields
960                 period)
961         {
962         }
963     }
964
965     /// <summary>
966     /// RelativeStrengthIndex FormulaInfo
967     /// </summary>
968     internal class RelativeStrengthIndexFormulaInfo : FormulaInfo
969     {
970         //Constructor
971         /// <summary>
972         /// Initializes a new instance of the <see cref="RelativeStrengthIndexFormulaInfo"/> class.
973         /// </summary>
974         public RelativeStrengthIndexFormulaInfo()
975             : this(10)                      //Defaults
976         {
977         }
978         /// <summary>
979         /// Initializes a new instance of the <see cref="RelativeStrengthIndexFormulaInfo"/> class.
980         /// </summary>
981         /// <param name="period">The period.</param>
982         public RelativeStrengthIndexFormulaInfo(int period)
983             : base(
984                 new DataField[] { DataField.Close }, //Input fields
985                 new DataField[] { DataField.Y }, //Output fields
986                 period)
987         {
988         }
989     }
990
991     /// <summary>
992     /// MovingAverageConvergenceDivergence FormulaInfo
993     /// </summary>
994     internal class MovingAverageConvergenceDivergenceFormulaInfo : FormulaInfo
995     {
996         //Constructor
997         /// <summary>
998         /// Initializes a new instance of the <see cref="MovingAverageConvergenceDivergenceFormulaInfo"/> class.
999         /// </summary>
1000         public MovingAverageConvergenceDivergenceFormulaInfo()
1001             : this(12, 26)                      //Defaults
1002         {
1003         }
1004         /// <summary>
1005         /// Initializes a new instance of the <see cref="MovingAverageConvergenceDivergenceFormulaInfo"/> class.
1006         /// </summary>
1007         /// <param name="shortPeriod">The short period.</param>
1008         /// <param name="longPeriod">The long period.</param>
1009         public MovingAverageConvergenceDivergenceFormulaInfo(int shortPeriod, int longPeriod)
1010             : base(
1011                 new DataField[] { DataField.Close }, //Input fields
1012                 new DataField[] { DataField.Y }, //Output fields
1013                 shortPeriod, longPeriod)
1014         {
1015         }
1016     }
1017
1018     /// <summary>
1019     /// CommodityChannelIndex FormulaInfo
1020     /// </summary>
1021     internal class CommodityChannelIndexFormulaInfo : FormulaInfo
1022     {
1023         //Constructor
1024         /// <summary>
1025         /// Initializes a new instance of the <see cref="CommodityChannelIndexFormulaInfo"/> class.
1026         /// </summary>
1027         public CommodityChannelIndexFormulaInfo()
1028             : this(10)                      //Defaults
1029         {
1030         }
1031         /// <summary>
1032         /// Initializes a new instance of the <see cref="CommodityChannelIndexFormulaInfo"/> class.
1033         /// </summary>
1034         /// <param name="period">The period.</param>
1035         public CommodityChannelIndexFormulaInfo(int period)
1036             : base(
1037                 new DataField[] { DataField.High, DataField.Low, DataField.Close }, //Input fields
1038                 new DataField[] { DataField.Y }, //Output fields
1039                 period)
1040         {
1041         }
1042     }
1043
1044     /// <summary>
1045     /// Forecasting FormulaInfo
1046     /// </summary>
1047     internal class ForecastingFormulaInfo : FormulaInfo
1048     {
1049         //Fields
1050         string _parameters;
1051
1052         //Constructor
1053         /// <summary>
1054         /// Initializes a new instance of the <see cref="ForecastingFormulaInfo"/> class.
1055         /// </summary>
1056         public ForecastingFormulaInfo()
1057             : this(TimeSeriesAndForecasting.RegressionType.Polynomial, 2, 0, true, true)                      //Defaults
1058         {
1059         }
1060         /// <summary>
1061         /// Initializes a new instance of the <see cref="ForecastingFormulaInfo"/> class.
1062         /// </summary>
1063         /// <param name="regressionType">Type of the regression.</param>
1064         /// <param name="polynomialDegree">The polynomial degree.</param>
1065         /// <param name="forecastingPeriod">The forecasting period.</param>
1066         /// <param name="returnApproximationError">if set to <c>true</c> [return approximation error].</param>
1067         /// <param name="returnForecastingError">if set to <c>true</c> [return forecasting error].</param>
1068         public ForecastingFormulaInfo(TimeSeriesAndForecasting.RegressionType regressionType, int polynomialDegree, int forecastingPeriod, bool returnApproximationError, bool returnForecastingError)
1069             : base(
1070                 new DataField[] { DataField.Close }, //Input fields
1071                 new DataField[] { DataField.Close, DataField.High, DataField.Low }, //Output fields
1072                 regressionType, polynomialDegree, forecastingPeriod, returnApproximationError, returnForecastingError)
1073         {
1074         }
1075
1076         //Methods
1077         /// <summary>
1078         /// Loads the formula parameters from string.
1079         /// </summary>
1080         /// <param name="parameters">Csv string with parameters.</param>
1081         internal override void LoadParametersFromString(string parameters)
1082         {
1083             _parameters = parameters;
1084         }
1085
1086         /// <summary>
1087         /// Checks the formula parameter string.
1088         /// </summary>
1089         /// <param name="parameters">The parameters.</param>
1090         internal override void CheckParameterString(string parameters)
1091         {
1092             if (String.IsNullOrEmpty(parameters))
1093                 return;
1094
1095             string[] paramStringList = parameters.Split(',');
1096             int paramStringIndex = 1;
1097             //Don't check the first param
1098             for (int i = 2; i < Parameters.Length && paramStringIndex < paramStringList.Length; i++)
1099             {
1100                 string newParamValue = paramStringList[paramStringIndex++];
1101                 if (!String.IsNullOrEmpty(newParamValue))
1102                 {
1103                     try
1104                     {
1105                         ParseParameter(i, newParamValue);
1106                     }
1107                     catch (FormatException)
1108                     {
1109                         throw new ArgumentException(SR.ExceptionFormulaDataFormatInvalid(parameters));
1110                     }
1111                 }
1112             }
1113         }
1114
1115         /// <summary>
1116         /// Saves the formula parameters to a string.
1117         /// </summary>
1118         /// <returns>Csv string with parameters</returns>
1119         internal override string SaveParametersToString()
1120         {
1121             if (String.IsNullOrEmpty(_parameters))
1122                 return _parameters;
1123             else
1124                 return "2,0,true,true";
1125         }
1126     }
1127
1128     /// <summary>
1129     /// MoneyFlow FormulaInfo
1130     /// </summary>
1131     internal class MoneyFlowFormulaInfo : FormulaInfo
1132     {
1133         //Constructor
1134         /// <summary>
1135         /// Initializes a new instance of the <see cref="MoneyFlowFormulaInfo"/> class.
1136         /// </summary>
1137         public MoneyFlowFormulaInfo()
1138             : this(2)                      //Defaults
1139         {
1140         }
1141         /// <summary>
1142         /// Initializes a new instance of the <see cref="MoneyFlowFormulaInfo"/> class.
1143         /// </summary>
1144         /// <param name="period">The period.</param>
1145         public MoneyFlowFormulaInfo(int period)
1146             : base(
1147                 new DataField[] { DataField.High, DataField.Low, DataField.Close, DataField.Y }, //Input fields: High,Low,Close,Volume
1148                 new DataField[] { DataField.Y }, //Output fields
1149                 period)
1150         {
1151         }
1152     }
1153
1154     /// <summary>
1155     /// PriceVolumeTrend FormulaInfo
1156     /// </summary>
1157     internal class PriceVolumeTrendFormulaInfo : FormulaInfo
1158     {
1159         //Constructor
1160         /// <summary>
1161         /// Initializes a new instance of the <see cref="PriceVolumeTrendFormulaInfo"/> class.
1162         /// </summary>
1163         public PriceVolumeTrendFormulaInfo()
1164             : base(
1165                 new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
1166                 new DataField[] { DataField.Y }) //Output fields
1167         {
1168         }
1169     }
1170
1171     /// <summary>
1172     /// OnBalanceVolume FormulaInfo
1173     /// </summary>
1174     internal class OnBalanceVolumeFormulaInfo : FormulaInfo
1175     {
1176         //Constructor
1177         /// <summary>
1178         /// Initializes a new instance of the <see cref="OnBalanceVolumeFormulaInfo"/> class.
1179         /// </summary>
1180         public OnBalanceVolumeFormulaInfo()
1181             : base(
1182                 new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
1183                 new DataField[] { DataField.Y }) //Output fields
1184         {
1185         }
1186     }
1187
1188     /// <summary>
1189     /// NegativeVolumeIndex FormulaInfo
1190     /// </summary>
1191     internal class NegativeVolumeIndexFormulaInfo : FormulaInfo
1192     {
1193         //Constructor
1194         /// <summary>
1195         /// Initializes a new instance of the <see cref="NegativeVolumeIndexFormulaInfo"/> class.
1196         /// </summary>
1197         public NegativeVolumeIndexFormulaInfo() //Note about parameters: Start value is mandatory so we don't provide the default
1198             : this(double.NaN)
1199         {
1200         }
1201         /// <summary>
1202         /// Initializes a new instance of the <see cref="NegativeVolumeIndexFormulaInfo"/> class.
1203         /// </summary>
1204         /// <param name="startValue">The start value.</param>
1205         public NegativeVolumeIndexFormulaInfo(double startValue)
1206             : base(
1207                 new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
1208                 new DataField[] { DataField.Y },
1209                 startValue) //Output fields
1210         {
1211         }
1212     }
1213
1214     /// <summary>
1215     /// PositiveVolumeIndex FormulaInfo
1216     /// </summary>
1217     internal class PositiveVolumeIndexFormulaInfo : FormulaInfo
1218     {
1219         //Constructor
1220         /// <summary>
1221         /// Initializes a new instance of the <see cref="PositiveVolumeIndexFormulaInfo"/> class.
1222         /// </summary>
1223         public PositiveVolumeIndexFormulaInfo() //Note about parameters: Start value is mandatory so we don't provide the default
1224             : this(double.NaN)
1225         {
1226         }
1227         /// <summary>
1228         /// Initializes a new instance of the <see cref="PositiveVolumeIndexFormulaInfo"/> class.
1229         /// </summary>
1230         /// <param name="startValue">The start value.</param>
1231         public PositiveVolumeIndexFormulaInfo(double startValue)
1232             : base(
1233                 new DataField[] { DataField.Close, DataField.Y }, //Input=Close,Volume
1234                 new DataField[] { DataField.Y },
1235                 startValue) //Output fields
1236         {
1237         }
1238     }
1239
1240     /// <summary>
1241     /// AccumulationDistribution FormulaInfo
1242     /// </summary>
1243     internal class AccumulationDistributionFormulaInfo : FormulaInfo
1244     {
1245         //Constructor
1246         /// <summary>
1247         /// Initializes a new instance of the <see cref="AccumulationDistributionFormulaInfo"/> class.
1248         /// </summary>
1249         public AccumulationDistributionFormulaInfo() //Note about parameters: Start value is mandatory so we don't provide the default
1250             : base(
1251                 new DataField[] { DataField.High, DataField.Low, DataField.Close, DataField.Y }, //Input=High, Low, Close, Volume
1252                 new DataField[] { DataField.Y }) //Output fields
1253         {
1254         }
1255     }
1256
1257     #endregion
1258
1259     #region enum DataField
1260     /// <summary>
1261     /// Chart data fields
1262     /// </summary>
1263     internal enum DataField
1264     {
1265         X,
1266         Y,
1267         LowerWisker,
1268         UpperWisker,
1269         LowerBox,
1270         UpperBox,
1271         Average,
1272         Median,
1273         Bubble,
1274         BubbleSize,
1275         High,
1276         Low,
1277         Open,
1278         Close,
1279         Center,
1280         LowerError,
1281         UpperError,
1282         Top,
1283         Bottom
1284     }
1285     #endregion
1286
1287     #region class SeriesFieldInfo
1288     /// <summary>
1289     /// SeriesFieldInfo class is a OO representation formula input/output data params ("Series1:Y2")
1290     /// </summary>
1291     internal class SeriesFieldInfo
1292     {
1293         #region Fields
1294         private Series _series;
1295         private string _seriesName;
1296         private DataField _dataField;
1297         #endregion
1298
1299         #region Properties
1300         /// <summary>
1301         /// Gets the series.
1302         /// </summary>
1303         /// <value>The series.</value>
1304         public Series Series 
1305         { 
1306             get { return _series; }
1307         }
1308         /// <summary>
1309         /// Gets the name of the series.
1310         /// </summary>
1311         /// <value>The name of the series.</value>
1312         public string SeriesName
1313         {
1314             get { return _series != null ? _series.Name : _seriesName; }
1315         }
1316         /// <summary>
1317         /// Gets the data field.
1318         /// </summary>
1319         /// <value>The data field.</value>
1320         public DataField DataField 
1321         { 
1322             get { return _dataField; }
1323         }
1324         #endregion
1325
1326         #region Constructors
1327         /// <summary>
1328         /// Initializes a new instance of the <see cref="SeriesFieldInfo"/> class.
1329         /// </summary>
1330         /// <param name="series">The series.</param>
1331         /// <param name="dataField">The data field.</param>
1332         public SeriesFieldInfo(Series series, DataField dataField)
1333         {
1334             _series = series;
1335             _dataField = dataField;
1336         }
1337         /// <summary>
1338         /// Initializes a new instance of the <see cref="SeriesFieldInfo"/> class.
1339         /// </summary>
1340         /// <param name="seriesName">Name of the series.</param>
1341         /// <param name="dataField">The data field.</param>
1342         public SeriesFieldInfo(string seriesName, DataField dataField)
1343         {
1344             _seriesName = seriesName;
1345             _dataField = dataField;
1346         }
1347         #endregion
1348     }
1349     #endregion
1350
1351     #region class SeriesFieldList
1352     /// <summary>
1353     /// SeriesFieldInfo class is a OO representation formula input/output data params ("Series1:Y2,Series2.Y4")
1354     /// </summary>
1355     internal class SeriesFieldList : List<SeriesFieldInfo>
1356     {
1357         /// <summary>
1358         /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
1359         /// </summary>
1360         /// <returns>
1361         /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
1362         /// </returns>
1363         public override string ToString()
1364         {
1365             StringBuilder sb = new StringBuilder();
1366             for (int i = 0; i < this.Count; i++)
1367             {
1368                 SeriesFieldInfo info = this[i];
1369
1370                 if (i > 0)
1371                     sb.Append(',');
1372
1373                 SeriesChartType seriesChartType = info.Series != null ?
1374                                                         info.Series.ChartType :
1375                                                         FormulaHelper.GetDefaultChartType(info.DataField);
1376
1377                 IList<DataField> dataFields = FormulaHelper.GetDataFields(seriesChartType);
1378
1379                 int dataFieldIndex = dataFields.IndexOf(info.DataField);
1380                 if (dataFieldIndex == 0)
1381                     sb.AppendFormat(CultureInfo.InvariantCulture, "{0}:Y", info.SeriesName); //The string field descriptor is 1 based ;-(
1382                 else
1383                     sb.AppendFormat(CultureInfo.InvariantCulture, "{0}:Y{1}", info.SeriesName, dataFieldIndex + 1); //The string field descriptor is 1 based ;-(
1384             }
1385             return sb.ToString();
1386         }
1387
1388         //Static
1389         /// <summary>
1390         /// Parse the string defining the formula's input/output series and fields.
1391         /// </summary>
1392         /// <param name="chart">The chart.</param>
1393         /// <param name="seriesFields">The series fields list. The series name can be followed by the field names. For example: "Series1:Y,Series1:Y3,Series2:Close"</param>
1394         /// <param name="formulaFields">The formula fields list.</param>
1395         /// <returns></returns>
1396         public static SeriesFieldList FromString(Chart chart, string seriesFields, IList<DataField> formulaFields)
1397         {
1398             SeriesFieldList result = new SeriesFieldList();
1399             if (String.IsNullOrEmpty(seriesFields))
1400             {
1401                 return result;
1402             }
1403
1404             List<DataField> unmappedFormulaFields = new List<DataField>(formulaFields);
1405
1406             //Loop through the series/field pairs
1407             foreach (string seriesField in seriesFields.Split(','))
1408             {
1409                 //Stop processing if all the formula fields are mapped
1410                 if (unmappedFormulaFields.Count == 0)
1411                     break;
1412
1413                 //Split a pair into a series + field
1414                 string[] seriesFieldParts = seriesField.Split(':');
1415                 if (seriesFieldParts.Length > 2)
1416                 {
1417                     throw new ArgumentException(SR.ExceptionFormulaDataFormatInvalid(seriesField));
1418                 }
1419
1420                 //Get the series and series fields
1421                 string seriesName = seriesFieldParts[0].Trim();
1422                 Series series = chart.Series.FindByName(seriesName);
1423                 if (series != null)
1424                 {
1425                     switch (seriesFieldParts.Length)
1426                     {
1427                         case 1: //Only series name is specified: "Series1"
1428                             AddSeriesFieldInfo(result, series, unmappedFormulaFields);
1429                             break;
1430                         case 2: //Series and field names are provided: "Series1:Y3"
1431                             AddSeriesFieldInfo(result, series, unmappedFormulaFields, seriesFieldParts[1]);
1432                             break;
1433                     }
1434                 }
1435                 else
1436                 {
1437                     switch (seriesFieldParts.Length)
1438                     {
1439                         case 1: //Only series name is specified: "Series1"
1440                             AddSeriesFieldInfo(result, seriesName, unmappedFormulaFields);
1441                             break;
1442                         case 2: //Series and field names are provided: "Series1:Y3"
1443                             AddSeriesFieldInfo(result, seriesName, unmappedFormulaFields, seriesFieldParts[1]);
1444                             break;
1445                     }
1446                 }
1447             }
1448             return result;
1449         }
1450
1451         /// <summary>
1452         /// Adds the series field info.
1453         /// </summary>
1454         /// <param name="result">The result.</param>
1455         /// <param name="series">The series.</param>
1456         /// <param name="unmappedFormulaFields">The unmapped formula fields.</param>
1457         private static void AddSeriesFieldInfo(SeriesFieldList result, Series series, IList<DataField> unmappedFormulaFields)
1458         {
1459             List<DataField> seriesFields = new List<DataField>(FormulaHelper.GetDataFields(series.ChartType));
1460
1461             for (int i = 0; i < unmappedFormulaFields.Count && seriesFields.Count > 0; )
1462             {
1463                 DataField formulaField = unmappedFormulaFields[i];
1464                 DataField? seriesField = null;
1465
1466                 // Case 1. Check if the formulaField is valid for this chart type
1467                 if (seriesFields.Contains(formulaField))
1468                 {
1469                     seriesField = formulaField;
1470                 }
1471                 // Case 2. Try to map the formula field to the series field
1472                 if (seriesField == null)
1473                 {
1474                     seriesField = FormulaHelper.MapFormulaDataField(series.ChartType, formulaField);
1475                 }
1476
1477                 // If the seriesField is found - add it to the results
1478                 if (seriesField != null)
1479                 {
1480                     result.Add(new SeriesFieldInfo(series, (DataField)seriesField));
1481                     seriesFields.Remove((DataField)formulaField);
1482                     unmappedFormulaFields.Remove(formulaField);
1483                 }
1484                 else
1485                 {
1486                     i++;
1487                 }
1488             }
1489         }
1490         /// <summary>
1491         /// Adds the series field info.
1492         /// </summary>
1493         /// <param name="result">The result.</param>
1494         /// <param name="series">The series.</param>
1495         /// <param name="unmappedFormulaFields">The unmapped formula fields.</param>
1496         /// <param name="seriesFieldId">The series field id.</param>
1497         private static void AddSeriesFieldInfo(SeriesFieldList result, Series series, IList<DataField> unmappedFormulaFields, string seriesFieldId)
1498         {
1499             IList<DataField> seriesFields = FormulaHelper.GetDataFields(series.ChartType);
1500
1501             DataField? seriesField = null;
1502
1503             seriesFieldId = seriesFieldId.ToUpperInvariant().Trim();
1504             if (seriesFieldId == "Y")
1505             {
1506                 seriesField = seriesFields[0];
1507             }
1508             else if (seriesFieldId.StartsWith("Y", StringComparison.Ordinal))
1509             {
1510                 int id = 0;
1511                 if (int.TryParse(seriesFieldId.Substring(1), out id))
1512                     if (id - 1 < seriesFields.Count)
1513                     {
1514                         seriesField = seriesFields[id - 1];
1515                     }
1516                     else
1517                     {
1518                         throw (new ArgumentException(SR.ExceptionFormulaYIndexInvalid, seriesFieldId));
1519                     }
1520             }
1521             else
1522             {
1523                 seriesField = (DataField)Enum.Parse(typeof(DataField), seriesFieldId, true);
1524             }
1525
1526             // Add the seriesField to the results
1527             if (seriesField != null)
1528             {
1529                 result.Add(new SeriesFieldInfo(series, (DataField)seriesField));
1530                 if (unmappedFormulaFields.Contains((DataField)seriesField))
1531                     unmappedFormulaFields.Remove((DataField)seriesField);
1532                 else
1533                     unmappedFormulaFields.RemoveAt(0);
1534             }
1535             else
1536             {
1537                 throw new ArgumentException(SR.ExceptionDataPointValueNameInvalid, seriesFieldId);
1538             }
1539
1540         }
1541
1542         /// <summary>
1543         /// Adds the series field info.
1544         /// </summary>
1545         /// <param name="result">The result.</param>
1546         /// <param name="seriesName">Name of the series.</param>
1547         /// <param name="unmappedFormulaFields">The unmapped formula fields.</param>
1548         private static void AddSeriesFieldInfo(SeriesFieldList result, string seriesName, IList<DataField> unmappedFormulaFields)
1549         {
1550             SeriesChartType chartType = FormulaHelper.GetDefaultChartType(unmappedFormulaFields[0]);
1551             List<DataField> seriesFields = new List<DataField>(FormulaHelper.GetDataFields(chartType));
1552
1553             for (int i = 0; i < unmappedFormulaFields.Count && seriesFields.Count > 0; )
1554             {
1555                 DataField formulaField = unmappedFormulaFields[i];
1556                 DataField? seriesField = null;
1557
1558                 // Check if the formulaField is valid for this chart type
1559                 if (seriesFields.Contains(formulaField))
1560                 {
1561                     seriesField = formulaField;
1562                 }
1563
1564                 // If the seriesField is found - add it to the results
1565                 if (seriesField != null)
1566                 {
1567                     result.Add(new SeriesFieldInfo(seriesName, (DataField)seriesField));
1568                     seriesFields.Remove((DataField)formulaField);
1569                     unmappedFormulaFields.Remove(formulaField);
1570                 }
1571                 else
1572                 {
1573                     i++;
1574                 }
1575             }
1576         }
1577         /// <summary>
1578         /// Adds the series field info.
1579         /// </summary>
1580         /// <param name="result">The result.</param>
1581         /// <param name="seriesName">Name of the series.</param>
1582         /// <param name="unmappedFormulaFields">The unmapped formula fields.</param>
1583         /// <param name="seriesFieldId">The series field id.</param>
1584         private static void AddSeriesFieldInfo(SeriesFieldList result, string seriesName, IList<DataField> unmappedFormulaFields, string seriesFieldId)
1585         {
1586             SeriesChartType chartType = FormulaHelper.GetDefaultChartType(unmappedFormulaFields[0]);
1587             IList<DataField> seriesFields = FormulaHelper.GetDataFields(chartType);
1588
1589             //Find the field
1590             DataField? seriesField = null;
1591
1592             seriesFieldId = seriesFieldId.ToUpperInvariant().Trim();
1593
1594             if (seriesFieldId == "Y")
1595             {
1596                 seriesField = seriesFields[0];
1597             }
1598             else if (seriesFieldId.StartsWith("Y", StringComparison.Ordinal))
1599             {
1600                 int seriesFieldIndex = 0;
1601                 if (int.TryParse(seriesFieldId.Substring(1), out seriesFieldIndex))
1602                     if (seriesFieldIndex < seriesFields.Count)
1603                     {
1604                         seriesField = seriesFields[seriesFieldIndex - 1];
1605                     }
1606                     else
1607                     {
1608                         throw (new ArgumentException(SR.ExceptionFormulaYIndexInvalid, seriesFieldId));
1609                     }
1610             }
1611             else
1612             {
1613                 //Try parse the field name
1614                 try
1615                 {
1616                     seriesField = (DataField)Enum.Parse(typeof(DataField), seriesFieldId, true);
1617                 }
1618                 catch (ArgumentException)
1619                 { }
1620             }
1621
1622             if (seriesField != null)
1623             {
1624                 result.Add(new SeriesFieldInfo(seriesName, (DataField)seriesField));
1625                 unmappedFormulaFields.Remove((DataField)seriesField);
1626             }
1627             else
1628             {
1629                 throw new ArgumentException(SR.ExceptionDataPointValueNameInvalid, seriesFieldId);
1630             }
1631         }
1632     }
1633     #endregion
1634
1635 }
1636