/// <summary> /// Exports the positions info in currency /// </summary> public void ExportPositionsInMoney() { string stage = String.Empty; if (Data.IsProgramBeta) { stage = " " + Language.T("Beta"); } else if (Data.IsProgramRC) { stage = " " + "RC"; } sb.Append("Forex Strategy Builder v" + Data.ProgramVersion + stage + Environment.NewLine); sb.Append("Strategy name: " + Data.Strategy.StrategyName + Environment.NewLine); sb.Append("Exported on " + DateTime.Now.ToString() + Environment.NewLine); sb.Append(Data.Symbol + " " + Data.PeriodString + "; Values in " + Configs.AccountCurrency + Environment.NewLine); sb.Append("Pos Numb\t"); sb.Append("Bar Numb\t"); sb.Append("Bar Opening Time\t"); sb.Append("Direction\t"); sb.Append("Amount\t"); sb.Append("Transaction\t"); sb.Append("Order Price\t"); sb.Append("Price\t"); sb.Append("Profit Loss\t"); sb.Append("Floating P/L\t"); sb.Append("Balance\t"); sb.Append("Equity\t"); sb.Append(Environment.NewLine); for (int iPos = 0; iPos < Backtester.PositionsTotal; iPos++) { Position position = Backtester.PosFromNumb(iPos); int bar = Backtester.PosCoordinates[iPos].Bar; sb.Append((position.PosNumb + 1).ToString() + "\t"); sb.Append((bar + 1).ToString() + "\t"); sb.Append((Data.Time[bar]).ToString() + "\t"); sb.Append((position.PosDir).ToString() + "\t"); sb.Append((position.PosDir == PosDirection.Long ? "" : "-") + (position.PosLots * Data.InstrProperties.LotSize).ToString() + "\t"); sb.Append((position.Transaction).ToString() + "\t"); sb.Append((position.FormOrdPrice).ToString(FF) + "\t"); sb.Append((position.PosPrice).ToString(FF) + "\t"); sb.Append((position.MoneyProfitLoss).ToString("F2") + "\t"); sb.Append((position.MoneyFloatingPL).ToString("F2") + "\t"); sb.Append((position.MoneyBalance).ToString("F2") + "\t"); sb.Append((position.MoneyEquity).ToString("F2") + "\t"); sb.Append(Environment.NewLine); } string fileName = Data.Strategy.StrategyName + "-" + Data.Symbol.ToString() + "-" + Data.Period.ToString(); SaveData(fileName); return; }
/// <summary> /// Exports the positions info /// </summary> public void ExportPositions() { string stage = String.Empty; if (Data.IsProgramBeta) { stage = " " + Language.T("Beta"); } else if (Data.IsProgramRC) { stage = " " + "RC"; } sb.Append("Forex Strategy Builder v" + Data.ProgramVersion + stage + Environment.NewLine); sb.Append("Strategy name: " + Data.Strategy.StrategyName + Environment.NewLine); sb.Append("Exported on " + DateTime.Now.ToString() + Environment.NewLine); sb.Append(Data.Symbol + " " + Data.PeriodString + "; Values in pips" + Environment.NewLine); sb.Append("Pos Numb\t"); sb.Append("Bar Numb\t"); sb.Append("Bar Opening Time\t"); sb.Append("Direction\t"); sb.Append("Lots\t"); sb.Append("Transaction\t"); sb.Append("Order Price\t"); sb.Append("Average Price\t"); sb.Append("Profit Loss\t"); sb.Append("Floating P/L\t"); sb.Append("Balance\t"); sb.Append("Equity\t"); sb.Append(Environment.NewLine); for (int iPos = 0; iPos < Backtester.PositionsTotal; iPos++) { Position position = Backtester.PosFromNumb(iPos); int bar = Backtester.PosCoordinates[iPos].Bar; sb.Append((position.PosNumb + 1).ToString() + "\t"); sb.Append((bar + 1).ToString() + "\t"); sb.Append((Data.Time[bar]).ToString() + "\t"); sb.Append((position.PosDir).ToString() + "\t"); sb.Append((position.PosLots).ToString() + "\t"); sb.Append((position.Transaction).ToString() + "\t"); sb.Append((position.FormOrdPrice).ToString(FF) + "\t"); sb.Append((position.PosPrice).ToString(FF) + "\t"); sb.Append((Math.Round(position.ProfitLoss)).ToString() + "\t"); sb.Append((Math.Round(position.FloatingPL)).ToString() + "\t"); sb.Append((Math.Round(position.Balance)).ToString() + "\t"); sb.Append((Math.Round(position.Equity)).ToString() + "\t"); sb.Append(Environment.NewLine); } string fileName = Data.Strategy.StrategyName + "-" + Data.Symbol.ToString() + "-" + Data.Period.ToString(); SaveData(fileName); return; }
/// <summary> /// Show position /// </summary> private void ShowPosition(string input) { const string pattern = @"^pos (?<numb>\d+)$"; var expression = new Regex(pattern, RegexOptions.Compiled); Match match = expression.Match(input); if (match.Success) { int pos = int.Parse(match.Groups["numb"].Value); if (pos < 1 || pos > Backtester.PositionsTotal) { return; } Position position = Backtester.PosFromNumb(pos - 1); TbxOutput.Text += "Position" + Environment.NewLine + "-----------------" + Environment.NewLine + position + Environment.NewLine; } }
/// <summary> /// Updates the journal data from the backtester /// </summary> void UpdateJournalData() { asJournalData = new string[shownPos, columns]; aiPositionIcons = new Image[shownPos]; for (int posIndex = firstPos; posIndex < firstPos + shownPos; posIndex++) { int posNumber = posIndex; if (!showTransfers) { posNumber = aiPosNumber[posIndex]; } int row = posIndex - firstPos; int bar = Backtester.PosCoordinates[posNumber].Bar; Position position = Backtester.PosFromNumb(posNumber); string posAmount; if (Configs.AccountInMoney) { posAmount = (position.PosDir == PosDirection.Short ? "-" : "") + (position.PosLots * Data.InstrProperties.LotSize).ToString(); } else { posAmount = position.PosLots.ToString(); } string profitLoss; if (Configs.AccountInMoney) { profitLoss = position.MoneyProfitLoss.ToString("F2"); } else { profitLoss = position.ProfitLoss.ToString("F2"); } string floatingPL; if (Configs.AccountInMoney) { floatingPL = position.MoneyFloatingPL.ToString("F2"); } else { floatingPL = position.FloatingPL.ToString("F2"); } int p = 0; asJournalData[row, p++] = (posNumber + 1).ToString(); asJournalData[row, p++] = (bar + 1).ToString(); asJournalData[row, p++] = Data.Time[bar].ToString(Data.DF) + Data.Time[bar].ToString(" HH:mm"); asJournalData[row, p++] = Language.T(position.Transaction.ToString()); asJournalData[row, p++] = Language.T(position.PosDir.ToString()); asJournalData[row, p++] = posAmount; asJournalData[row, p++] = position.FormOrdPrice.ToString(Data.FF); asJournalData[row, p++] = position.PosPrice.ToString(Data.FF); asJournalData[row, p++] = position.RequiredMargin.ToString("F2"); // Charges if (Configs.AccountInMoney) { // in currency if (position.Transaction == Transaction.Open || position.Transaction == Transaction.Add || position.Transaction == Transaction.Reverse) { asJournalData[row, p++] = position.MoneySpread.ToString("F2"); } else { asJournalData[row, p++] = "-"; } if (position.Transaction == Transaction.Transfer) { asJournalData[row, p++] = position.MoneyRollover.ToString("F2"); } else { asJournalData[row, p++] = "-"; } if (position.Transaction == Transaction.Open || position.Transaction == Transaction.Close || position.Transaction == Transaction.Add || position.Transaction == Transaction.Reduce || position.Transaction == Transaction.Reverse) { asJournalData[row, p++] = position.MoneyCommission.ToString("F2"); asJournalData[row, p++] = position.MoneySlippage.ToString("F2"); } else { asJournalData[row, p++] = "-"; asJournalData[row, p++] = "-"; } } else { // In pips if (position.Transaction == Transaction.Open || position.Transaction == Transaction.Add || position.Transaction == Transaction.Reverse) { asJournalData[row, p++] = position.Spread.ToString(); } else { asJournalData[row, p++] = "-"; } if (position.Transaction == Transaction.Transfer) { asJournalData[row, p++] = position.Rollover.ToString("F2"); } else { asJournalData[row, p++] = "-"; } if (position.Transaction == Transaction.Open || position.Transaction == Transaction.Close || position.Transaction == Transaction.Add || position.Transaction == Transaction.Reduce || position.Transaction == Transaction.Reverse) { asJournalData[row, p++] = position.Commission.ToString("F2"); asJournalData[row, p++] = position.Slippage.ToString(); } else { asJournalData[row, p++] = "-"; asJournalData[row, p++] = "-"; } } // Profit Loss if (position.Transaction == Transaction.Close || position.Transaction == Transaction.Reduce || position.Transaction == Transaction.Reverse) { asJournalData[row, p++] = profitLoss; } else { asJournalData[row, p++] = "-"; } // Floating Profit Loss if (position.PosNumb == Backtester.SummaryPosNumb(bar) && position.Transaction != Transaction.Close) { asJournalData[row, p++] = floatingPL; //Last position of the bar only } else { asJournalData[row, p++] = "-"; } // Balance / Equity if (Configs.AccountInMoney) { asJournalData[row, p++] = position.MoneyBalance.ToString("F2"); asJournalData[row, p++] = position.MoneyEquity.ToString("F2"); } else { asJournalData[row, p++] = position.Balance.ToString("F2"); asJournalData[row, p++] = position.Equity.ToString("F2"); } asJournalData[row, p++] = Language.T(Backtester.BackTestEval(bar)); // Icons aiPositionIcons[row] = position.PositionIcon; } return; }
/// <summary> /// Paints panel pnlInfo /// </summary> void PnlInfo_Paint(object sender, PaintEventArgs e) { // ---------------------------------------------------------------------+ // | Way points description | // |--------------------------------------------------------------------+ // | Number | Description | Price | Direction | Lots | Position | Order | // |--------------------------------------------------------------------+ //xp0 xp1 xp2 xp3 xp4 xp5 xp6 xp7 Graphics g = e.Graphics; g.Clear(LayoutColors.ColorControlBack); if (!Data.IsData || !Data.IsResult) { return; } Panel pnl = (Panel)sender; Brush brush = Brushes.White; string FF = Data.FF; // Format modifier to print the floats Size size = new Size(aiX[columns] - aiX[0], infoRowHeight); StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Near; // Caption background PointF pntStart = new PointF(0, 0); SizeF szfCaption = new Size(pnl.ClientSize.Width, 2 * infoRowHeight); RectangleF rectfCaption = new RectangleF(pntStart, szfCaption); Data.GradientPaint(g, rectfCaption, LayoutColors.ColorCaptionBack, LayoutColors.DepthCaption); // Caption Text StringFormat stringFormatCaption = new StringFormat(); stringFormatCaption.LineAlignment = StringAlignment.Center; stringFormatCaption.Trimming = StringTrimming.EllipsisCharacter; stringFormatCaption.FormatFlags = StringFormatFlags.NoWrap; stringFormatCaption.Alignment = StringAlignment.Near; string stringCaptionText = Language.T("Way Points Description"); float fCaptionWidth = (float)Math.Min(pnlInfo.ClientSize.Width, aiX[columns] - aiX[0]); float fCaptionTextWidth = g.MeasureString(stringCaptionText, fontInfo).Width; float fCaptionTextX = (float)Math.Max((fCaptionWidth - fCaptionTextWidth) / 2f, 0); PointF pfCaptionText = new PointF(fCaptionTextX, 0); SizeF sfCaptionText = new SizeF(fCaptionWidth - fCaptionTextX, infoRowHeight); rectfCaption = new RectangleF(pfCaptionText, sfCaptionText); // First caption raw g.DrawString(stringCaptionText, fontInfo, brushCaptionText, rectfCaption, stringFormatCaption); // Second caption raw for (int i = 0; i < columns; i++) { g.DrawString(asTitles[i], fontInfo, brushCaptionText, (aiX[i] + aiX[i + 1]) / 2, infoRowHeight, sf); } brush = new SolidBrush(LayoutColors.ColorControlText); for (int pnt = 0; pnt < Backtester.WayPoints(bar); pnt++) { int y = (pnt + 2) * infoRowHeight; Point point = new Point(aiX[0], y); // Even row if (pnt % 2f != 0) { g.FillRectangle(brushEvenRow, new Rectangle(point, size)); } int positionNumber = Backtester.WayPoint(bar, pnt).PosNumb; WayPointType wpType = Backtester.WayPoint(bar, pnt).WPType; PosDirection posDirection = Backtester.PosFromNumb(positionNumber).PosDir; double posLots = Backtester.PosFromNumb(positionNumber).PosLots; int ordNumber = Backtester.WayPoint(bar, pnt).OrdNumb; g.DrawString((pnt + 1).ToString(), fontInfo, brush, (aiX[0] + aiX[1]) / 2, y, sf); g.DrawString(Language.T(Way_Point.WPTypeToString(wpType)), fontInfo, brush, aiX[1] + 2, y); g.DrawString(Backtester.WayPoint(bar, pnt).Price.ToString(FF), fontInfo, brush, (aiX[3] + aiX[2]) / 2, y, sf); if (positionNumber > -1) { g.DrawString(Language.T(posDirection.ToString()), fontInfo, brush, (aiX[4] + aiX[3]) / 2, y, sf); g.DrawString(posLots.ToString(), fontInfo, brush, (aiX[5] + aiX[4]) / 2, y, sf); g.DrawString((positionNumber + 1).ToString(), fontInfo, brush, (aiX[6] + aiX[5]) / 2, y, sf); } if (ordNumber > -1) { g.DrawString((ordNumber + 1).ToString(), fontInfo, brush, (aiX[7] + aiX[6]) / 2, y, sf); } } // Vertical lines Pen penLine = new Pen(LayoutColors.ColorJournalLines); for (int i = 1; i < columns; i++) { g.DrawLine(penLine, aiX[i], 2 * infoRowHeight, aiX[i], ClientSize.Height - border); } // Border Pen penBorder = new Pen(Data.GetGradientColor(LayoutColors.ColorCaptionBack, -LayoutColors.DepthCaption), border); g.DrawLine(penBorder, 1, 2 * infoRowHeight, 1, pnl.ClientSize.Height); g.DrawLine(penBorder, pnl.ClientSize.Width - border + 1, 2 * infoRowHeight, pnl.ClientSize.Width - border + 1, pnl.ClientSize.Height); g.DrawLine(penBorder, 0, pnl.ClientSize.Height - border + 1, pnl.ClientSize.Width, pnl.ClientSize.Height - border + 1); return; }
/// <summary> /// Paints panel pnlChart /// </summary> private void PnlChartPaint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(LayoutColors.ColorChartBack); if (!Data.IsData || !Data.IsResult) { return; } var pnl = (Panel)sender; int width = pnl.ClientSize.Width; // Caption background var pntStart = new PointF(0, 0); SizeF szfCaption = new Size(width, _infoRowHeight); var rectfCaption = new RectangleF(pntStart, szfCaption); Data.GradientPaint(g, rectfCaption, LayoutColors.ColorCaptionBack, LayoutColors.DepthCaption); // Caption Text var stringFormat = new StringFormat { LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap, Alignment = StringAlignment.Center }; string stringCaptionText = Language.T("Price Route Inside the Bar"); rectfCaption = new RectangleF(Border, 0, pnl.ClientSize.Width - 2 * Border, _infoRowHeight); g.DrawString(stringCaptionText, _fontInfo, _brushCaptionText, rectfCaption, stringFormat); // Paint the panel background var rectClient = new RectangleF(0, _infoRowHeight, pnl.ClientSize.Width, pnl.Height - _infoRowHeight); Data.GradientPaint(g, rectClient, LayoutColors.ColorChartBack, LayoutColors.DepthControl); // Paint bar info var rectBarInfo = new RectangleF(Border, _infoRowHeight + 1, pnl.ClientSize.Width - 2 * Border, _infoRowHeight); g.DrawString(_barInfo, _fontInfo, _brushGridText, rectBarInfo, stringFormat); // Searching the min and the max price and volume width = pnl.ClientSize.Width - 2 * Border; double maxPrice = Data.High[_barCurrent]; double minPrice = Data.Low[_barCurrent]; const int space = 8; int spcRight = _szPrice.Width + 4; const int xLeft = Border + space; int xRight = width - spcRight; int yTop = 2 * _infoRowHeight + 6; int yBottom = pnl.ClientSize.Height - 22; int barPixels = _maxWayPoints < 10 ? 28 : _maxWayPoints < 15 ? 24 : 20; const int spcLeft = 3; int x = barPixels + spcLeft; int pointLeft = x + barPixels + 30; int pointRight = xRight - 20; int points = Backtester.WayPoints(_barCurrent); const int pointRadius = 3; // Grid var iCntLabels = (int)Math.Max((yBottom - yTop) / 30d, 1); double deltaPoint = (Data.InstrProperties.Digits == 5 || Data.InstrProperties.Digits == 3) ? Data.InstrProperties.Point * 100 : Data.InstrProperties.Point * 10; double delta = Math.Max(Math.Round((maxPrice - minPrice) / iCntLabels, Data.InstrProperties.Point < 0.001 ? 3 : 1), deltaPoint); minPrice = Math.Round(minPrice, Data.InstrProperties.Point < 0.001f ? 3 : 1) - Data.InstrProperties.Point * 10; minPrice -= delta; maxPrice += delta; iCntLabels = (int)Math.Ceiling((maxPrice - minPrice) / delta); maxPrice = minPrice + iCntLabels * delta; double scaleY = (yBottom - yTop) / (iCntLabels * delta); var yOpen = (int)(yBottom - (Data.Open[_barCurrent] - minPrice) * scaleY); var yHigh = (int)(yBottom - (Data.High[_barCurrent] - minPrice) * scaleY); var yLow = (int)(yBottom - (Data.Low[_barCurrent] - minPrice) * scaleY); var yClose = (int)(yBottom - (Data.Close[_barCurrent] - minPrice) * scaleY); // Find the price distance double priceDistance = 0; for (int point = 1; point < points; point++) { priceDistance += Math.Abs(Backtester.WayPoint(_barCurrent, point).Price - Backtester.WayPoint(_barCurrent, point - 1).Price); } double dPriceForAPixel = (pointRight - pointLeft) / priceDistance; // Points X var aiPointX = new int[points]; aiPointX[0] = pointLeft; for (int point = 1; point < points - 1; point++) { var iDistance = (int) (Math.Abs(Backtester.WayPoint(_barCurrent, point).Price - Backtester.WayPoint(_barCurrent, point - 1).Price) * dPriceForAPixel); aiPointX[point] = aiPointX[point - 1] + iDistance; } aiPointX[points - 1] = pointRight; for (int point = 1; point < points - 1; point++) { if (aiPointX[point] - aiPointX[point - 1] < barPixels + 1) { aiPointX[point] = aiPointX[point - 1] + barPixels + 1; } } for (int point = points - 2; point > 0; point--) { if (aiPointX[point + 1] - aiPointX[point] < barPixels + 1) { aiPointX[point] = aiPointX[point + 1] - barPixels - 1; } } // Find coordinates of the Way Points var pntWay = new Point[points]; for (int point = 0; point < points; point++) { var pointY = (int)(yBottom - (Backtester.WayPoint(_barCurrent, point).Price - minPrice) * scaleY); pntWay[point] = new Point(aiPointX[point], pointY); } // Horizontal grid and Price labels for (double label = minPrice; label <= maxPrice + Data.InstrProperties.Point; label += delta) { var labelY = (int)(yBottom - (label - minPrice) * scaleY); g.DrawString(label.ToString(Data.FF), Font, _brushGridText, xRight, labelY - Font.Height / 2 - 1); g.DrawLine(_penGrid, Border + space, labelY, xRight, labelY); } // Vertical Grid g.DrawLine(_penGrid, x + barPixels / 2 - 1, yTop, x + barPixels / 2 - 1, yBottom + 2); for (int point = 0; point < points; point++) { var pt1 = new Point(pntWay[point].X, yTop); var pt2 = new Point(pntWay[point].X, yBottom + 2); var pt3 = new Point(pntWay[point].X - 5, yBottom + 4); g.DrawLine(_penGrid, pt1, pt2); g.DrawString((point + 1).ToString(CultureInfo.InvariantCulture), Font, _brushGridText, pt3); } // Bar Number string barNumber = (_barCurrent + 1).ToString(CultureInfo.InvariantCulture); int stringX = x + barPixels / 2 - 1 - g.MeasureString(barNumber, Font).ToSize().Width / 2; Brush barBrush = Backtester.BackTestEval(_barCurrent) == BacktestEval.Ambiguous ? _brushRed : _brushGridText; g.DrawString(barNumber, Font, barBrush, stringX, yBottom + 4); // Draw the bar g.DrawLine(_penBarBorder, x + barPixels / 2 - 1, yLow, x + barPixels / 2 - 1, yHigh); if (yClose < yOpen) // White bar { var rect = new Rectangle(x, yClose, barPixels - 2, yOpen - yClose); var lgBrush = new LinearGradientBrush(rect, _colorBarWight1, _colorBarWight2, 5f); g.FillRectangle(lgBrush, rect); g.DrawRectangle(_penBarBorder, x, yClose, barPixels - 2, yOpen - yClose); } else if (yClose > yOpen) // Black bar { var rect = new Rectangle(x, yOpen, barPixels - 2, yClose - yOpen); var lgBrush = new LinearGradientBrush(rect, _colorBarBlack1, _colorBarBlack2, 5f); g.FillRectangle(lgBrush, rect); g.DrawRectangle(_penBarBorder, x, yOpen, barPixels - 2, yClose - yOpen); } else // Cross { g.DrawLine(_penBarBorder, x, yClose, x + barPixels - 2, yClose); } // Draw cancelled orders for (int orderIndex = 0; orderIndex < Backtester.Orders(_barCurrent); orderIndex++) { int ordNumber = Backtester.OrdNumb(_barCurrent, orderIndex); Order order = Backtester.OrdFromNumb(ordNumber); if (order.OrdStatus != OrderStatus.Cancelled) { continue; } if (order.OrdPrice > Data.High[_barCurrent] || order.OrdPrice < Data.Low[_barCurrent]) { continue; } int d = barPixels / 2 - 1; int x1 = x + d; int x2 = x + barPixels - 2; var yDeal = (int)(yBottom - (order.OrdPrice - minPrice) * scaleY); var pen = new Pen(LayoutColors.ColorChartGrid, 2); if (order.OrdDir == OrderDirection.Buy) { g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal, x2, yDeal - d); g.DrawLine(pen, x2 + 1, yDeal - d + 1, x1 + d / 2 + 1, yDeal - d + 1); g.DrawLine(pen, x2, yDeal - d, x2, yDeal - d / 2); } else if (order.OrdDir == OrderDirection.Sell) { g.DrawLine(pen, x, yDeal + 1, x1 + 1, yDeal + 1); g.DrawLine(pen, x1, yDeal, x2, yDeal + d); g.DrawLine(pen, x1 + d / 2 + 1, yDeal + d, x2, yDeal + d); g.DrawLine(pen, x2, yDeal + d, x2, yDeal + d / 2 + 1); } } // Draw the deals on the bar for (int pos = 0; pos < Backtester.Positions(_barCurrent); pos++) { if (Backtester.PosTransaction(_barCurrent, pos) == Transaction.Transfer) { continue; } var yDeal = (int)(yBottom - (Backtester.PosOrdPrice(_barCurrent, pos) - minPrice) * scaleY); if (Backtester.PosDir(_barCurrent, pos) == PosDirection.Long || Backtester.PosDir(_barCurrent, pos) == PosDirection.Short) { int d = barPixels / 2 - 1; int x1 = x + d; int x2 = x + barPixels - 2; if (Backtester.OrdFromNumb(Backtester.PosOrdNumb(_barCurrent, pos)).OrdDir == OrderDirection.Buy) { // Buy var pen = new Pen(LayoutColors.ColorTradeLong, 2); g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal, x2, yDeal - d); g.DrawLine(pen, x2 + 1, yDeal - d + 1, x1 + d / 2 + 1, yDeal - d + 1); g.DrawLine(pen, x2, yDeal - d, x2, yDeal - d / 2); } else { // Sell var pen = new Pen(LayoutColors.ColorTradeShort, 2); g.DrawLine(pen, x, yDeal + 1, x1 + 1, yDeal + 1); g.DrawLine(pen, x1, yDeal, x2, yDeal + d); g.DrawLine(pen, x1 + d / 2 + 1, yDeal + d, x2, yDeal + d); g.DrawLine(pen, x2, yDeal + d, x2, yDeal + d / 2 + 1); } } else if (Backtester.PosDir(_barCurrent, pos) == PosDirection.Closed) { // Close position int d = barPixels / 2 - 1; int x1 = x + d; int x2 = x + barPixels - 3; var pen = new Pen(LayoutColors.ColorTradeClose, 2); g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal + d / 2, x2, yDeal - d / 2); g.DrawLine(pen, x1, yDeal - d / 2, x2, yDeal + d / 2); } } // Draw position lots for (int point = 0; point < points; point++) { int posNumber = Backtester.WayPoint(_barCurrent, point).PosNumb; if (posNumber == -1) { continue; } Position position = Backtester.PosFromNumb(posNumber); double posLots = position.PosLots; PosDirection posDirection = position.PosDir; WayPointType wpType = Backtester.WayPoint(_barCurrent, point).WPType; var hight = (int)(Math.Max(posLots * 2, 2)); int lenght = barPixels; int posX = pntWay[point].X - (barPixels - 1) / 2; int posY = yBottom - hight; if (point < points - 1) { lenght = pntWay[point + 1].X - pntWay[point].X + 1; } if (posDirection == PosDirection.Long) { // Long var rect = new Rectangle(posX - 1, posY - 1, lenght, hight + 2); var lgBrush = new LinearGradientBrush(rect, _colorLongTrade1, _colorLongTrade2, LinearGradientMode.Vertical); rect = new Rectangle(posX - 1, posY, lenght, hight); g.FillRectangle(lgBrush, rect); } else if (posDirection == PosDirection.Short) { // Short var rect = new Rectangle(posX - 1, posY - 1, lenght, hight + 2); var lgBrush = new LinearGradientBrush(rect, _colorShortTrade1, _colorShortTrade2, LinearGradientMode.Vertical); rect = new Rectangle(posX - 1, posY, lenght, hight); g.FillRectangle(lgBrush, rect); } else if (posDirection == PosDirection.Closed && wpType == WayPointType.Exit) { // Closed var rect = new Rectangle(posX - 1, yBottom - 2, barPixels + 1, 2); var lgBrush = new LinearGradientBrush(rect, _colorClosedTrade1, _colorClosedTrade2, LinearGradientMode.Vertical); rect = new Rectangle(posX, yBottom - 2, barPixels - 1, 2); g.FillRectangle(lgBrush, rect); } } // Draw the Beziers for (int point = 1; point < points; point++) { Point ptKnot1 = pntWay[point - 1]; Point ptKnot2 = pntWay[point]; int ctrlX1 = (ptKnot1.X + ptKnot2.X) / 2; int ctrlX2 = (ptKnot1.X + ptKnot2.X) / 2; int ctrlY1 = ptKnot1.Y; int ctrlY2 = ptKnot2.Y; if (point > 1) { if (pntWay[point - 2].Y > pntWay[point - 1].Y && pntWay[point - 1].Y > pntWay[point].Y || pntWay[point - 2].Y < pntWay[point - 1].Y && pntWay[point - 1].Y < pntWay[point].Y) { ctrlY1 = (pntWay[point - 1].Y + pntWay[point].Y) / 2; } } if (point < points - 1) { if (pntWay[point - 1].Y > pntWay[point].Y && pntWay[point].Y > pntWay[point + 1].Y || pntWay[point - 1].Y < pntWay[point].Y && pntWay[point].Y < pntWay[point + 1].Y) { ctrlY2 = (pntWay[point - 1].Y + pntWay[point].Y) / 2; } } if (point == 1) { ctrlX1 = ptKnot1.X; ctrlY1 = ptKnot1.Y; } if (point == points - 1) { ctrlX2 = ptKnot2.X; ctrlY2 = ptKnot2.Y; } var ptControl1 = new Point(ctrlX1, ctrlY1); var ptControl2 = new Point(ctrlX2, ctrlY2); g.DrawBezier(_penCross, ptKnot1, ptControl1, ptControl2, ptKnot2); } // Draw the WayPoints Brush brushWeyPnt = new SolidBrush(LayoutColors.ColorChartBack); for (int point = 0; point < points; point++) { g.FillEllipse(brushWeyPnt, pntWay[point].X - pointRadius, pntWay[point].Y - pointRadius, 2 * pointRadius, 2 * pointRadius); g.DrawEllipse(_penCross, pntWay[point].X - pointRadius, pntWay[point].Y - pointRadius, 2 * pointRadius, 2 * pointRadius); } // Draw O, H, L, C labels for (int point = 0; point < points; point++) { WayPointType wpType = Backtester.WayPoint(_barCurrent, point).WPType; if (wpType != WayPointType.Open && wpType != WayPointType.High && wpType != WayPointType.Low && wpType != WayPointType.Close) { continue; } string label = "?"; switch (wpType) { case WayPointType.Open: label = "O"; break; case WayPointType.High: label = "H"; break; case WayPointType.Low: label = "L"; break; case WayPointType.Close: label = "C"; break; } int xPoint = pntWay[point].X; int yPoint = pntWay[point].Y - Font.Height - 3; var stringFormatLabel = new StringFormat { Alignment = StringAlignment.Center }; g.DrawString(label, Font, _brushGridText, xPoint, yPoint, stringFormatLabel); } // Draw the deals on the route for (int point = 0; point < points; point++) { int posNumber = Backtester.WayPoint(_barCurrent, point).PosNumb; int ordNumber = Backtester.WayPoint(_barCurrent, point).OrdNumb; if (posNumber < 0 || ordNumber < 0) { continue; } PosDirection posDirection = Backtester.PosFromNumb(posNumber).PosDir; OrderDirection ordDirection = Backtester.OrdFromNumb(ordNumber).OrdDir; WayPointType wpType = Backtester.WayPoint(_barCurrent, point).WPType; if (wpType == WayPointType.None || wpType == WayPointType.Open || wpType == WayPointType.High || wpType == WayPointType.Low || wpType == WayPointType.Close) { continue; } int yDeal = pntWay[point].Y; if (posDirection == PosDirection.Long || posDirection == PosDirection.Short || wpType == WayPointType.Cancel) { int d = barPixels / 2 - 1; x = pntWay[point].X - d; int x1 = pntWay[point].X; int x2 = x + barPixels - 2; if (ordDirection == OrderDirection.Buy) { // Buy var pen = new Pen(LayoutColors.ColorTradeLong, 2); if (wpType == WayPointType.Cancel) { pen = new Pen(LayoutColors.ColorChartGrid, 2); } g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal, x2, yDeal - d); g.DrawLine(pen, x2 + 1, yDeal - d + 1, x1 + d / 2 + 1, yDeal - d + 1); g.DrawLine(pen, x2, yDeal - d, x2, yDeal - d / 2); } else { // Sell var pen = new Pen(LayoutColors.ColorTradeShort, 2); if (wpType == WayPointType.Cancel) { pen = new Pen(LayoutColors.ColorChartGrid, 2); } g.DrawLine(pen, x, yDeal + 1, x1 + 1, yDeal + 1); g.DrawLine(pen, x1, yDeal, x2, yDeal + d); g.DrawLine(pen, x1 + d / 2 + 1, yDeal + d, x2, yDeal + d); g.DrawLine(pen, x2, yDeal + d, x2, yDeal + d / 2 + 1); } } if (posDirection == PosDirection.Closed) { // Close position int d = barPixels / 2 - 1; x = pntWay[point].X - d; int x1 = pntWay[point].X; int x2 = x + barPixels - 3; var pen = new Pen(LayoutColors.ColorTradeClose, 2); g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal + d / 2, x2, yDeal - d / 2); g.DrawLine(pen, x1, yDeal - d / 2, x2, yDeal + d / 2); } } // Coordinate axes g.DrawLine(_penAxes, xLeft, yTop - 4, xLeft, yBottom); // Vertical left line g.DrawLine(_penAxes, xLeft, yBottom, xRight, yBottom); // Border var penBorder = new Pen(Data.GetGradientColor(LayoutColors.ColorCaptionBack, -LayoutColors.DepthCaption), Border); g.DrawLine(penBorder, 1, _infoRowHeight, 1, pnl.ClientSize.Height); g.DrawLine(penBorder, pnl.ClientSize.Width - Border + 1, _infoRowHeight, pnl.ClientSize.Width - Border + 1, pnl.ClientSize.Height); g.DrawLine(penBorder, 0, pnl.ClientSize.Height - Border + 1, pnl.ClientSize.Width, pnl.ClientSize.Height - Border + 1); }
/// <summary> /// Paints panel pnlChart /// </summary> void PnlChart_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(LayoutColors.ColorChartBack); if (!Data.IsData || !Data.IsResult) { return; } Panel pnl = (Panel)sender; Pen penFore = new Pen(LayoutColors.ColorControlText); string FF = Data.FF; // Format modifier to print the floats int width = pnl.ClientSize.Width; Size size = new Size(width, infoRowHeight); StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Near; // Caption background PointF pntStart = new PointF(0, 0); SizeF szfCaption = new Size(width, infoRowHeight); RectangleF rectfCaption = new RectangleF(pntStart, szfCaption); Data.GradientPaint(g, rectfCaption, LayoutColors.ColorCaptionBack, LayoutColors.DepthCaption); // Caption Text StringFormat stringFormatCaption = new StringFormat(); stringFormatCaption.LineAlignment = StringAlignment.Center; stringFormatCaption.Trimming = StringTrimming.EllipsisCharacter; stringFormatCaption.FormatFlags = StringFormatFlags.NoWrap; stringFormatCaption.Alignment = StringAlignment.Center; string stringCaptionText = Language.T("Price Route Inside the Bar"); rectfCaption = new RectangleF(border, 0, pnl.ClientSize.Width - 2 * border, infoRowHeight); g.DrawString(stringCaptionText, fontInfo, brushCaptionText, rectfCaption, stringFormatCaption); // Paint the panel background RectangleF rectClient = new RectangleF(0, infoRowHeight, pnl.ClientSize.Width, pnl.Height - infoRowHeight); Data.GradientPaint(g, rectClient, LayoutColors.ColorChartBack, LayoutColors.DepthControl); // Paint bar info RectangleF rectBarInfo = new RectangleF(border, infoRowHeight + 1, pnl.ClientSize.Width - 2 * border, infoRowHeight); g.DrawString(barInfo, fontInfo, brushGridText, rectBarInfo, stringFormatCaption); // Searching the min and the max price and volume width = pnl.ClientSize.Width - 2 * border; double maxPrice = Data.High[bar]; double minPrice = Data.Low[bar]; int space = 8; int spcRight = szPrice.Width + 4; int XLeft = border + space; int XRight = width - spcRight; int chartWidth = XRight - XLeft; int YTop = 2 * infoRowHeight + 6; int YBottom = pnl.ClientSize.Height - 22; int barPixels = 28; int spcLeft = 3; int x = barPixels + spcLeft; int pointLeft = x + barPixels + 30; int pointX = pointLeft; int pointRight = XRight - 20; int points = Backtester.WayPoints(bar); int pointRadius = 3; // Grid int iCntLabels = (int)Math.Max((YBottom - YTop) / 30d, 1); double deltaPoint = (Data.InstrProperties.Digits == 5 || Data.InstrProperties.Digits == 3) ? Data.InstrProperties.Point * 100 : Data.InstrProperties.Point * 10; double delta = Math.Max(Math.Round((maxPrice - minPrice) / iCntLabels, Data.InstrProperties.Point < 0.001 ? 3 : 1), deltaPoint); minPrice = Math.Round(minPrice, Data.InstrProperties.Point < 0.001f ? 3 : 1) - Data.InstrProperties.Point * 10; minPrice -= delta; maxPrice += delta; iCntLabels = (int)Math.Ceiling((maxPrice - minPrice) / delta); maxPrice = minPrice + iCntLabels * delta; double scaleY = (YBottom - YTop) / (iCntLabels * delta); int yOpen = (int)(YBottom - (Data.Open[bar] - minPrice) * scaleY); int yHigh = (int)(YBottom - (Data.High[bar] - minPrice) * scaleY); int yLow = (int)(YBottom - (Data.Low[bar] - minPrice) * scaleY); int yClose = (int)(YBottom - (Data.Close[bar] - minPrice) * scaleY); // Find the price distance double priceDistance = 0; for (int point = 1; point < points; point++) { priceDistance += Math.Abs(Backtester.WayPoint(bar, point).Price - Backtester.WayPoint(bar, point - 1).Price); } double dPriceForAPixel = (pointRight - pointLeft) / priceDistance; // Points X int[] aiPointX = new int[points]; aiPointX[0] = pointLeft; for (int point = 1; point < points - 1; point++) { int iDistance = (int)(Math.Abs(Backtester.WayPoint(bar, point).Price - Backtester.WayPoint(bar, point - 1).Price) * dPriceForAPixel); aiPointX[point] = aiPointX[point - 1] + iDistance; } aiPointX[points - 1] = pointRight; for (int point = 1; point < points - 1; point++) { if (aiPointX[point] - aiPointX[point - 1] < barPixels + 1) { aiPointX[point] = aiPointX[point - 1] + barPixels + 1; } } for (int point = points - 2; point > 0; point--) { if (aiPointX[point + 1] - aiPointX[point] < barPixels + 1) { aiPointX[point] = aiPointX[point + 1] - barPixels - 1; } } // Find coordinates of the Way Points Point[] pntWay = new Point[points]; for (int point = 0; point < points; point++) { int pointY = (int)(YBottom - (Backtester.WayPoint(bar, point).Price - minPrice) * scaleY); pntWay[point] = new Point(aiPointX[point], pointY); } // Horizontal grid and Price labels for (double label = minPrice; label <= maxPrice + Data.InstrProperties.Point; label += delta) { int labelY = (int)(YBottom - (label - minPrice) * scaleY); g.DrawString(label.ToString(Data.FF), Font, brushGridText, XRight, labelY - Font.Height / 2 - 1); g.DrawLine(penGrid, border + space, labelY, XRight, labelY); } // Vertical Grid g.DrawLine(penGrid, x + barPixels / 2 - 1, YTop, x + barPixels / 2 - 1, YBottom + 2); for (int point = 0; point < points; point++) { Point pt1 = new Point(pntWay[point].X, YTop); Point pt2 = new Point(pntWay[point].X, YBottom + 2); Point pt3 = new Point(pntWay[point].X - 5, YBottom + 4); g.DrawLine(penGrid, pt1, pt2); g.DrawString((point + 1).ToString(), Font, brushGridText, pt3); } // Bar Number string barNumber = (bar + 1).ToString(); int stringX = x + barPixels / 2 - 1 - g.MeasureString(barNumber, Font).ToSize().Width / 2; if (Backtester.BackTestEval(bar) == "Ambiguous") { g.DrawString(barNumber, Font, brushRed, stringX, YBottom + 4); } else { g.DrawString(barNumber, Font, brushGridText, stringX, YBottom + 4); } // Draw the bar g.DrawLine(penBarBorder, x + barPixels / 2 - 1, yLow, x + barPixels / 2 - 1, yHigh); if (yClose < yOpen) // White bar { Rectangle rect = new Rectangle(x, yClose, barPixels - 2, yOpen - yClose); LinearGradientBrush lgBrush = new LinearGradientBrush(rect, colorBarWight1, colorBarWight2, 5f); g.FillRectangle(lgBrush, rect); g.DrawRectangle(penBarBorder, x, yClose, barPixels - 2, yOpen - yClose); } else if (yClose > yOpen) // Black bar { Rectangle rect = new Rectangle(x, yOpen, barPixels - 2, yClose - yOpen); LinearGradientBrush lgBrush = new LinearGradientBrush(rect, colorBarBlack1, colorBarBlack2, 5f); g.FillRectangle(lgBrush, rect); g.DrawRectangle(penBarBorder, x, yOpen, barPixels - 2, yClose - yOpen); } else // Cross { g.DrawLine(penBarBorder, x, yClose, x + barPixels - 2, yClose); } // Draw cancelled orders for (int orderIndex = 0; orderIndex < Backtester.Orders(bar); orderIndex++) { int ordNumber = Backtester.OrdNumb(bar, orderIndex); Order order = Backtester.OrdFromNumb(ordNumber); if (order.OrdStatus != OrderStatus.Cancelled) { continue; } if (order.OrdPrice > Data.High[bar] || order.OrdPrice < Data.Low[bar]) { continue; } int d = barPixels / 2 - 1; int x1 = x + d; int x2 = x + barPixels - 2; int yDeal = (int)(YBottom - (order.OrdPrice - minPrice) * scaleY); Pen pen = new Pen(LayoutColors.ColorChartGrid, 2); if (order.OrdDir == OrderDirection.Buy) { g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal, x2, yDeal - d); g.DrawLine(pen, x2 + 1, yDeal - d + 1, x1 + d / 2 + 1, yDeal - d + 1); g.DrawLine(pen, x2, yDeal - d, x2, yDeal - d / 2); } else if (order.OrdDir == OrderDirection.Sell) { g.DrawLine(pen, x, yDeal + 1, x1 + 1, yDeal + 1); g.DrawLine(pen, x1, yDeal, x2, yDeal + d); g.DrawLine(pen, x1 + d / 2 + 1, yDeal + d, x2, yDeal + d); g.DrawLine(pen, x2, yDeal + d, x2, yDeal + d / 2 + 1); } } // Draw the deals on the bar for (int pos = 0; pos < Backtester.Positions(bar); pos++) { if (Backtester.PosTransaction(bar, pos) == Transaction.Transfer) { continue; } int yDeal = (int)(YBottom - (Backtester.PosOrdPrice(bar, pos) - minPrice) * scaleY); if (Backtester.PosDir(bar, pos) == PosDirection.Long || Backtester.PosDir(bar, pos) == PosDirection.Short) { int d = barPixels / 2 - 1; int x1 = x + d; int x2 = x + barPixels - 2; if (Backtester.OrdFromNumb(Backtester.PosOrdNumb(bar, pos)).OrdDir == OrderDirection.Buy) { // Buy Pen pen = new Pen(LayoutColors.ColorTradeLong, 2); g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal, x2, yDeal - d); g.DrawLine(pen, x2 + 1, yDeal - d + 1, x1 + d / 2 + 1, yDeal - d + 1); g.DrawLine(pen, x2, yDeal - d, x2, yDeal - d / 2); } else { // Sell Pen pen = new Pen(LayoutColors.ColorTradeShort, 2); g.DrawLine(pen, x, yDeal + 1, x1 + 1, yDeal + 1); g.DrawLine(pen, x1, yDeal, x2, yDeal + d); g.DrawLine(pen, x1 + d / 2 + 1, yDeal + d, x2, yDeal + d); g.DrawLine(pen, x2, yDeal + d, x2, yDeal + d / 2 + 1); } } else if (Backtester.PosDir(bar, pos) == PosDirection.Closed) { // Close position int d = barPixels / 2 - 1; int x1 = x + d; int x2 = x + barPixels - 3; Pen pen = new Pen(LayoutColors.ColorTradeClose, 2); g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal + d / 2, x2, yDeal - d / 2); g.DrawLine(pen, x1, yDeal - d / 2, x2, yDeal + d / 2); } } // Draw position lots for (int point = 0; point < points; point++) { int posNumber = Backtester.WayPoint(bar, point).PosNumb; if (posNumber == -1) { continue; } double posLots = Backtester.PosFromNumb(posNumber).PosLots; PosDirection posDirection = Backtester.PosFromNumb(posNumber).PosDir; WayPointType wpType = Backtester.WayPoint(bar, point).WPType; int posHight = (int)(Math.Max(posLots * 2, 2)); int posY = YBottom - posHight; int d = (barPixels - 1) / 2; x = pntWay[point].X - d; if (posDirection == PosDirection.Long) { // Long Rectangle rect = new Rectangle(x - 1, posY, barPixels + 1, posHight); LinearGradientBrush lgBrush = new LinearGradientBrush(rect, colorLongTrade1, colorLongTrade2, 0f); rect = new Rectangle(x, posY, barPixels - 1, posHight); g.FillRectangle(lgBrush, rect); } else if (posDirection == PosDirection.Short) { // Short Rectangle rect = new Rectangle(x - 1, posY, barPixels + 1, posHight); LinearGradientBrush lgBrush = new LinearGradientBrush(rect, colorShortTrade1, colorShortTrade2, 0f); rect = new Rectangle(x, posY, barPixels - 1, posHight); g.FillRectangle(lgBrush, rect); } else if (posDirection == PosDirection.Closed && wpType == WayPointType.Exit) { // Closed Rectangle rect = new Rectangle(x - 1, YBottom - 2, barPixels + 1, 2); LinearGradientBrush lgBrush = new LinearGradientBrush(rect, colorClosedTrade1, colorClosedTrade2, 0f); rect = new Rectangle(x, YBottom - 2, barPixels - 1, 2); g.FillRectangle(lgBrush, rect); } } // Draw the Beziers for (int point = 1; point < points; point++) { Point ptKnot1 = pntWay[point - 1]; Point ptKnot2 = pntWay[point]; int ctrlX1 = (ptKnot1.X + ptKnot2.X) / 2; int ctrlX2 = (ptKnot1.X + ptKnot2.X) / 2; int ctrlY1 = ptKnot1.Y; int ctrlY2 = ptKnot2.Y; if (point > 1) { if (pntWay[point - 2].Y > pntWay[point - 1].Y && pntWay[point - 1].Y > pntWay[point].Y || pntWay[point - 2].Y < pntWay[point - 1].Y && pntWay[point - 1].Y < pntWay[point].Y) { ctrlY1 = (pntWay[point - 1].Y + pntWay[point].Y) / 2; } } if (point < points - 1) { if (pntWay[point - 1].Y > pntWay[point].Y && pntWay[point].Y > pntWay[point + 1].Y || pntWay[point - 1].Y < pntWay[point].Y && pntWay[point].Y < pntWay[point + 1].Y) { ctrlY2 = (pntWay[point - 1].Y + pntWay[point].Y) / 2; } } if (point == 1) { ctrlX1 = ptKnot1.X; ctrlY1 = ptKnot1.Y; } if (point == points - 1) { ctrlX2 = ptKnot2.X; ctrlY2 = ptKnot2.Y; } Point ptControl1 = new Point(ctrlX1, ctrlY1); Point ptControl2 = new Point(ctrlX2, ctrlY2); g.DrawBezier(penCross, ptKnot1, ptControl1, ptControl2, ptKnot2); } // Draw the WayPoints Brush brushWeyPnt = new SolidBrush(LayoutColors.ColorChartBack); for (int point = 0; point < points; point++) { g.FillEllipse(brushWeyPnt, pntWay[point].X - pointRadius, pntWay[point].Y - pointRadius, 2 * pointRadius, 2 * pointRadius); g.DrawEllipse(penCross, pntWay[point].X - pointRadius, pntWay[point].Y - pointRadius, 2 * pointRadius, 2 * pointRadius); } // Draw the deals on the route for (int point = 0; point < points; point++) { int posNumber = Backtester.WayPoint(bar, point).PosNumb; int ordNumber = Backtester.WayPoint(bar, point).OrdNumb; if (posNumber < 0 || ordNumber < 0) { continue; } PosDirection posDirection = Backtester.PosFromNumb(posNumber).PosDir; OrderDirection ordDirection = Backtester.OrdFromNumb(ordNumber).OrdDir; WayPointType wpType = Backtester.WayPoint(bar, point).WPType; if (Backtester.PosFromNumb(posNumber).Transaction == Transaction.Transfer || wpType == WayPointType.Cancel || wpType == WayPointType.None || wpType == WayPointType.Open || wpType == WayPointType.High || wpType == WayPointType.Low || wpType == WayPointType.Close) { continue; } int yDeal = pntWay[point].Y; if (posDirection == PosDirection.Long || posDirection == PosDirection.Short) { int d = barPixels / 2 - 1; x = pntWay[point].X - d; int x1 = pntWay[point].X; int x2 = x + barPixels - 2; if (ordDirection == OrderDirection.Buy) { // Buy Pen pen = new Pen(LayoutColors.ColorTradeLong, 2); g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal, x2, yDeal - d); g.DrawLine(pen, x2 + 1, yDeal - d + 1, x1 + d / 2 + 1, yDeal - d + 1); g.DrawLine(pen, x2, yDeal - d, x2, yDeal - d / 2); } else { // Sell Pen pen = new Pen(LayoutColors.ColorTradeShort, 2); g.DrawLine(pen, x, yDeal + 1, x1 + 1, yDeal + 1); g.DrawLine(pen, x1, yDeal, x2, yDeal + d); g.DrawLine(pen, x1 + d / 2 + 1, yDeal + d, x2, yDeal + d); g.DrawLine(pen, x2, yDeal + d, x2, yDeal + d / 2 + 1); } } else if (posDirection == PosDirection.Closed) { // Close position int d = barPixels / 2 - 1; x = pntWay[point].X - d; int x1 = pntWay[point].X; int x2 = x + barPixels - 3; Pen pen = new Pen(LayoutColors.ColorTradeClose, 2); g.DrawLine(pen, x, yDeal, x1, yDeal); g.DrawLine(pen, x1, yDeal + d / 2, x2, yDeal - d / 2); g.DrawLine(pen, x1, yDeal - d / 2, x2, yDeal + d / 2); } } // Coordinate axes g.DrawLine(penAxes, XLeft, YTop - 4, XLeft, YBottom); // Vertical left line g.DrawLine(penAxes, XLeft, YBottom, XRight, YBottom); // Border Pen penBorder = new Pen(Data.GetGradientColor(LayoutColors.ColorCaptionBack, -LayoutColors.DepthCaption), border); g.DrawLine(penBorder, 1, infoRowHeight, 1, pnl.ClientSize.Height); g.DrawLine(penBorder, pnl.ClientSize.Width - border + 1, infoRowHeight, pnl.ClientSize.Width - border + 1, pnl.ClientSize.Height); g.DrawLine(penBorder, 0, pnl.ClientSize.Height - border + 1, pnl.ClientSize.Width, pnl.ClientSize.Height - border + 1); }
/// <summary> /// Paints panel pnlInfo /// </summary> private void PnlInfoPaint(object sender, PaintEventArgs e) { // ---------------------------------------------------------------------+ // | Way points description | // |--------------------------------------------------------------------+ // | Number | Description | Price | Direction | Lots | Position | Order | // |--------------------------------------------------------------------+ //xp0 xp1 xp2 xp3 xp4 xp5 xp6 xp7 Graphics g = e.Graphics; g.Clear(LayoutColors.ColorControlBack); if (!Data.IsData || !Data.IsResult) { return; } var pnl = (Panel)sender; string ff = Data.FF; // Format modifier to print the floats var size = new Size(_aiX[_columns] - _aiX[0], _infoRowHeight); var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Near }; // Caption background var pntStart = new PointF(0, 0); SizeF szfCaption = new Size(pnl.ClientSize.Width, 2 * _infoRowHeight); var rectfCaption = new RectangleF(pntStart, szfCaption); Data.GradientPaint(g, rectfCaption, LayoutColors.ColorCaptionBack, LayoutColors.DepthCaption); // Caption Text var stringFormat = new StringFormat { LineAlignment = StringAlignment.Center, Trimming = StringTrimming.EllipsisCharacter, FormatFlags = StringFormatFlags.NoWrap, Alignment = StringAlignment.Near }; string stringCaptionText = Language.T("Way Points Description"); float captionWidth = Math.Min(_pnlInfo.ClientSize.Width, _aiX[_columns] - _aiX[0]); float fCaptionTextWidth = g.MeasureString(stringCaptionText, _fontInfo).Width; float fCaptionTextX = Math.Max((captionWidth - fCaptionTextWidth) / 2f, 0); var pfCaptionText = new PointF(fCaptionTextX, 0); var sfCaptionText = new SizeF(captionWidth - fCaptionTextX, _infoRowHeight); rectfCaption = new RectangleF(pfCaptionText, sfCaptionText); // First caption raw g.DrawString(stringCaptionText, _fontInfo, _brushCaptionText, rectfCaption, stringFormat); // Second caption raw for (int i = 0; i < _columns; i++) { g.DrawString(_asTitles[i], _fontInfo, _brushCaptionText, (_aiX[i] + _aiX[i + 1]) / 2f, _infoRowHeight, sf); } Brush brush = new SolidBrush(LayoutColors.ColorControlText); for (int pnt = 0; pnt < Backtester.WayPoints(_barCurrent); pnt++) { int y = (pnt + 2) * _infoRowHeight; var point = new Point(_aiX[0], y); // Even row if (Math.Abs(pnt % 2f - 0) > 0.00001) { g.FillRectangle(_brushEvenRow, new Rectangle(point, size)); } int positionNumber = Backtester.WayPoint(_barCurrent, pnt).PosNumb; WayPointType wpType = Backtester.WayPoint(_barCurrent, pnt).WPType; PosDirection posDirection = Backtester.PosFromNumb(positionNumber).PosDir; double posLots = Backtester.PosFromNumb(positionNumber).PosLots; int ordNumber = Backtester.WayPoint(_barCurrent, pnt).OrdNumb; g.DrawString((pnt + 1).ToString(CultureInfo.InvariantCulture), _fontInfo, brush, (_aiX[0] + _aiX[1]) / 2f, y, sf); g.DrawString(Language.T(WayPoint.WPTypeToString(wpType)), _fontInfo, brush, _aiX[1] + 2, y); g.DrawString(Backtester.WayPoint(_barCurrent, pnt).Price.ToString(ff), _fontInfo, brush, (_aiX[3] + _aiX[2]) / 2f, y, sf); if (positionNumber > -1) { g.DrawString(Language.T(posDirection.ToString()), _fontInfo, brush, (_aiX[4] + _aiX[3]) / 2f, y, sf); g.DrawString(posLots.ToString(CultureInfo.InvariantCulture), _fontInfo, brush, (_aiX[5] + _aiX[4]) / 2f, y, sf); g.DrawString((positionNumber + 1).ToString(CultureInfo.InvariantCulture), _fontInfo, brush, (_aiX[6] + _aiX[5]) / 2f, y, sf); } if (ordNumber > -1) { g.DrawString((ordNumber + 1).ToString(CultureInfo.InvariantCulture), _fontInfo, brush, (_aiX[7] + _aiX[6]) / 2f, y, sf); } } // Vertical lines var penLine = new Pen(LayoutColors.ColorJournalLines); for (int i = 1; i < _columns; i++) { g.DrawLine(penLine, _aiX[i], 2 * _infoRowHeight, _aiX[i], ClientSize.Height - Border); } // Border var penBorder = new Pen(Data.GetGradientColor(LayoutColors.ColorCaptionBack, -LayoutColors.DepthCaption), Border); g.DrawLine(penBorder, 1, 2 * _infoRowHeight, 1, pnl.ClientSize.Height); g.DrawLine(penBorder, pnl.ClientSize.Width - Border + 1, 2 * _infoRowHeight, pnl.ClientSize.Width - Border + 1, pnl.ClientSize.Height); g.DrawLine(penBorder, 0, pnl.ClientSize.Height - Border + 1, pnl.ClientSize.Width, pnl.ClientSize.Height - Border + 1); }
/// <summary> /// Exports the positions info in currency /// </summary> public void ExportPositionsInMoney(bool showTransfers) { string stage = String.Empty; if (Data.IsProgramBeta) { stage = " " + Language.T("Beta"); } else if (Data.IsProgramRC) { stage = " " + "RC"; } string ff = Data.FF; // Format modifier to print float numbers var sb = new StringBuilder(); sb.Append("Forex Strategy Builder v" + Data.ProgramVersion + stage + Environment.NewLine); sb.Append("Strategy name: " + Data.Strategy.StrategyName + Environment.NewLine); sb.Append("Exported on " + DateTime.Now + Environment.NewLine); sb.Append(Data.Symbol + " " + Data.PeriodString + "; Values in " + Configs.AccountCurrency + Environment.NewLine); sb.Append("Pos Numb\t"); sb.Append("Bar Numb\t"); sb.Append("Bar Opening Time\t"); sb.Append("Direction\t"); sb.Append("Amount\t"); sb.Append("Transaction\t"); sb.Append("Order Price\t"); sb.Append("Price\t"); sb.Append("Profit Loss\t"); sb.Append("Floating P/L\t"); sb.Append("Balance\t"); sb.Append("Equity\t"); sb.Append(Environment.NewLine); for (int pos = 0; pos < Backtester.PositionsTotal; pos++) { Position position = Backtester.PosFromNumb(pos); int bar = Backtester.PosCoordinates[pos].Bar; if (!showTransfers && position.Transaction == Transaction.Transfer) { continue; } sb.Append((position.PosNumb + 1) + "\t"); sb.Append((bar + 1) + "\t"); sb.Append(Data.Time[bar] + "\t"); sb.Append(position.PosDir + "\t"); sb.Append((position.PosDir == PosDirection.Long ? "" : "-") + (position.PosLots * Data.InstrProperties.LotSize) + "\t"); sb.Append(position.Transaction + "\t"); sb.Append(position.FormOrdPrice.ToString(ff) + "\t"); sb.Append(position.PosPrice.ToString(ff) + "\t"); sb.Append(position.MoneyProfitLoss.ToString("F2") + "\t"); sb.Append(position.MoneyFloatingPL.ToString("F2") + "\t"); sb.Append(position.MoneyBalance.ToString("F2") + "\t"); sb.Append(position.MoneyEquity.ToString("F2") + "\t"); sb.Append(Environment.NewLine); } string fileName = Data.Strategy.StrategyName + "-" + Data.Symbol + "-" + Data.Period; SaveData(fileName, sb); }
/// <summary> /// Exports the positions info /// </summary> public void ExportPositions(bool showTransfers) { string stage = String.Empty; if (Data.IsProgramBeta) { stage = " " + Language.T("Beta"); } else if (Data.IsProgramRC) { stage = " " + "RC"; } string ff = Data.FF; // Format modifier to print float numbers var sb = new StringBuilder(); sb.Append("Forex Strategy Builder v" + Data.ProgramVersion + stage + Environment.NewLine); sb.Append("Strategy name: " + Data.Strategy.StrategyName + Environment.NewLine); sb.Append("Exported on " + DateTime.Now + Environment.NewLine); sb.Append(Data.Symbol + " " + Data.PeriodString + "; Values in pips" + Environment.NewLine); sb.Append("Pos Numb\t"); sb.Append("Bar Numb\t"); sb.Append("Bar Opening Time\t"); sb.Append("Direction\t"); sb.Append("Lots\t"); sb.Append("Transaction\t"); sb.Append("Order Price\t"); sb.Append("Average Price\t"); sb.Append("Profit Loss\t"); sb.Append("Floating P/L\t"); sb.Append("Balance\t"); sb.Append("Equity\t"); sb.Append(Environment.NewLine); for (int iPos = 0; iPos < Backtester.PositionsTotal; iPos++) { Position position = Backtester.PosFromNumb(iPos); int bar = Backtester.PosCoordinates[iPos].Bar; if (!showTransfers && position.Transaction == Transaction.Transfer) { continue; } sb.Append((position.PosNumb + 1) + "\t"); sb.Append((bar + 1) + "\t"); sb.Append(Data.Time[bar] + "\t"); sb.Append(position.PosDir + "\t"); sb.Append(position.PosLots + "\t"); sb.Append(position.Transaction + "\t"); sb.Append(position.FormOrdPrice.ToString(ff) + "\t"); sb.Append(position.PosPrice.ToString(ff) + "\t"); sb.Append(Math.Round(position.ProfitLoss) + "\t"); sb.Append(Math.Round(position.FloatingPL) + "\t"); sb.Append(Math.Round(position.Balance) + "\t"); sb.Append(Math.Round(position.Equity) + "\t"); sb.Append(Environment.NewLine); } string fileName = Data.Strategy.StrategyName + "-" + Data.Symbol + "-" + Data.Period; SaveData(fileName, sb); }
/// <summary> /// Calculates the account statistics. /// </summary> public static void CalculateAccountStats() { maxBalance = 0; minBalance = 0; maxEquity = 0; minEquity = 0; maxEquityDrawdown = 0; maxDrawdown = 0; maxMoneyBalance = Configs.InitialAccount; minMoneyBalance = Configs.InitialAccount; maxMoneyEquity = Configs.InitialAccount; minMoneyEquity = Configs.InitialAccount; maxMoneyEquityDrawdown = 0; maxMoneyDrawdown = 0; barsInPosition = 0; grossProfit = 0; grossLoss = 0; grossMoneyProfit = 0; grossMoneyLoss = 0; chargedSpread = 0; chargedRollOver = 0; chargedCommission = 0; chargedSlippage = 0; moneyChargedSpread = 0; moneyChargedRollOver = 0; moneyChargedCommission = 0; moneyChargedSlippage = 0; ambiguousBars = 0; unknownBars = 0; balanceDrawdown = new int[Bars]; equityDrawdown = new int[Bars]; maxBalanceDate = Time[0]; minBalanceDate = Time[0]; maxMoneyBalanceDate = Time[0]; minMoneyBalanceDate = Time[0]; maxDrawdownDate = Time[0]; maxMoneyDrawdownDate = Time[0]; equityPercentDrawdown = 100; maxMoneyDrawdownPercent = 0; moneyEquityPercentDrawdown = 0; winLossRatio = 0; winningTrades = 0; losingTrades = 0; totalTrades = 0; testedDays = 0; for (int bar = FirstBar; bar < Bars; bar++) { double accountExchangeRate = AccountExchangeRate(Close[bar]); double pipsToMoney = InstrProperties.Point * InstrProperties.LotSize / accountExchangeRate; // Balance double balance = session[bar].Summary.Balance; if (balance > maxBalance) { maxBalance = balance; maxBalanceDate = Time[bar]; } if (balance < minBalance) { minBalance = balance; minBalanceDate = Time[bar]; } // Money Balance double moneyBalance = session[bar].Summary.MoneyBalance; if (moneyBalance > maxMoneyBalance) { maxMoneyBalance = moneyBalance; maxMoneyBalanceDate = Time[bar]; } if (moneyBalance < minMoneyBalance) { minMoneyBalance = moneyBalance; minMoneyBalanceDate = Time[bar]; } // Equity double equity = session[bar].Summary.Equity; if (equity > maxEquity) { maxEquity = equity; } if (equity < minEquity) { minEquity = equity; } // Money Equity double moneyEquity = session[bar].Summary.MoneyEquity; if (moneyEquity > maxMoneyEquity) { maxMoneyEquity = moneyEquity; } if (moneyEquity < minMoneyEquity) { minMoneyEquity = moneyEquity; } // Maximum Drawdown if (maxBalance - balance > maxDrawdown) { maxDrawdown = maxBalance - balance; maxDrawdownDate = Time[bar]; } // Maximum Equity Drawdown if (maxEquity - equity > maxEquityDrawdown) { maxEquityDrawdown = maxEquity - equity; // In percents if (maxEquity > 0) { equityPercentDrawdown = 100 * (maxEquityDrawdown / maxEquity); } } // Maximum Money Drawdown if (maxMoneyBalance - MoneyBalance(bar) > maxMoneyDrawdown) { maxMoneyDrawdown = maxMoneyBalance - MoneyBalance(bar); maxMoneyDrawdownPercent = 100 * (maxMoneyDrawdown / maxMoneyBalance); maxMoneyDrawdownDate = Time[bar]; } // Maximum Money Equity Drawdown if (maxMoneyEquity - MoneyEquity(bar) > maxMoneyEquityDrawdown) { maxMoneyEquityDrawdown = maxMoneyEquity - MoneyEquity(bar); // Maximum Money Equity Drawdown in percents if (100 * maxMoneyEquityDrawdown / maxMoneyEquity > moneyEquityPercentDrawdown) { moneyEquityPercentDrawdown = 100 * (maxMoneyEquityDrawdown / maxMoneyEquity); } } // Drawdown balanceDrawdown[bar] = (int)Math.Round((maxBalance - balance)); equityDrawdown[bar] = (int)Math.Round((maxEquity - equity)); // Bars in position if (session[bar].Positions > 0) { barsInPosition++; } // Bar interpolation evaluation if (session[bar].BacktestEval == BacktestEval.Ambiguous) { ambiguousBars++; } else if (session[bar].BacktestEval == BacktestEval.Unknown) { unknownBars++; } // Margin Call bar if (!Configs.TradeUntilMarginCall && marginCallBar == 0 && session[bar].Summary.FreeMargin < 0) { marginCallBar = bar; } } for (int pos = 0; pos < PositionsTotal; pos++) { // Charged fees Position position = Backtester.PosFromNumb(pos); chargedSpread += position.Spread; chargedRollOver += position.Rollover; chargedCommission += position.Commission; chargedSlippage += position.Slippage; moneyChargedSpread += position.MoneySpread; moneyChargedRollOver += position.MoneyRollover; moneyChargedCommission += position.MoneyCommission; moneyChargedSlippage += position.MoneySlippage; // Winning losing trades. if (position.Transaction == Transaction.Close || position.Transaction == Transaction.Reduce || position.Transaction == Transaction.Reverse) { if (position.ProfitLoss > 0) { grossProfit += position.ProfitLoss; grossMoneyProfit += position.MoneyProfitLoss; winningTrades++; } else if (position.ProfitLoss < 0) { grossLoss += position.ProfitLoss; grossMoneyLoss += position.MoneyProfitLoss; losingTrades++; } totalTrades++; } } winLossRatio = (double)winningTrades / Math.Max((losingTrades + winningTrades), 1.0); executedOrders = 0; tradedLots = 0; for (int ord = 0; ord < totalOrders; ord++) { if (OrdFromNumb(ord).OrdStatus == OrderStatus.Executed) { executedOrders++; tradedLots += OrdFromNumb(ord).OrdLots; } } testedDays = (Time[Bars - 1] - Time[FirstBar]).Days; if (testedDays < 1) { testedDays = 1; } if (Configs.AccountInMoney) { GenerateAccountStatsInMoney(); } else { GenerateAccountStats(); } if (Configs.AdditionalStatistics) { CalculateAdditionalStats(); if (Configs.AccountInMoney) { SetAdditioanlMoneyStats(); } else { SetAdditioanlStats(); } } return; }
/// <summary> /// Calculates the values of the stats parameters. /// </summary> static void CalculateAdditionalStats() { longBalance = new double[Bars]; shortBalance = new double[Bars]; longMoneyBalance = new double[Bars]; shortMoneyBalance = new double[Bars]; maxLongMoneyBalance = Configs.InitialAccount; minLongMoneyBalance = Configs.InitialAccount; maxShortMoneyBalance = Configs.InitialAccount; minShortMoneyBalance = Configs.InitialAccount; maxLongBalance = 0; minLongBalance = 0; maxShortBalance = 0; minShortBalance = 0; maxLongBalanceDate = Time[0]; minLongBalanceDate = Time[0]; maxShortBalanceDate = Time[0]; minShortBalanceDate = Time[0]; maxLongMoneyBalanceDate = Time[0]; minLongMoneyBalanceDate = Time[0]; maxShortMoneyBalanceDate = Time[0]; minShortMoneyBalanceDate = Time[0]; maxLongDrawdownDate = Time[0]; maxShortDrawdownDate = Time[0]; maxLongMoneyDrawdownDate = Time[0]; maxShortMoneyDrawdownDate = Time[0]; grossLongProfit = 0; grossLongLoss = 0; grossShortProfit = 0; grossShortLoss = 0; grossLongMoneyProfit = 0; grossLongMoneyLoss = 0; grossShortMoneyProfit = 0; grossShortMoneyLoss = 0; maxLongDrawdown = 0; maxShortDrawdown = 0; maxLongMoneyDrawdown = 0; maxShortMoneyDrawdown = 0; maxShortDrawdown = 0; maxLongMoneyDrawdown = 0; maxShortMoneyDrawdown = 0; maxLongMoneyDrawdownPercent = 0; maxShortMoneyDrawdownPercent = 0; barsWithPos = 0; barsWithLongPos = 0; barsWithShortPos = 0; winningLongTrades = 0; winningShortTrades = 0; losingLongTrades = 0; losingShortTrades = 0; totalLongTrades = 0; totalShortTrades = 0; maxLongWin = 0; maxShortWin = 0; maxLongMoneyWin = 0; maxShortMoneyWin = 0; maxLongLoss = 0; maxShortLoss = 0; maxLongMoneyLoss = 0; maxShortMoneyLoss = 0; for (int bar = 0; bar < FirstBar; bar++) { longBalance[bar] = 0; shortBalance[bar] = 0; longMoneyBalance[bar] = Configs.InitialAccount; shortMoneyBalance[bar] = Configs.InitialAccount; } for (int bar = Data.FirstBar; bar < Bars; bar++) { double accountExchangeRate = AccountExchangeRate(Close[bar]); double pipsToMoney = InstrProperties.Point * InstrProperties.LotSize / accountExchangeRate; longBalance[bar] = longBalance[bar - 1]; shortBalance[bar] = shortBalance[bar - 1]; longMoneyBalance[bar] = longMoneyBalance[bar - 1]; shortMoneyBalance[bar] = shortMoneyBalance[bar - 1]; bool isLong = false; bool isShort = false; for (int pos = 0; pos < Positions(bar); pos++) { if (PosDir(bar, pos) == PosDirection.Long) { isLong = true; } if (PosDir(bar, pos) == PosDirection.Short) { isShort = true; } double positionProfitLoss = PosProfitLoss(bar, pos); double positionMoneyProfitLoss = PosMoneyProfitLoss(bar, pos); if (PosTransaction(bar, pos) == Transaction.Close || PosTransaction(bar, pos) == Transaction.Reduce || PosTransaction(bar, pos) == Transaction.Reverse) { if (OrdFromNumb(PosOrdNumb(bar, pos)).OrdDir == OrderDirection.Sell) { // Closing long position longBalance[bar] += positionProfitLoss; longMoneyBalance[bar] += positionMoneyProfitLoss; if (positionProfitLoss > 0) { grossLongProfit += positionProfitLoss; grossLongMoneyProfit += positionMoneyProfitLoss; winningLongTrades++; if (positionProfitLoss > maxLongWin) { maxLongWin = positionProfitLoss; } if (positionMoneyProfitLoss > maxLongMoneyWin) { maxLongMoneyWin = positionMoneyProfitLoss; } } if (positionProfitLoss < 0) { grossLongLoss += positionProfitLoss; grossLongMoneyLoss += positionMoneyProfitLoss; losingLongTrades++; if (positionProfitLoss < maxLongLoss) { maxLongLoss = positionProfitLoss; } if (positionMoneyProfitLoss < maxLongMoneyLoss) { maxLongMoneyLoss = positionMoneyProfitLoss; } } totalLongTrades++; } if (OrdFromNumb(PosOrdNumb(bar, pos)).OrdDir == OrderDirection.Buy) { // Closing short position shortBalance[bar] += positionProfitLoss; shortMoneyBalance[bar] += positionMoneyProfitLoss; if (positionProfitLoss > 0) { grossShortProfit += positionProfitLoss; grossShortMoneyProfit += positionMoneyProfitLoss; winningShortTrades++; if (positionProfitLoss > maxShortWin) { maxShortWin = positionProfitLoss; } if (positionMoneyProfitLoss > maxShortMoneyWin) { maxShortMoneyWin = positionMoneyProfitLoss; } } if (positionProfitLoss < 0) { grossShortLoss += positionProfitLoss; grossShortMoneyLoss += positionMoneyProfitLoss; losingShortTrades++; if (positionProfitLoss < maxShortLoss) { maxShortLoss = positionProfitLoss; } if (positionMoneyProfitLoss < maxShortMoneyLoss) { maxShortMoneyLoss = positionMoneyProfitLoss; } } totalShortTrades++; } } } barsWithPos += (isLong || isShort) ? 1 : 0; barsWithLongPos += isLong ? 1 : 0; barsWithShortPos += isShort ? 1 : 0; if (maxLongBalance < longBalance[bar]) { maxLongBalance = longBalance[bar]; maxLongBalanceDate = Time[bar]; } if (minLongBalance > longBalance[bar]) { minLongBalance = longBalance[bar]; minLongBalanceDate = Time[bar]; } if (maxShortBalance < shortBalance[bar]) { maxShortBalance = shortBalance[bar]; maxShortBalanceDate = Time[bar]; } if (minShortBalance > shortBalance[bar]) { minShortBalance = shortBalance[bar]; minShortBalanceDate = Time[bar]; } if (maxLongMoneyBalance < longMoneyBalance[bar]) { maxLongMoneyBalance = longMoneyBalance[bar]; maxLongMoneyBalanceDate = Time[bar]; } if (minLongMoneyBalance > longMoneyBalance[bar]) { minLongMoneyBalance = longMoneyBalance[bar]; minLongMoneyBalanceDate = Time[bar]; } if (maxShortMoneyBalance < shortMoneyBalance[bar]) { maxShortMoneyBalance = shortMoneyBalance[bar]; maxShortMoneyBalanceDate = Time[bar]; } if (minShortMoneyBalance > shortMoneyBalance[bar]) { minShortMoneyBalance = shortMoneyBalance[bar]; minShortMoneyBalanceDate = Time[bar]; } // Maximum Drawdown if (maxLongBalance - longBalance[bar] > maxLongDrawdown) { maxLongDrawdown = maxLongBalance - longBalance[bar]; maxLongDrawdownDate = Time[bar]; } if (maxLongMoneyBalance - longMoneyBalance[bar] > maxLongMoneyDrawdown) { maxLongMoneyDrawdown = maxLongMoneyBalance - longMoneyBalance[bar]; maxLongMoneyDrawdownPercent = 100 * maxLongMoneyDrawdown / maxLongMoneyBalance; maxLongMoneyDrawdownDate = Time[bar]; } if (maxShortBalance - shortBalance[bar] > maxShortDrawdown) { maxShortDrawdown = maxShortBalance - shortBalance[bar]; maxShortDrawdownDate = Time[bar]; } if (maxShortMoneyBalance - shortMoneyBalance[bar] > maxShortMoneyDrawdown) { maxShortMoneyDrawdown = maxShortMoneyBalance - shortMoneyBalance[bar]; maxShortMoneyDrawdownPercent = 100 * maxShortMoneyDrawdown / maxShortMoneyBalance; maxShortMoneyDrawdownDate = Time[bar]; } } // Holding period returns AHPR = 0; AHPRLong = 0; AHPRShort = 0; double[] HPR = new double[totalTrades]; double[] HPRLong = new double[totalLongTrades]; double[] HPRShort = new double[totalShortTrades]; double totalHPR = 0; double totalHPRLong = 0; double totalHPRShort = 0; double startBalance = Configs.InitialAccount; double startBalanceLong = Configs.InitialAccount; double startBalanceShort = Configs.InitialAccount; int count = 0; int countL = 0; int countS = 0; for (int pos = 0; pos < PositionsTotal; pos++) { // Charged fees Position position = Backtester.PosFromNumb(pos); // Winning losing trades. if (position.Transaction == Transaction.Close || position.Transaction == Transaction.Reduce || position.Transaction == Transaction.Reverse) { if (OrdFromNumb(position.FormOrdNumb).OrdDir == OrderDirection.Sell) { // Closing long position HPRLong[countL] = 1 + position.MoneyProfitLoss / startBalanceLong; totalHPRLong += HPRLong[countL]; countL++; startBalanceLong += position.MoneyProfitLoss; } if (OrdFromNumb(position.FormOrdNumb).OrdDir == OrderDirection.Buy) { // Closing short position HPRShort[countS] = 1 + position.MoneyProfitLoss / startBalanceShort; totalHPRShort += HPRShort[countS]; countS++; startBalanceShort += position.MoneyProfitLoss; } HPR[count] = 1 + position.MoneyProfitLoss / startBalance; totalHPR += HPR[count]; count++; startBalance += position.MoneyProfitLoss; } } double averageHPR = totalHPR / totalTrades; double averageHPRLong = totalHPRLong / totalLongTrades; double averageHPRShort = totalHPRShort / totalShortTrades; AHPR = 100 * (averageHPR - 1); AHPRLong = 100 * (averageHPRLong - 1); AHPRShort = 100 * (averageHPRShort - 1); GHPR = 100 * (Math.Pow((NetMoneyBalance / Configs.InitialAccount), (1f / totalTrades)) - 1); GHPRLong = 100 * (Math.Pow((NetLongMoneyBalance / Configs.InitialAccount), (1f / totalLongTrades)) - 1); GHPRShort = 100 * (Math.Pow((NetShortMoneyBalance / Configs.InitialAccount), (1f / totalShortTrades)) - 1); // Sharpe Ratio sharpeRatio = 0; sharpeRatioLong = 0; sharpeRatioShort = 0; double sumPow = 0; double sumPowLong = 0; double sumPowShort = 0; for (int i = 0; i < totalTrades; i++) { sumPow += Math.Pow((HPR[i] - averageHPR), 2); } for (int i = 0; i < totalLongTrades; i++) { sumPowLong += Math.Pow((HPRLong[i] - averageHPRLong), 2); } for (int i = 0; i < totalShortTrades; i++) { sumPowShort += Math.Pow((HPRShort[i] - averageHPRShort), 2); } double stDev = Math.Sqrt(sumPow / (totalTrades - 1)); double stDevLong = Math.Sqrt(sumPowLong / (totalLongTrades - 1)); double stDevShort = Math.Sqrt(sumPowShort / (totalShortTrades - 1)); sharpeRatio = (averageHPR - 1) / stDev; sharpeRatioLong = (averageHPRLong - 1) / stDevLong; sharpeRatioShort = (averageHPRShort - 1) / stDevShort; }