/// <summary> /// Constructor /// </summary> public OverOptimizationChartsForm(OverOptimizationDataTable[] tableReport, List<string> paramNames) { TableReport = tableReport; Text = Language.T("Over-optimization Report"); Icon = Data.Icon; FormBorderStyle = FormBorderStyle.Sizable; AcceptButton = BtnClose; // Button Next Chart BtnNextCharts = new Button {Parent = this, Text = Language.T("Next Chart")}; BtnNextCharts.Click += BtnNextChartsClick; BtnNextCharts.DoubleClick += BtnNextChartsClick; BtnNextCharts.MouseWheel += ChartMouseWheel; BtnNextCharts.KeyUp += NextChartsKeyUp; BtnNextCharts.UseVisualStyleBackColor = true; // Button Close BtnClose = new Button { Parent = this, Text = Language.T("Close"), DialogResult = DialogResult.Cancel, UseVisualStyleBackColor = true }; CurrentChart = new OverOptimizationCharts {Parent = this}; CurrentChart.InitChart(tableReport[currentChartNumber]); ChartLegend = new OverOptimizationChartLegend {Parent = this}; ChartLegend.InitChart(paramNames); }
/// <summary> /// Sets table with parameter values. /// </summary> private void SetParametersValues(int percentDeviation, int countParam) { paramNames = new List<string>(); listParametersName = "Index" + Configs.ColumnSeparator + "Parameter name" + Environment.NewLine; countStratParams = 0; cycles = 0; deviationSteps = 2*percentDeviation + 1; tableParameters = new OverOptimizationDataTable(percentDeviation, countParam) {Name = "Values of the Parameters"}; for (int slot = 0; slot < Data.Strategy.Slots; slot++) for (int numParam = 0; numParam < 6; numParam++) if (Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Enabled && countStratParams < countParam) { NumericParam currentParam = Data.Strategy.Slot[slot].IndParam.NumParam[numParam]; double minVal = currentParam.Min; double maxVal = currentParam.Max; int point = currentParam.Point; double originalValue = currentParam.Value; double deltaStep = originalValue/100.0; for (int p = 0; p < deviationSteps; p++) { int index = p - percentDeviation; double value = originalValue + index*deltaStep; value = Math.Round(value, point); if (index == 0) value = originalValue; if (value < minVal) value = minVal; if (value > maxVal) value = maxVal; tableParameters.SetData(index, countStratParams, value); cycles++; } paramNames.Add(currentParam.Caption); listParametersName += (countStratParams + 1) + Configs.ColumnSeparator + currentParam.Caption + Environment.NewLine; countStratParams++; } for (int prm = countStratParams; prm < countParam; prm++) listParametersName += (prm + 1) + Environment.NewLine; listParametersName += Environment.NewLine; }
/// <summary> /// Sets the chart parameters /// </summary> public void InitChart(OverOptimizationDataTable table) { Table = table; // Chart Title chartTitle = table.Name; font = new Font(Font.FontFamily, 9); captionHeight = Math.Max(font.Height, 18); rectfCaption = new RectangleF(0, 0, ClientSize.Width, captionHeight); stringFormatCaption = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap }; brushFore = new SolidBrush(LayoutColors.ColorChartFore); penGrid = new Pen(LayoutColors.ColorChartGrid) {DashStyle = DashStyle.Dash, DashPattern = new float[] {4, 2}}; penBorder = new Pen(Data.GetGradientColor(LayoutColors.ColorCaptionBack, -LayoutColors.DepthCaption), Border); devSteps = table.CountDeviationSteps; percentDev = (devSteps - 1)/2; parameters = table.CountStrategyParams; if (parameters == 0) return; double minValue = double.MaxValue; double maxValue = double.MinValue; for (int param = 0; param < parameters; param++) { double min = double.MaxValue; double max = double.MinValue; for (int dev = 0; dev < devSteps; dev++) { int index = dev - percentDev; double value = table.GetData(index, param); if (min > value) min = value; if (max < value) max = value; } if (minValue > min) minValue = min; if (maxValue < max) maxValue = max; } maximum = (int) Math.Round(maxValue + 0.1*Math.Abs(maxValue)); minimum = (int) Math.Round(minValue - 0.1*Math.Abs(minValue)); int roundStep = Math.Abs(minimum) > 1 ? 10 : 1; minimum = (int) (Math.Floor(minimum/(float) roundStep)*roundStep); if (Math.Abs(maximum - minimum) < 1) maximum = minimum + 1; yTop = (int) captionHeight + 2*Space + 1; yBottom = ClientSize.Height - 2*Space - 1 - Border - Space - Font.Height; Graphics g = CreateGraphics(); labelWidth = (int) Math.Max(g.MeasureString(minimum.ToString(CultureInfo.InvariantCulture), font).Width, g.MeasureString(maximum.ToString(CultureInfo.InvariantCulture), font).Width); g.Dispose(); labelWidth = Math.Max(labelWidth, 30); xLeft = Border + 3*Space; xRight = ClientSize.Width - Border - 2*Space - labelWidth; xScale = (xRight - xLeft)/(float) (devSteps - 1); xMiddle = (int) (xLeft + percentDev*xScale); countLabels = Math.Max((yBottom - yTop)/20, 2); delta = (float) Math.Max(Math.Round((maximum - minimum)/(float) countLabels), roundStep); step = (int) Math.Ceiling(delta/roundStep)*roundStep; countLabels = (int) Math.Ceiling((maximum - minimum)/(float) step); maximum = minimum + countLabels*step; yScale = (yBottom - yTop)/(countLabels*(float) step); apntParameters = new PointF[parameters][]; for (int param = 0; param < parameters; param++) { apntParameters[param] = new PointF[devSteps]; for (int dev = 0; dev < devSteps; dev++) { int index = dev - percentDev; apntParameters[param][dev].X = xLeft + dev*xScale; apntParameters[param][dev].Y = (float) (yBottom - (table.GetData(index, param) - minimum)*yScale); } } }
/// <summary> /// Sets table with parameter values. /// </summary> private void SetParametersValues(int percentDeviation, int countParam) { paramNames = new List <string>(); listParametersName = "Index" + Configs.ColumnSeparator + "Parameter name" + Environment.NewLine; countStratParams = 0; cycles = 0; deviationSteps = 2 * percentDeviation + 1; tableParameters = new OverOptimizationDataTable(percentDeviation, countParam) { Name = "Values of the Parameters" }; for (int slot = 0; slot < Data.Strategy.Slots; slot++) { for (int numParam = 0; numParam < 6; numParam++) { if (Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Enabled && countStratParams < countParam) { NumericParam currentParam = Data.Strategy.Slot[slot].IndParam.NumParam[numParam]; double minVal = currentParam.Min; double maxVal = currentParam.Max; int point = currentParam.Point; double originalValue = currentParam.Value; double deltaStep = originalValue / 100.0; for (int p = 0; p < deviationSteps; p++) { int index = p - percentDeviation; double value = originalValue + index * deltaStep; value = Math.Round(value, point); if (index == 0) { value = originalValue; } if (value < minVal) { value = minVal; } if (value > maxVal) { value = maxVal; } tableParameters.SetData(index, countStratParams, value); cycles++; } paramNames.Add(currentParam.Caption); listParametersName += (countStratParams + 1) + Configs.ColumnSeparator + currentParam.Caption + Environment.NewLine; countStratParams++; } } } for (int prm = countStratParams; prm < countParam; prm++) { listParametersName += (prm + 1) + Environment.NewLine; } listParametersName += Environment.NewLine; }
/// <summary> /// Calculates Data Tables. /// </summary> private void CalculateStatsTables(int percentDeviation, int countParam) { string unit = " " + Configs.AccountCurrency; var tableNames = new[] { Language.T("Account balance") + unit, Language.T("Profit per day") + unit, Language.T("Maximum drawdown") + unit, Language.T("Gross profit") + unit, Language.T("Gross loss") + unit, Language.T("Executed orders"), Language.T("Traded lots"), Language.T("Time in position") + " %", Language.T("Sent orders"), Language.T("Charged spread") + unit, Language.T("Charged rollover") + unit, Language.T("Winning trades"), Language.T("Losing trades"), Language.T("Win/loss ratio"), Language.T("Max equity drawdown") + " %" }; tablesCount = tableNames.Length; tableReport = new OverOptimizationDataTable[tablesCount]; for (int t = 0; t < tableNames.Length; t++) { tableReport[t] = new OverOptimizationDataTable(percentDeviation, countParam) { Name = tableNames[t] }; } int parNumber = 0; bool isBgWorkCanceled = false; for (int slot = 0; slot < Data.Strategy.Slots && !isBgWorkCanceled; slot++) { for (int numParam = 0; numParam < 6 && !isBgWorkCanceled; numParam++) { if (Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Enabled && parNumber < countParam) { for (int index = percentDeviation; index >= -percentDeviation && !isBgWorkCanceled; index--) { isBgWorkCanceled = bgWorker.CancellationPending; Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Value = tableParameters.GetData( index, parNumber); CalculateIndicator(slot); Backtester.Calculate(); Backtester.CalculateAccountStats(); var statValues = new[] { Backtester.NetMoneyBalance, Backtester.MoneyProfitPerDay, Backtester.MaxMoneyDrawdown, Backtester.GrossMoneyProfit, Backtester.GrossMoneyLoss, Backtester.ExecutedOrders, Backtester.TradedLots, Backtester.TimeInPosition, Backtester.SentOrders, Backtester.TotalChargedMoneySpread, Backtester.TotalChargedMoneyRollOver, Backtester.WinningTrades, Backtester.LosingTrades, Backtester.WinLossRatio, Backtester.MoneyEquityPercentDrawdown }; for (int tn = 0; tn < tablesCount; tn++) { tableReport[tn].SetData(index, parNumber, statValues[tn]); } // Report progress as a percentage of the total task. computedCycles++; int percentComplete = Math.Min(100 * computedCycles / cycles, 100); if (percentComplete > progressPercent) { progressPercent = percentComplete; bgWorker.ReportProgress(percentComplete); } } // Set default value Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Value = tableParameters.GetData(0, parNumber); CalculateIndicator(slot); parNumber++; } } } }
/// <summary> /// Calculates Data Tables. /// </summary> private void CalculateStatsTables(int percentDeviation, int countParam) { string unit = " " + Configs.AccountCurrency; var tableNames = new[] { Language.T("Account balance") + unit, Language.T("Profit per day") + unit, Language.T("Maximum drawdown") + unit, Language.T("Gross profit") + unit, Language.T("Gross loss") + unit, Language.T("Executed orders"), Language.T("Traded lots"), Language.T("Time in position") + " %", Language.T("Sent orders"), Language.T("Charged spread") + unit, Language.T("Charged rollover") + unit, Language.T("Winning trades"), Language.T("Losing trades"), Language.T("Win/loss ratio"), Language.T("Max equity drawdown") + " %" }; tablesCount = tableNames.Length; tableReport = new OverOptimizationDataTable[tablesCount]; for (int t = 0; t < tableNames.Length; t++) { tableReport[t] = new OverOptimizationDataTable(percentDeviation, countParam) {Name = tableNames[t]}; } int parNumber = 0; bool isBgWorkCanceled = false; for (int slot = 0; slot < Data.Strategy.Slots && !isBgWorkCanceled; slot++) for (int numParam = 0; numParam < 6 && !isBgWorkCanceled; numParam++) if (Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Enabled && parNumber < countParam) { for (int index = percentDeviation; index >= -percentDeviation && !isBgWorkCanceled; index--) { isBgWorkCanceled = bgWorker.CancellationPending; Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Value = tableParameters.GetData( index, parNumber); CalculateIndicator(slot); Backtester.Calculate(); Backtester.CalculateAccountStats(); var statValues = new[] { Backtester.NetMoneyBalance, Backtester.MoneyProfitPerDay, Backtester.MaxMoneyDrawdown, Backtester.GrossMoneyProfit, Backtester.GrossMoneyLoss, Backtester.ExecutedOrders, Backtester.TradedLots, Backtester.TimeInPosition, Backtester.SentOrders, Backtester.TotalChargedMoneySpread, Backtester.TotalChargedMoneyRollOver, Backtester.WinningTrades, Backtester.LosingTrades, Backtester.WinLossRatio, Backtester.MoneyEquityPercentDrawdown }; for (int tn = 0; tn < tablesCount; tn++) tableReport[tn].SetData(index, parNumber, statValues[tn]); // Report progress as a percentage of the total task. computedCycles++; int percentComplete = Math.Min(100*computedCycles/cycles, 100); if (percentComplete > progressPercent) { progressPercent = percentComplete; bgWorker.ReportProgress(percentComplete); } } // Set default value Data.Strategy.Slot[slot].IndParam.NumParam[numParam].Value = tableParameters.GetData(0, parNumber); CalculateIndicator(slot); parNumber++; } }
/// <summary> /// Sets the chart parameters /// </summary> public void InitChart(OverOptimizationDataTable table) { Table = table; // Chart Title chartTitle = table.Name; font = new Font(Font.FontFamily, 9); captionHeight = Math.Max(font.Height, 18); rectfCaption = new RectangleF(0, 0, ClientSize.Width, captionHeight); stringFormatCaption = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap }; brushFore = new SolidBrush(LayoutColors.ColorChartFore); penGrid = new Pen(LayoutColors.ColorChartGrid) { DashStyle = DashStyle.Dash, DashPattern = new float[] { 4, 2 } }; penBorder = new Pen(Data.GetGradientColor(LayoutColors.ColorCaptionBack, -LayoutColors.DepthCaption), Border); devSteps = table.CountDeviationSteps; percentDev = (devSteps - 1) / 2; parameters = table.CountStrategyParams; if (parameters == 0) { return; } double minValue = double.MaxValue; double maxValue = double.MinValue; for (int param = 0; param < parameters; param++) { double min = double.MaxValue; double max = double.MinValue; for (int dev = 0; dev < devSteps; dev++) { int index = dev - percentDev; double value = table.GetData(index, param); if (min > value) { min = value; } if (max < value) { max = value; } } if (minValue > min) { minValue = min; } if (maxValue < max) { maxValue = max; } } maximum = (int)Math.Round(maxValue + 0.1 * Math.Abs(maxValue)); minimum = (int)Math.Round(minValue - 0.1 * Math.Abs(minValue)); int roundStep = Math.Abs(minimum) > 1 ? 10 : 1; minimum = (int)(Math.Floor(minimum / (float)roundStep) * roundStep); if (Math.Abs(maximum - minimum) < 1) { maximum = minimum + 1; } yTop = (int)captionHeight + 2 * Space + 1; yBottom = ClientSize.Height - 2 * Space - 1 - Border - Space - Font.Height; Graphics g = CreateGraphics(); labelWidth = (int) Math.Max(g.MeasureString(minimum.ToString(CultureInfo.InvariantCulture), font).Width, g.MeasureString(maximum.ToString(CultureInfo.InvariantCulture), font).Width); g.Dispose(); labelWidth = Math.Max(labelWidth, 30); xLeft = Border + 3 * Space; xRight = ClientSize.Width - Border - 2 * Space - labelWidth; xScale = (xRight - xLeft) / (float)(devSteps - 1); xMiddle = (int)(xLeft + percentDev * xScale); countLabels = Math.Max((yBottom - yTop) / 20, 2); delta = (float)Math.Max(Math.Round((maximum - minimum) / (float)countLabels), roundStep); step = (int)Math.Ceiling(delta / roundStep) * roundStep; countLabels = (int)Math.Ceiling((maximum - minimum) / (float)step); maximum = minimum + countLabels * step; yScale = (yBottom - yTop) / (countLabels * (float)step); apntParameters = new PointF[parameters][]; for (int param = 0; param < parameters; param++) { apntParameters[param] = new PointF[devSteps]; for (int dev = 0; dev < devSteps; dev++) { int index = dev - percentDev; apntParameters[param][dev].X = xLeft + dev * xScale; apntParameters[param][dev].Y = (float)(yBottom - (table.GetData(index, param) - minimum) * yScale); } } }