/// <summary>
        ///     Sets the chart parameters
        /// </summary>
        private void InitChart(int width, int height)
        {
            try
            {
                Chart = new Bitmap(width, height);

                if (!Data.IsData || !Data.IsResult || Data.Bars <= Data.FirstBar)
                {
                    return;
                }

                const int border = 1;
                const int space  = 2;

                int maximum;
                int minimum;

                int firstBar   = Data.FirstBar;
                int bars       = Data.Bars;
                int chartBars  = Data.Bars - firstBar;
                int maxBalance = Configs.AccountInMoney ? (int)Backtester.MaxMoneyBalance : Backtester.MaxBalance;
                int minBalance = Configs.AccountInMoney ? (int)Backtester.MinMoneyBalance : Backtester.MinBalance;
                int maxEquity  = Configs.AccountInMoney ? (int)Backtester.MaxMoneyEquity : Backtester.MaxEquity;
                int minEquity  = Configs.AccountInMoney ? (int)Backtester.MinMoneyEquity : Backtester.MinEquity;

                if (Configs.AdditionalStatistics)
                {
                    int maxLongBalance = Configs.AccountInMoney
                                             ? (int)Backtester.MaxLongMoneyBalance
                                             : Backtester.MaxLongBalance;
                    int minLongBalance = Configs.AccountInMoney
                                             ? (int)Backtester.MinLongMoneyBalance
                                             : Backtester.MinLongBalance;
                    int maxShortBalance = Configs.AccountInMoney
                                              ? (int)Backtester.MaxShortMoneyBalance
                                              : Backtester.MaxShortBalance;
                    int minShortBalance = Configs.AccountInMoney
                                              ? (int)Backtester.MinShortMoneyBalance
                                              : Backtester.MinShortBalance;
                    int maxLsBalance = Math.Max(maxLongBalance, maxShortBalance);
                    int minLsBalance = Math.Min(minLongBalance, minShortBalance);

                    maximum = Math.Max(Math.Max(maxBalance, maxEquity), maxLsBalance) + 1;
                    minimum = Math.Min(Math.Min(minBalance, minEquity), minLsBalance) - 1;
                }
                else
                {
                    maximum = Math.Max(maxBalance, maxEquity) + 1;
                    minimum = Math.Min(minBalance, minEquity) - 1;
                }

                const int yTop    = border + space;
                int       yBottom = height - border - space;
                const int xLeft   = border;
                int       xRight  = width - border - space;
                float     xScale  = (xRight - xLeft) / (float)chartBars;
                float     yScale  = (yBottom - yTop) / (float)(maximum - minimum);

                var penBorder = new Pen(Data.GetGradientColor(LayoutColors.ColorCaptionBack, -LayoutColors.DepthCaption),
                                        border);

                var balancePoints      = new PointF[chartBars];
                var equityPoints       = new PointF[chartBars];
                var longBalancePoints  = new PointF[chartBars];
                var shortBalancePoints = new PointF[chartBars];

                int index = 0;
                for (int bar = firstBar; bar < bars; bar++)
                {
                    balancePoints[index].X = xLeft + index * xScale;
                    equityPoints[index].X  = xLeft + index * xScale;
                    if (Configs.AccountInMoney)
                    {
                        balancePoints[index].Y = (float)(yBottom - (Backtester.MoneyBalance(bar) - minimum) * yScale);
                        equityPoints[index].Y  = (float)(yBottom - (Backtester.MoneyEquity(bar) - minimum) * yScale);
                    }
                    else
                    {
                        balancePoints[index].Y = yBottom - (Backtester.Balance(bar) - minimum) * yScale;
                        equityPoints[index].Y  = yBottom - (Backtester.Equity(bar) - minimum) * yScale;
                    }

                    if (Configs.AdditionalStatistics)
                    {
                        longBalancePoints[index].X  = xLeft + index * xScale;
                        shortBalancePoints[index].X = xLeft + index * xScale;
                        if (Configs.AccountInMoney)
                        {
                            longBalancePoints[index].Y =
                                (float)(yBottom - (Backtester.LongMoneyBalance(bar) - minimum) * yScale);
                            shortBalancePoints[index].Y =
                                (float)(yBottom - (Backtester.ShortMoneyBalance(bar) - minimum) * yScale);
                        }
                        else
                        {
                            longBalancePoints[index].Y  = yBottom - (Backtester.LongBalance(bar) - minimum) * yScale;
                            shortBalancePoints[index].Y = yBottom - (Backtester.ShortBalance(bar) - minimum) * yScale;
                        }
                    }

                    index++;
                }

                Graphics g = Graphics.FromImage(Chart);

                // Paints the background by gradient
                var rectField = new RectangleF(1, 1, width - 2, height - 2);
                g.FillRectangle(new SolidBrush(LayoutColors.ColorChartBack), rectField);

                // Border
                g.DrawRectangle(penBorder, 0, 0, width - 1, height - 1);

                // Equity line
                g.DrawLines(new Pen(LayoutColors.ColorChartEquityLine), equityPoints);

                // Draw Long and Short balance
                if (Configs.AdditionalStatistics)
                {
                    g.DrawLines(new Pen(Color.Red), shortBalancePoints);
                    g.DrawLines(new Pen(Color.Green), longBalancePoints);
                }

                // Draw the balance line
                g.DrawLines(new Pen(LayoutColors.ColorChartBalanceLine), balancePoints);
            } catch { }
        }
