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