/// <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 { } }
/// <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); }