Exemple #2
0
        /// <summary>
        ///     Sets chart's back testing data.
        /// </summary>
        public void SetChartData()
        {
            isNotPaint = !Data.IsData || !Data.IsResult || Data.Bars <= Data.FirstBar;

            if (isNotPaint)
            {
                return;
            }

            showPriceLine   = Configs.ShowPriceChartOnAccountChart && Backtester.ExecutedOrders > 0;
            isScanPerformed = Backtester.IsScanPerformed;

            data.FirstBar = Data.FirstBar;
            data.Bars     = Data.Bars;
            chartBars     = Data.Bars - Data.FirstBar;

            int maxBalance = Configs.AccountInMoney ? (int)Backtester.MaxMoneyBalance : Backtester.MaxBalance;
            int minBalance = Configs.AccountInMoney ? (int)Backtester.MinMoneyBalance : Backtester.MinBalance;
            int maxEquity  = Configs.AccountInMoney ? (int)Backtester.MaxMoneyEquity : Backtester.MaxEquity;
            int minEquity  = Configs.AccountInMoney ? (int)Backtester.MinMoneyEquity : Backtester.MinEquity;

            if (Configs.AdditionalStatistics)
            {
                int maxLongBalance = Configs.AccountInMoney
                                         ? (int)Backtester.MaxLongMoneyBalance
                                         : Backtester.MaxLongBalance;
                int minLongBalance = Configs.AccountInMoney
                                         ? (int)Backtester.MinLongMoneyBalance
                                         : Backtester.MinLongBalance;
                int maxShortBalance = Configs.AccountInMoney
                                          ? (int)Backtester.MaxShortMoneyBalance
                                          : Backtester.MaxShortBalance;
                int minShortBalance = Configs.AccountInMoney
                                          ? (int)Backtester.MinShortMoneyBalance
                                          : Backtester.MinShortBalance;
                int maxLongShortBalance = Math.Max(maxLongBalance, maxShortBalance);
                int minLongShortBalance = Math.Min(minLongBalance, minShortBalance);

                data.Maximum = Math.Max(Math.Max(maxBalance, maxEquity), maxLongShortBalance) + 1;
                data.Minimum = Math.Min(Math.Min(minBalance, minEquity), minLongShortBalance) - 1;
            }
            else
            {
                data.Maximum = Math.Max(maxBalance, maxEquity) + 1;
                data.Minimum = Math.Min(minBalance, minEquity) - 1;
            }

            data.Minimum = (int)(Math.Floor(data.Minimum / 10f) * 10);

            data.DataMaxPrice = Data.MaxPrice;
            data.DataMinPrice = Data.MinPrice;

            if (showPriceLine)
            {
                data.ClosePrice = new double[data.Bars];
                Data.Close.CopyTo(data.ClosePrice, 0);
            }

            if (Configs.AccountInMoney)
            {
                data.MoneyBalance = new double[data.Bars];
                data.MoneyEquity  = new double[data.Bars];
            }
            else
            {
                data.Balance = new int[data.Bars];
                data.Equity  = new int[data.Bars];
            }

            if (Configs.AdditionalStatistics)
            {
                if (Configs.AccountInMoney)
                {
                    data.LongMoneyBalance  = new double[data.Bars];
                    data.ShortMoneyBalance = new double[data.Bars];
                }
                else
                {
                    data.LongBalance  = new int[data.Bars];
                    data.ShortBalance = new int[data.Bars];
                }
            }


            for (int bar = data.FirstBar; bar < data.Bars; bar++)
            {
                if (Configs.AccountInMoney)
                {
                    data.MoneyBalance[bar] = Backtester.MoneyBalance(bar);
                    data.MoneyEquity[bar]  = Backtester.MoneyEquity(bar);
                }
                else
                {
                    data.Balance[bar] = Backtester.Balance(bar);
                    data.Equity[bar]  = Backtester.Equity(bar);
                }

                if (Configs.AdditionalStatistics)
                {
                    if (Configs.AccountInMoney)
                    {
                        data.LongMoneyBalance[bar]  = Backtester.LongMoneyBalance(bar);
                        data.ShortMoneyBalance[bar] = Backtester.ShortMoneyBalance(bar);
                    }
                    else
                    {
                        data.LongBalance[bar]  = Backtester.LongBalance(bar);
                        data.ShortBalance[bar] = Backtester.ShortBalance(bar);
                    }
                }
            }

            data.MarginCallBar = Backtester.MarginCallBar;

            if (IsOOS && OOSBar > data.FirstBar)
            {
                data.NetBalance =
                    (float)(Configs.AccountInMoney ? Backtester.MoneyBalance(OOSBar) : Backtester.Balance(OOSBar));
                data.DataTimeBarOOS = Data.Time[OOSBar];
            }
            else
            {
                data.NetBalance = (float)(Configs.AccountInMoney ? Backtester.NetMoneyBalance : Backtester.NetBalance);
            }
        }
        /// <summary>
        ///     Calculates the balance lines
        /// </summary>
        private int Calculate(BackgroundWorker worker)
        {
            // Determine the number of lines
            // For each method per line
            // The random line shows the averaged values
            // Also we have two border lines for the random method
            // Plus the average balance line

            isRandom      = false;
            minimum       = float.MaxValue;
            maximum       = float.MinValue;
            minimumRandom = float.MaxValue;
            maximumRandom = float.MinValue;
            var randomLines = (int)NumRandom.Value;

            checkedMethods = 0;
            lines          = 1;
            for (int m = 0; m < countMethods; m++)
            {
                if (AchboxMethods[m].Checked)
                {
                    checkedMethods++;
                    lines++;
                    if ((InterpolationMethod)AchboxMethods[m].Tag == InterpolationMethod.Random)
                    {
                        isRandom = true;
                    }
                }
            }

            if (checkedMethods == 0 && Configs.PlaySounds)
            {
                SystemSounds.Hand.Play();
                return(-1);
            }

            afBalance = new float[Data.Bars - Data.FirstBar];
            afMethods = new float[countMethods, Data.Bars - Data.FirstBar];
            if (isRandom)
            {
                afRandoms   = new float[randomLines, Data.Bars - Data.FirstBar];
                afMinRandom = new float[Data.Bars - Data.FirstBar];
                afMaxRandom = new float[Data.Bars - Data.FirstBar];
            }

            // Progress parameters
            int computedCycles           = 0;
            int cycles                   = lines + (isRandom ? randomLines : 0);
            int highestPercentageReached = 0;
            int percentComplete;

            // Calculates the lines
            for (int m = 0; m < countMethods; m++)
            {
                if (worker.CancellationPending)
                {
                    return(-1);
                }
                if (!AchboxMethods[m].Checked)
                {
                    continue;
                }

                var method = (InterpolationMethod)AchboxMethods[m].Tag;

                if (method == InterpolationMethod.Random)
                {
                    for (int r = 0; r < randomLines; r++)
                    {
                        if (worker.CancellationPending)
                        {
                            return(-1);
                        }

                        Backtester.InterpolationMethod = method;
                        Backtester.Calculate();

                        if (Configs.AccountInMoney)
                        {
                            for (int iBar = 0; iBar < Data.Bars - Data.FirstBar; iBar++)
                            {
                                afRandoms[r, iBar] = (float)Backtester.MoneyBalance(iBar + Data.FirstBar);
                            }
                        }
                        else
                        {
                            for (int iBar = 0; iBar < Data.Bars - Data.FirstBar; iBar++)
                            {
                                afRandoms[r, iBar] = Backtester.Balance(iBar + Data.FirstBar);
                            }
                        }


                        // Report progress as a percentage of the total task.
                        computedCycles++;
                        percentComplete = 100 * computedCycles / cycles;
                        percentComplete = percentComplete > 100 ? 100 : percentComplete;
                        if (percentComplete > highestPercentageReached)
                        {
                            highestPercentageReached = percentComplete;
                            worker.ReportProgress(percentComplete);
                        }
                    }

                    for (int iBar = 0; iBar < Data.Bars - Data.FirstBar; iBar++)
                    {
                        float randomSum = 0;
                        float minRandom = float.MaxValue;
                        float maxRandom = float.MinValue;
                        for (int r = 0; r < randomLines; r++)
                        {
                            float value = afRandoms[r, iBar];
                            randomSum += value;
                            minRandom  = value < minRandom ? value : minRandom;
                            maxRandom  = value > maxRandom ? value : maxRandom;
                        }
                        afMethods[m, iBar] = randomSum / randomLines;
                        afMinRandom[iBar]  = minRandom;
                        afMaxRandom[iBar]  = maxRandom;
                        minimumRandom      = minRandom < minimumRandom ? minRandom : minimumRandom;
                        maximumRandom      = maxRandom > maximumRandom ? maxRandom : maximumRandom;
                    }

                    // Report progress as a percentage of the total task.
                    computedCycles++;
                    percentComplete = 100 * computedCycles / cycles;
                    percentComplete = percentComplete > 100 ? 100 : percentComplete;
                    if (percentComplete > highestPercentageReached)
                    {
                        highestPercentageReached = percentComplete;
                        worker.ReportProgress(percentComplete);
                    }
                }
                else
                {
                    Backtester.InterpolationMethod = method;
                    Backtester.Calculate();

                    if (Configs.AccountInMoney)
                    {
                        for (int iBar = 0; iBar < Data.Bars - Data.FirstBar; iBar++)
                        {
                            afMethods[m, iBar] = (float)Backtester.MoneyBalance(iBar + Data.FirstBar);
                        }
                    }
                    else
                    {
                        for (int iBar = 0; iBar < Data.Bars - Data.FirstBar; iBar++)
                        {
                            afMethods[m, iBar] = Backtester.Balance(iBar + Data.FirstBar);
                        }
                    }

                    // Report progress as a percentage of the total task.
                    computedCycles++;
                    percentComplete = 100 * computedCycles / cycles;
                    percentComplete = percentComplete > 100 ? 100 : percentComplete;
                    if (percentComplete > highestPercentageReached)
                    {
                        highestPercentageReached = percentComplete;
                        worker.ReportProgress(percentComplete);
                    }
                }
            }

            // Calculates the average balance, Min and Max
            for (int bar = 0; bar < Data.Bars - Data.FirstBar; bar++)
            {
                float sum = 0;
                for (int m = 0; m < countMethods; m++)
                {
                    if (!AchboxMethods[m].Checked)
                    {
                        continue;
                    }

                    float value = afMethods[m, bar];
                    sum += value;
                    if (value < minimum)
                    {
                        minimum = value;
                    }
                    if (value > maximum)
                    {
                        maximum = value;
                    }
                }
                afBalance[bar] = sum / checkedMethods;
            }

            // Report progress as a percentage of the total task.
            computedCycles++;
            percentComplete = 100 * computedCycles / cycles;
            percentComplete = percentComplete > 100 ? 100 : percentComplete;
            if (percentComplete > highestPercentageReached)
            {
                worker.ReportProgress(percentComplete);
            }

            return(0);
        }
        /// <summary>
        ///     Represents the Strategy as a XmlDocument.
        /// </summary>
        public static XmlDocument CreateStrategyXmlDoc(Strategy strategy)
        {
            // Create the XmlDocument.
            var xmlDocStrategy = new XmlDocument();

            xmlDocStrategy.LoadXml("<strategy></strategy>");

            //Create the XML declaration.
            XmlDeclaration xmldecl = xmlDocStrategy.CreateXmlDeclaration("1.0", null, null);

            //Add new node to the document.
            XmlElement root = xmlDocStrategy.DocumentElement;

            xmlDocStrategy.InsertBefore(xmldecl, root);

            AppendStringElement(xmlDocStrategy, "programName", Data.ProgramName);
            AppendStringElement(xmlDocStrategy, "programVersion", Data.ProgramVersion);
            AppendStringElement(xmlDocStrategy, "strategyName", strategy.StrategyName);
            AppendStringElement(xmlDocStrategy, "instrumentSymbol", strategy.Symbol);
            AppendStringElement(xmlDocStrategy, "instrumentPeriod", (int)strategy.DataPeriod);
            AppendStringElement(xmlDocStrategy, "sameDirSignalAction", strategy.SameSignalAction.ToString());
            AppendStringElement(xmlDocStrategy, "oppDirSignalAction", strategy.OppSignalAction.ToString());

            // Add the Permanent Stop Loss
            XmlElement newElem = xmlDocStrategy.CreateElement("permanentStopLoss");

            newElem.InnerText = strategy.PermanentSL.ToString(CultureInfo.InvariantCulture);
            newElem.SetAttribute("usePermanentSL", strategy.UsePermanentSL.ToString(CultureInfo.InvariantCulture));
            newElem.SetAttribute("permanentSLType", strategy.PermanentSLType.ToString());
            if (xmlDocStrategy.DocumentElement != null)
            {
                xmlDocStrategy.DocumentElement.AppendChild(newElem);
            }

            // Add the Permanent Take Profit
            newElem           = xmlDocStrategy.CreateElement("permanentTakeProfit");
            newElem.InnerText = strategy.PermanentTP.ToString(CultureInfo.InvariantCulture);
            newElem.SetAttribute("usePermanentTP", strategy.UsePermanentTP.ToString(CultureInfo.InvariantCulture));
            newElem.SetAttribute("permanentTPType", strategy.PermanentTPType.ToString());
            if (xmlDocStrategy.DocumentElement != null)
            {
                xmlDocStrategy.DocumentElement.AppendChild(newElem);
            }

            // Add the Break Even
            newElem           = xmlDocStrategy.CreateElement("breakEven");
            newElem.InnerText = strategy.BreakEven.ToString(CultureInfo.InvariantCulture);
            newElem.SetAttribute("useBreakEven", strategy.UseBreakEven.ToString(CultureInfo.InvariantCulture));
            if (xmlDocStrategy.DocumentElement != null)
            {
                xmlDocStrategy.DocumentElement.AppendChild(newElem);
            }

            AppendStringElement(xmlDocStrategy, "maxOpenLots", strategy.MaxOpenLots);
            AppendStringElement(xmlDocStrategy, "useAccountPercentEntry", strategy.UseAccountPercentEntry);
            AppendStringElement(xmlDocStrategy, "entryLots", strategy.EntryLots);
            AppendStringElement(xmlDocStrategy, "addingLots", strategy.AddingLots);
            AppendStringElement(xmlDocStrategy, "reducingLots", strategy.ReducingLots);
            AppendStringElement(xmlDocStrategy, "useMartingale", strategy.UseMartingale);
            AppendStringElement(xmlDocStrategy, "martingaleMultiplier", strategy.MartingaleMultiplier);
            AppendStringElement(xmlDocStrategy, "description", strategy.Description);

            // Add the slots.
            AppendStringElement(xmlDocStrategy, "openFilters", strategy.OpenFilters);
            AppendStringElement(xmlDocStrategy, "closeFilters", strategy.CloseFilters);
            for (int slot = 0; slot < strategy.Slots; slot++)
            {
                IndicatorSlot stratSlot = strategy.Slot[slot];
                SlotTypes     slType    = stratSlot.SlotType;

                // Add a slot element.
                XmlElement newSlot = xmlDocStrategy.CreateElement("slot");
                newSlot.SetAttribute("slotNumber", slot.ToString(CultureInfo.InvariantCulture));
                newSlot.SetAttribute("slotType", slType.ToString());
                newSlot.SetAttribute("slotStatus", stratSlot.SlotStatus.ToString());

                if (slType == SlotTypes.OpenFilter || slType == SlotTypes.CloseFilter)
                {
                    newSlot.SetAttribute("logicalGroup", stratSlot.LogicalGroup);
                }

                // Add an element.
                newElem           = xmlDocStrategy.CreateElement("indicatorName");
                newElem.InnerText = stratSlot.IndicatorName;
                newSlot.AppendChild(newElem);

                // Add the list parameters.
                for (int param = 0; param < stratSlot.IndParam.ListParam.Length; param++)
                {
                    if (!stratSlot.IndParam.ListParam[param].Enabled)
                    {
                        continue;
                    }

                    // Add an element.
                    XmlElement newListElem = xmlDocStrategy.CreateElement("listParam");
                    newListElem.SetAttribute("paramNumber", param.ToString(CultureInfo.InvariantCulture));

                    // Add an element.
                    newElem           = xmlDocStrategy.CreateElement("caption");
                    newElem.InnerText = stratSlot.IndParam.ListParam[param].Caption;
                    newListElem.AppendChild(newElem);

                    // Add an element.
                    newElem           = xmlDocStrategy.CreateElement("index");
                    newElem.InnerText = stratSlot.IndParam.ListParam[param].Index.ToString(CultureInfo.InvariantCulture);
                    newListElem.AppendChild(newElem);

                    // Add an element.
                    newElem           = xmlDocStrategy.CreateElement("value");
                    newElem.InnerText = stratSlot.IndParam.ListParam[param].Text;
                    newListElem.AppendChild(newElem);

                    newSlot.AppendChild(newListElem);
                }

                // Add the num parameters.
                for (int param = 0; param < stratSlot.IndParam.NumParam.Length; param++)
                {
                    if (!stratSlot.IndParam.NumParam[param].Enabled)
                    {
                        continue;
                    }

                    // Add an element.
                    XmlElement newNumElem = xmlDocStrategy.CreateElement("numParam");
                    newNumElem.SetAttribute("paramNumber", param.ToString(CultureInfo.InvariantCulture));

                    // Add an element.
                    newElem           = xmlDocStrategy.CreateElement("caption");
                    newElem.InnerText = stratSlot.IndParam.NumParam[param].Caption;
                    newNumElem.AppendChild(newElem);

                    // Add an element.
                    newElem           = xmlDocStrategy.CreateElement("value");
                    newElem.InnerText = stratSlot.IndParam.NumParam[param].ValueToString;
                    newNumElem.AppendChild(newElem);

                    newSlot.AppendChild(newNumElem);
                }

                // Add the check parameters.
                for (int param = 0; param < stratSlot.IndParam.CheckParam.Length; param++)
                {
                    if (!stratSlot.IndParam.CheckParam[param].Enabled)
                    {
                        continue;
                    }

                    // Add an element.
                    XmlElement newCheckElem = xmlDocStrategy.CreateElement("checkParam");
                    newCheckElem.SetAttribute("paramNumber", param.ToString(CultureInfo.InvariantCulture));

                    // Add an element.
                    newElem           = xmlDocStrategy.CreateElement("caption");
                    newElem.InnerText = stratSlot.IndParam.CheckParam[param].Caption;
                    newCheckElem.AppendChild(newElem);

                    // Add an element.
                    newElem           = xmlDocStrategy.CreateElement("value");
                    newElem.InnerText =
                        stratSlot.IndParam.CheckParam[param].Checked.ToString(CultureInfo.InvariantCulture);
                    newCheckElem.AppendChild(newElem);

                    newSlot.AppendChild(newCheckElem);
                }

                if (xmlDocStrategy.DocumentElement != null)
                {
                    xmlDocStrategy.DocumentElement.AppendChild(newSlot);
                }
            }

            // Add statistics meta data.
            string unit = " " + Configs.AccountCurrency;

            AppendStringElement(xmlDocStrategy, "AccountBalance", Backtester.NetMoneyBalance.ToString("F2") + unit);
            AppendStringElement(xmlDocStrategy, "ProfitPerDay", Backtester.MoneyProfitPerDay.ToString("F2") + unit);
            AppendStringElement(xmlDocStrategy, "WinLossRatio", Backtester.WinLossRatio.ToString("F2"));
            AppendStringElement(xmlDocStrategy, "AccountStatsParam", String.Join(";", Backtester.AccountStatsParam));
            AppendStringElement(xmlDocStrategy, "AccountStatsValue", String.Join(";", Backtester.AccountStatsValue));
            AppendStringElement(xmlDocStrategy, "MarketStatsParam", String.Join(";", Data.MarketStatsParam));
            AppendStringElement(xmlDocStrategy, "MarketStatsValue", String.Join(";", Data.MarketStatsValue));

            // Add chart data
            int length      = Data.Bars - StatsBuffer.FirstBar;
            var balanceLine = new double[length];
            var equityLine  = new double[length];

            for (int bar = 0; bar < length; bar++)
            {
                balanceLine[bar] = Backtester.MoneyBalance(bar);
                equityLine[bar]  = Backtester.MoneyEquity(bar);
            }
            int size = Math.Min(600, length);

            string[] balanceList = MathUtils.ArrayToStringArray(MathUtils.ArrayResize(balanceLine, size));
            string[] equityList  = MathUtils.ArrayToStringArray(MathUtils.ArrayResize(equityLine, size));
            AppendStringElement(xmlDocStrategy, "BalanceLine", String.Join(";", balanceList));
            AppendStringElement(xmlDocStrategy, "EquityLine", String.Join(";", equityList));

            return(xmlDocStrategy);
        }