public void BuildSeries(ChartControl chart) { var commentText = GetStatText(); if (string.IsNullOrEmpty(commentText)) { return; } var candles = chart.StockSeries.Data.Candles; // добавить комментарий с текстом var series = chart.Owner.seriesComment; var indiTitle = MakeCommentTitle(); var comment = series.data.FirstOrDefault(c => c.Magic == CommentMagic && c.Text.StartsWith(indiTitle)); if (comment != null) { comment.Text = commentText; return; } // таки создать новый комментарий comment = new ChartComment { ArrowAngle = nextAngle, ArrowLength = 50, Magic = CommentMagic, Text = commentText, Owner = series, PivotIndex = candles.Count - 1, PivotPrice = candles[candles.Count - 1].open, }; nextAngle += 45; series.data.Add(comment); }
public ChartCommentEditDialog(ChartComment obj, CandleChartControl chart) : this() { comment = obj;//new ChartComment(); ChartComment.Copy(comment, obj); // отобразить содержание полей tbText.Text = comment.Text; btnColorPicker.BackColor = comment.ColorText; tbText.ForeColor = comment.ColorText; tbText.BackColor = chart.chart.BackColor; cbShowArrow.Checked = !comment.HideArrow; cbShowFrame.Checked = comment.DrawFrame; btnColorLine.ForeColor = comment.Color; btnColorFill.ForeColor = comment.ColorFill; tbMagic.Text = comment.Magic.ToString(); transparencyControl.Transparency = comment.FillTransparency; // настройки выпадающих списков //cbTextStyle.SelectedIndex = SeriesComment.FontBold var patterns = new CommentaryPatternsList().GetStandardValues(); if (patterns == null) { return; } var items = new object[patterns.Count]; patterns.CopyTo(items, 0); cbTemplates.Items.AddRange(items); }
protected void MakeChartGraph(List <TrendLine> lines) { if (removeOldSigns) { RemoveOldSigns(); } int totalLosses = 0, totalProfits = 0; double totalPoints = 0; foreach (var line in lines) { line.Owner = chart.seriesTrendLine; chart.seriesTrendLine.data.Add(line); var sign = line.LineColor == ColorBuy ? 1 : -1; var deltaPoints = sign * (line.linePoints[1].Y - line.linePoints[0].Y); deltaPoints = DalSpot.Instance.GetPointsValue(chart.Symbol, (float)deltaPoints); totalPoints += deltaPoints; if (deltaPoints > 0) { totalProfits++; } if (deltaPoints < 0) { totalLosses++; } var comment = new ChartComment { Magic = LineMagic, PivotIndex = line.linePoints[1].X, PivotPrice = line.linePoints[1].Y, ArrowAngle = 120, ArrowLength = 30, Color = line.LineColor, ColorText = chart.chart.visualSettings.SeriesForeColor, Text = string.Format("{0:f1} пп", deltaPoints), Owner = chart.seriesComment }; chart.seriesComment.data.Add(comment); } var message = string.Format("{0:f1} пунктов всего, {1} \"профитов\", {2} \"лоссов\"", totalPoints, totalProfits, totalLosses); MessageBox.Show(message); }
public override string ActivateScript(CandleChartControl chart, PointD worldCoords) { var comment = chart.seriesComment.data.FirstOrDefault(c => c.Name == CommentSpecName); // удалить существующий if (comment != null) { chart.seriesComment.data.Remove(comment); chart.RedrawChartSafe(); return(string.Empty); } // добавить новый MarketOrder sumPos; string scriptText; GetCommentText(chart, out scriptText, out sumPos); if (sumPos == null) { return(string.Empty); } var colorFill = sumPos.ResultDepo > 0 ? Color.LightGreen : sumPos.ResultDepo < 0 ? Color.LightCoral : Color.Gray; var colorText = chart.chart.BackColor.GetBrightness() < 0.4f ? Color.White : Color.Black; comment = new ChartComment { FillTransparency = 80, ColorFill = colorFill, HideArrow = true, ArrowAngle = 90, ArrowLength = 1, PivotIndex = worldCoords.X, PivotPrice = worldCoords.Y, Owner = chart.seriesComment, Name = CommentSpecName, Text = scriptText, ColorText = colorText, Color = colorText }; chart.seriesComment.data.Add(comment); chart.RedrawChartSafe(); return(string.Empty); }
private void MakeCommentsByTags(List <DiverTag> tags) { foreach (var diverTag in tags) { var comm = new ChartComment { ArrowLength = arrowLengthPx, ArrowAngle = diverTag.side > 0 ? 270 : 90, Text = "!", //diverTag.description, PivotIndex = diverTag.candleIndex, PivotPrice = diverTag.price, HideArrow = !ShowArrow, HideBox = !ShowBox, ColorText = Color.Red }; seriesComment.data.Add(comm); } }
public override string ActivateScript(CandleChartControl chart, PointD worldCoords) { var robots = MainForm.Instance.RobotFarm.GetRobotsAsIs().ToList(); var commentText = GetCommentForChart(chart.Symbol, chart.Timeframe, robots); if (string.IsNullOrEmpty(commentText)) { return("Нет роботов для " + chart.Symbol + " " + BarSettingsStorage.Instance.GetBarSettingsFriendlyName(chart.Timeframe)); } // разместить комментарий на графике в указанной точке, удалить такой же, если был добавлен var comment = chart.seriesComment.data.FirstOrDefault(c => c.Name == CommentSpecName); if (comment != null) { chart.seriesComment.data.Remove(comment); chart.RedrawChartSafe(); return(string.Empty); } var colorFill = Color.LightGreen; var colorText = chart.chart.BackColor.GetBrightness() < 0.4f ? Color.White : Color.Black; comment = new ChartComment { FillTransparency = 80, ColorFill = colorFill, HideArrow = true, ArrowAngle = 90, ArrowLength = 1, PivotIndex = worldCoords.X, PivotPrice = worldCoords.Y, Owner = chart.seriesComment, Name = CommentSpecName, Text = commentText, ColorText = colorText, Color = colorText }; chart.seriesComment.data.Add(comment); chart.RedrawChartSafe(); return(string.Empty); }
private void AddChartCommentOnRobotState(int dealsOpened) { var commentLines = new List <string> { selectedBot.GetUniqueName() }; if (selectedBot is FiboLevelRobot) { var fiboBot = ((FiboLevelRobot)selectedBot); commentLines.Add("открыто " + dealsOpened + " из " + fiboBot.MaxDealsInSeries + " сделок"); commentLines.Add("цена A: " + fiboBot.PriceA.ToStringUniformPriceFormat() + ", цена B: " + fiboBot.PriceB.ToStringUniformPriceFormat()); } else { commentLines.Add("открыто " + dealsOpened + " сделок"); } var commentStr = string.Join(Environment.NewLine, commentLines); // найти на графике существующий комментарий или создать новый var comment = chart.seriesComment.data.FirstOrDefault(c => c.Name == CommentSpecName); if (comment == null) { comment = new ChartComment { Name = CommentSpecName, PivotIndex = scriptActivatedCoords.X, PivotPrice = scriptActivatedCoords.Y, HideArrow = true, Color = chart.chart.visualSettings.SeriesForeColor, ColorFill = Color.DarkTurquoise, Text = commentStr, Owner = chart.seriesComment }; chart.seriesComment.data.Add(comment); } else { comment.Text = commentStr; } }
/// <summary> /// начать визуальное редактировние (перетаскивание) ордера /// </summary> public override bool OnMouseButtonDown(MouseEventArgs e, Keys modifierKeys) { if (e.Button != MouseButtons.Left) { return(false); } draggedComment = null; var clientPoint = owner.PointToScreen(new Point(e.X, e.Y)); var x = clientPoint.X; var y = clientPoint.Y; // перетаскиваем ордер суммарной позы? var selectedCommentsLastBar = seriesCommentLastBar.GetObjectsUnderCursor(x, y, SeriesComment.DefaultMouseTolerance).Where(c => c.Name == "drag").ToList(); if (selectedCommentsLastBar.Count > 0) { draggedComment = (ChartComment)selectedCommentsLastBar[0]; draggedCommentSource = DraggedCommentSource.SummaryPositionOrder; } else if (selectedOrder != null) { // перетаскиваем ордер открытой позы? var selectedCommentsHit = seriesCommentSelected.GetObjectsUnderCursor(x, y, SeriesComment.DefaultMouseTolerance); draggedComment = (ChartComment)selectedCommentsHit.FirstOrDefault(c => c.Name == "drag"); draggedCommentSource = DraggedCommentSource.MarketOrder; } if (draggedComment == null) { return(false); } startDragPrice = (float)draggedComment.PivotPrice; draggedComment.DrawFrame = true; draggedComment.HideBox = false; return(true); }
private void AddTotalDealSlTpComment(int pivotIndex, float price, string comment, Color cl) { var text = comment + " " + DalSpot.Instance.FormatPrice(owner.Symbol, price); var comm = new ChartComment { PivotIndex = pivotIndex, PivotPrice = price, ArrowAngle = 0, ArrowLength = 20, Text = text, Color = cl, ColorText = cl, HideArrow = true, HideBox = true, Name = "drag" }; seriesCommentLastBar.data.Add(comm); }
public CommentsResponse AddChartComment(ChartCommentDto comment) { var response = new CommentsResponse(); try { var commentRepository = new ChartCommentRepository(); var userRepository = new UserProfileRepository(); var newComment = new ChartComment { IdUser = comment.IdUser, DatePosted = DateTime.Now, Message = comment.Message, IdSmartChart = comment.IdSmartChart, IsActive = true }; commentRepository.Add(newComment); commentRepository.SaveChanges(); int userId = Convert.ToInt32(comment.IdUser); response.Comments.Add(new ChartCommentDto() { Message = newComment.Message, DatePosted = newComment.DatePosted, UserName = userRepository.Query().FirstOrDefault(x => x.UserId == userId).UserName, IdComment = newComment.IdComment, IdUser = userId.ToString() }); commentRepository.Dispose(); response.Acknowledgment = true; response.Message = "Success"; } catch (Exception ex) { _logger.Error("Error adding comment. With the exception: " + ex.Message); response.Acknowledgment = false; response.Message = "Error: " + ex.Message; } return(response); }
public override string ActivateScript(CandleChartControl chart, PointD worldCoords) { chartSymbol = chart.Symbol; var comment = chart .seriesComment .data .FirstOrDefault(c => c.Name == CommentSpecName); // удалить существующий if (comment != null) { chart.seriesComment.data.Remove(comment); chart.RedrawChartSafe(); return(string.Empty); } var accountData = AccountStatus.Instance.AccountData; var accountStatistics = TradeSharpAccountStatistics.Instance.proxy.GetAccountProfit1000(accountData.ID); var ordersBySymbol = GetMarketOrders(accountData.ID).Where(x => x.Symbol == chart.Symbol); var scriptText = GetCommentText(accountData, accountStatistics, ordersBySymbol); comment = new ChartComment { FillTransparency = 80, ColorFill = Color.Gray, HideArrow = true, ArrowAngle = 90, ArrowLength = 1, PivotIndex = worldCoords.X, PivotPrice = worldCoords.Y, Owner = chart.seriesComment, Name = CommentSpecName, Text = scriptText, ColorText = Color.Black, Color = Color.Black }; chart.seriesComment.data.Add(comment); chart.RedrawChartSafe(); return(string.Empty); }
/// <summary> /// опеределить попадание в ордер, дабы "подсветить" его и произвести определенные действия /// (показать линии SL - TP, редактировать ордера) /// </summary> public override bool OnMouseButtonUp(MouseEventArgs e, Keys modifierKeys) { // закончить драг текстовой меточки if (draggedComment != null) { var newPrice = draggedComment.PivotPrice; var commentTitle = draggedComment.Text; // если цена сдвинута меньше, чем на 0.1 пп - ничего не предлагать var minDelta = DalSpot.Instance.GetAbsValue(owner.Symbol, 0.1f); // предложить пользователю изменить SL / TP if (commentTitle.Contains("SL") || commentTitle.Contains("TP")) { var isSl = commentTitle.Contains("SL"); if (Math.Abs(newPrice - startDragPrice) > minDelta) { // меняем рыночный ордер? if (draggedCommentSource == DraggedCommentSource.MarketOrder) { ModifyMarketOrderAfterDrag(newPrice, isSl, selectedOrder); } else if (draggedCommentSource == DraggedCommentSource.SummaryPositionOrder) { ModifySummaryOrderAfterDrag(newPrice, isSl, commentTitle); } } } // забыть о текстовой метке draggedComment.HideBox = true; draggedComment.DrawFrame = false; draggedComment = null; } if (e.Button != MouseButtons.Left) { return(false); } var screenPoint = owner.PointToScreen(new Point(e.X, e.Y)); var x = screenPoint.X; var y = screenPoint.Y; // попали в специальные отметки для выделенного ордера (например, предложение редактировать // или закрыть ордер)? var selectedCommentsHit = seriesCommentSelected.GetObjectsUnderCursor(x, y, SeriesAsteriks.DefaultMouseTolerance); if (selectedCommentsHit.Count > 0) { if (selectedOrder.IsOpened)//selectedCommentsHit.Any(c => c.Name == "edit") && selectedOrder != null) { // открыть окно редактирования ордера owner.Owner.CallShowWindowEditMarketOrder(selectedOrder); } else if (selectedCommentsHit.Any(c => c.Name == "info") && selectedOrder != null) { // показать информацию по закрытому ордеру owner.Owner.CallShowWindowInfoOnClosedOrder(selectedOrder); } return(false); } // найти выделенные кликом коменты или астериксы var comments = seriesComment.GetObjectsUnderCursor(x, y, SeriesComment.DefaultMouseTolerance); var asters = seriesAsteriks.GetObjectsUnderCursor(x, y, SeriesAsteriks.DefaultMouseTolerance); var orders = comments.Where(p => p.Magic > 0).Select(p => p.Magic).ToList(); orders.AddRange(asters.Where(a => a.Magic > 0).Select(a => a.Magic)); // ордер не выбран и до того не был выбран if (orders.Count == 0 && selectedOrder == null) { return(false); } // ордер не выбран, но был уже выбран ранее - снять выделение if (selectedOrder != null) { DeselectOrder(); return(true); } // найти ордер в списке открытых или закрытых позиций var order = openPositions.FirstOrDefault(p => orders.Contains(p.ID)) ?? closedPositions.FirstOrDefault(p => orders.Contains(p.ID)); if (order == null) { return(false); } var ptPane = Conversion.ScreenToWorld(new PointD(e.X, e.Y), owner.StockPane.WorldRect, owner.StockPane.CanvasRect); var pointTime = owner.StockSeries.GetCandleOpenTimeByIndex((int)Math.Round(ptPane.X)); SelectOrder(order, pointTime); return(true); }
private void AddClosedOrderMarks(ChartControl chart, MarketOrder pos) { // отметка входа var indexEnter = (int)chart.StockSeries.GetDoubleIndexByTime(pos.TimeEnter, true); if (indexEnter >= 0) { var tipEnter = new AsteriskTooltip("c", string.Empty) { Magic = pos.ID, Price = pos.PriceEnter, CandleIndex = indexEnter, DateStart = pos.TimeEnter, Sign = "", Shape = pos.Side > 0 ? AsteriskTooltip.ShapeType.СтрелкаВверх : AsteriskTooltip.ShapeType.СтрелкаВниз, ColorFill = pos.Side > 0 ? ColorBuyClosed : ColorSellClosed, ColorLine = Color.Black, ColorText = Color.Black, Radius = ArrowSizeClosed }; seriesAsteriks.data.Add(tipEnter); } if (pos.PriceExit == null || pos.TimeExit == null) { Logger.ErrorFormat("Индикатор ордеров: ошибка при отображении закрытой позиции №{0} - нет данных закрытия", pos.ID); return; } var indexExit = (int)chart.StockSeries.GetDoubleIndexByTime(pos.TimeExit.Value, true); if (indexExit >= 0) { var mark = pos.ExitReason == PositionExitReason.Closed ? "" : pos.ExitReason == PositionExitReason.ClosedFromUI ? "q" : pos.ExitReason == PositionExitReason.SL ? "SL" : pos.ExitReason == PositionExitReason.TP ? "TP" : pos.ExitReason == PositionExitReason.Stopout ? "STOP" : string.Empty; var tipExit = new AsteriskTooltip("q", mark) { Magic = pos.ID, Price = pos.PriceExit.Value, CandleIndex = indexExit, DateStart = pos.TimeExit, Sign = "", Shape = pos.Side > 0 ? AsteriskTooltip.ShapeType.КрестВниз : AsteriskTooltip.ShapeType.КрестВверх, ColorFill = chart.StockSeries.BarNeutralColor, ColorLine = Color.Black, ColorText = Color.Black, Radius = ArrowSizeClosed }; seriesAsteriks.data.Add(tipExit); } // комменты у стрелок входа / выхода if (ShowHistoryComments) { // коммент для входа if (indexEnter >= 0) { var comm = new ChartComment { PivotIndex = indexEnter, PivotPrice = pos.PriceEnter, ArrowAngle = 180, ArrowLength = 3, Text = pos.Side == 1 ? "B" : "S", Color = chart.StockSeries.BarNeutralColor, ColorText = pos.Side > 0 ? ColorBuyClosed : ColorSellClosed, HideArrow = true, HideBox = true, Name = Localizer.GetString("TitlePositionNumber") + pos.ID, Magic = pos.ID }; seriesComment.data.Add(comm); } // коммент для выхода // разобраться - выход был по ордеру либо просто закрытие if (indexExit >= 0) { var commentMark = pos.ExitReason == PositionExitReason.SL ? "SL" : pos.ExitReason == PositionExitReason.TP ? "TP" : "C"; var comm = new ChartComment { PivotIndex = indexExit, PivotPrice = pos.PriceExit.Value, ArrowAngle = 180, ArrowLength = 3, Text = commentMark, Color = pos.Side > 0 ? ColorBuyQuit : ColorSellQuit, ColorText = chart.StockSeries.BarNeutralColor, HideArrow = true, HideBox = true, Name = Localizer.GetString("TitlePositionNumber") + pos.ID, Magic = pos.ID }; seriesComment.data.Add(comm); } } }
private void AddOpenPositionMarks(ChartControl chart, MarketOrder pos, double x) { var index = (int)x; if (index <= 0) { return; } var tip = new AsteriskTooltip("o", string.Empty) { Price = pos.PriceEnter, Magic = pos.ID, CandleIndex = index, DateStart = pos.TimeEnter, Sign = "", Shape = pos.Side > 0 ? AsteriskTooltip.ShapeType.СтрелкаВверх : AsteriskTooltip.ShapeType.СтрелкаВниз, ColorFill = pos.Side > 0 ? ColorBuy : ColorSell, ColorLine = Color.Black, ColorText = Color.Black, Radius = ArrowSizeOpened, }; seriesAsteriks.data.Add(tip); if (ShowComments) { var currPrice = chart.StockPane.StockSeries.Data[chart.StockPane.StockSeries.Data.Count - 1].close; var profit = Math.Round(DalSpot.Instance.GetPointsValue(pos.Symbol, pos.Side * (currPrice - pos.PriceEnter))); var profitText = MakeCommentProfit(profit, pos.ResultDepo); // измерить высоту текста (необходимо для позиционирования текстовой метки) SizeF szText; using (var g = owner.CreateGraphics()) { using (var font = new Font(owner.Font.FontFamily, SeriesComment.FontSize, SeriesComment.FontBold ? FontStyle.Bold : FontStyle.Regular)) { szText = g.MeasureString("BS", font); } } var h = szText.Height / 2; const int l = 15; var arLen = (int)Math.Sqrt(h * h + l * l); var angle = 180 - Math.Atan2(h, l) * 180 / Math.PI; var dealText = (pos.Side == 1 ? "Buy " : "Sell ") + pos.PriceEnter.ToStringUniformPriceFormat(); var commPos = new ChartComment { PivotIndex = x, PivotPrice = pos.PriceEnter, ArrowAngle = -angle, ArrowLength = arLen, Text = dealText, ColorText = pos.Side > 0 ? ColorBuy : ColorSell, HideArrow = true, HideBox = true, // ReSharper disable SpecifyACultureInStringConversionExplicitly Name = pos.ID.ToString(), // ReSharper restore SpecifyACultureInStringConversionExplicitly Magic = pos.ID }; seriesComment.data.Add(commPos); var commProfit = new ChartComment { PivotIndex = x, PivotPrice = pos.PriceEnter, ArrowAngle = angle, ArrowLength = arLen, Text = profitText, ColorText = profit > 0 ? Color.Green : Color.Red, HideArrow = true, HideBox = true, Name = pos.ID + "PL", Magic = pos.ID, TextCustom = "p" }; seriesComment.data.Add(commProfit); } }
/// <summary> /// добавить метки справа от крайнего бара: SL, TP, профит и прочая хрень /// </summary> private void AddLastBarMarks() { var pivotIndex = owner.StockPane.StockSeries.Data.Count - 1; // прибыль/убыток по сделкам var comm = new ChartComment { PivotIndex = pivotIndex, PivotPrice = owner.StockPane.StockSeries.Data[owner.StockPane.StockSeries.Data.Count - 1].close, ArrowAngle = 330, ArrowLength = 23, Text = "", Color = owner.StockSeries.BarNeutralColor, ColorText = owner.StockSeries.BarNeutralColor, HideArrow = true, HideBox = true, Name = "CurrProfit" }; seriesCommentLastBar.data.Add(comm); var commProfitAbs = new ChartComment { PivotIndex = pivotIndex, PivotPrice = owner.StockPane.StockSeries.Data[owner.StockPane.StockSeries.Data.Count - 1].close, ArrowAngle = 0, ArrowLength = 20, Text = "", Color = owner.StockSeries.BarNeutralColor, ColorText = owner.StockSeries.BarNeutralColor, HideArrow = true, HideBox = true, Name = "CurrProfitAbs" }; seriesCommentLastBar.data.Add(commProfitAbs); var commProfitPercent = new ChartComment { PivotIndex = pivotIndex, PivotPrice = owner.StockPane.StockSeries.Data[owner.StockPane.StockSeries.Data.Count - 1].close, ArrowAngle = 30, ArrowLength = 23, Text = "", Color = owner.StockSeries.BarNeutralColor, ColorText = owner.StockSeries.BarNeutralColor, HideArrow = true, HideBox = true, Name = "CurrProfitPercent" }; seriesCommentLastBar.data.Add(commProfitPercent); // стопы-тейки и прочая ерунда // раздельно по BUY и SELL float?buyStop = null, buyTake = null; //, buyTrail = null; float?sellStop = null, sellTake = null; //, sellTrail = null; int buyVolumeSl = 0, buyVolumeTake = 0; //, buyVolumeTrail = 0; int sellVolumeSl = 0, sellVolumeTake = 0; //, sellVolumeTrail = 0; foreach (var pos in openPositions) { if (pos.Side > 0) { if (pos.StopLoss.HasValue) { buyStop = (buyStop ?? 0) + pos.StopLoss * pos.Volume; buyVolumeSl += pos.Volume; } if (pos.TakeProfit.HasValue) { buyTake = (buyTake ?? 0) + pos.TakeProfit * pos.Volume; buyVolumeTake += pos.Volume; } continue; } if (pos.StopLoss.HasValue) { sellStop = (sellStop ?? 0) + pos.StopLoss * pos.Volume; sellVolumeSl += pos.Volume; } if (pos.TakeProfit.HasValue) { sellTake = (sellTake ?? 0) + pos.TakeProfit * pos.Volume; sellVolumeTake += pos.Volume; } } var hasMarksBuy = buyStop.HasValue || buyTake.HasValue; var hasMarksSell = sellStop.HasValue || sellTake.HasValue; var colorMarkBuy = Color.Blue; var colorMarkSell = Color.Red; // добавить коменты if (buyStop.HasValue) { buyStop /= buyVolumeSl; AddTotalDealSlTpComment(pivotIndex, buyStop.Value, hasMarksSell ? "BUY SL" : "SL", colorMarkBuy); } if (buyTake.HasValue) { buyTake /= buyVolumeTake; AddTotalDealSlTpComment(pivotIndex, buyTake.Value, hasMarksSell ? "BUY TP" : "TP", colorMarkBuy); } if (sellStop.HasValue) { sellStop /= sellVolumeSl; AddTotalDealSlTpComment(pivotIndex, sellStop.Value, hasMarksBuy ? "SELL SL" : "SL", colorMarkSell); } if (sellTake.HasValue) { sellTake /= sellVolumeTake; AddTotalDealSlTpComment(pivotIndex, sellTake.Value, hasMarksBuy ? "SELL TP" : "TP", colorMarkSell); } }
/// <summary> /// добавить подробные коменты по сделке /// </summary> private void ShowSelectedDealComments(MarketOrder order, bool selectedByExitMark) { var listComments = new List <string>(); // данные по входу в рынок var commentMain = new StringBuilder (((DealType)order.Side).ToString() + " " + order.Volume.ToStringUniformMoneyFormat() + " " + order.Symbol + Environment.NewLine + order.TimeEnter.ToString("dd MMM HH:mm") + ", " + order.PriceEnter.ToStringUniformPriceFormat(true)); // SL, TP, результат выхода из рынка if (order.TimeExit.HasValue || order.StopLoss.HasValue || order.TakeProfit.HasValue) { commentMain.AppendLine(); if (order.StopLoss.HasValue || order.TakeProfit.HasValue) { if (order.StopLoss.HasValue) { commentMain.AppendFormat("SL: {0}", order.StopLoss.Value.ToStringUniformPriceFormat()); } if (order.StopLoss.HasValue && order.TakeProfit.HasValue) { commentMain.Append(" "); } if (order.TakeProfit.HasValue) { commentMain.AppendFormat("TP: {0}", order.TakeProfit.Value.ToStringUniformPriceFormat()); } if (order.TimeExit.HasValue) { commentMain.AppendLine(); } } if (order.TimeExit.HasValue) { commentMain.AppendFormat("Закрыт {0} по {1}", order.TimeExit.Value.ToString("dd MMM HH:mm"), // ReSharper disable PossibleInvalidOperationException order.PriceExit.Value.ToStringUniformPriceFormat()); // ReSharper restore PossibleInvalidOperationException commentMain.AppendLine(); commentMain.AppendFormat("Результат: {0}", order.ResultDepo.ToStringUniformMoneyFormat()); } } listComments.Add(commentMain.ToString()); // предложение закрыть или редактировать ордер //if (!order.TimeExit.HasValue) // listComments.Add("Редактировать /" + Environment.NewLine + "Закрыть"); // точка привязки (к маркеру входа или выхода) var pivot = selectedByExitMark // ReSharper disable PossibleInvalidOperationException ? new PointD(owner.StockSeries.GetDoubleIndexByTime(order.TimeExit.Value, true), order.PriceExit.Value) // ReSharper restore PossibleInvalidOperationException : new PointD(owner.StockSeries.GetDoubleIndexByTime(order.TimeEnter, true), order.PriceEnter); // по текущему результату определить, прибыльный ордер или убыточный var profitSide = Math.Sign(order.ResultDepo); if (order.IsOpened) { var curPrice = owner.StockSeries.Data.Candles[owner.StockSeries.DataCount - 1].close; profitSide = Math.Sign(order.Side * (curPrice - order.PriceEnter)); } var colorFill = profitSide > 0 ? Color.LightGreen : profitSide < 0 ? Color.LightCoral : Color.BlanchedAlmond; // таки добавить коменты var angle = selectedDealCommentAngle; const int arrowLen = 90; foreach (var text in listComments) { var x = pivot.X; var price = pivot.Y; var comment = new ChartComment { Text = text, ArrowLength = arrowLen, ArrowAngle = angle, HideBox = false, HideArrow = false, DrawFrame = true, FillTransparency = 200, ColorFill = colorFill, Color = owner.visualSettings.SeriesForeColor, ColorText = owner.visualSettings.SeriesForeColor, PivotIndex = x, PivotPrice = price }; // развернуть комментарий так, чтобы он не накрывал ничего лишнего и не вылезал за пределы экрана AdjustDealCommentArrowAngle(comment, order); if (order.IsClosed) { comment.Name = "info"; } seriesCommentSelected.data.Add(comment); angle += 120; } // пометить комментарий с предложением редактировать / закрыть ордер if (!order.TimeExit.HasValue) { seriesCommentSelected.data[seriesCommentSelected.data.Count - 1].Name = "edit"; } // сместить угол стрелок selectedDealCommentAngle += 45; if (selectedDealCommentAngle > 360) { selectedDealCommentAngle -= 360; } }
/// <summary> /// нарисовать "полочки" уровней SL - TP /// </summary> private void ShowSelectedDealSlTpLines(MarketOrder order) { if (order.StopLoss == null && order.TakeProfit == null) { return; } var indexStart = (int)owner.StockSeries.GetDoubleIndexByTime(order.TimeEnter); var indexEnd = order.TimeExit.HasValue ? (int)owner.StockSeries.GetDoubleIndexByTime(order.TimeExit.Value) : indexStart; // линия не должна быть слишком короткой var sizePixel = Conversion.WorldToScreen(new SizeD(indexEnd - indexStart, 0), owner.StockPane.WorldRect, owner.StockPane.CanvasRect); const int targetWidth = 35; if (sizePixel.Width < targetWidth) { var newSize = Conversion.ScreenToWorld(new SizeD(targetWidth, 0), owner.StockPane.WorldRect, owner.StockPane.CanvasRect); var len = (int)Math.Round(newSize.Width); indexEnd += len; } var prices = new List <float>(); var tags = new List <string>(); if (order.StopLoss.HasValue) { prices.Add(order.StopLoss.Value); tags.Add("SL"); } if (order.TakeProfit.HasValue) { prices.Add(order.TakeProfit.Value); tags.Add("TP"); } // добавить линии var priceIndex = 0; foreach (var price in prices) { var posLine = new TrendLine { LineColor = owner.StockSeries.DownLineColor, ShapeAlpha = 255, ShapeFillColor = owner.StockSeries.DownLineColor, LineStyle = TrendLine.TrendLineStyle.Отрезок, PenWidth = 1, PenDashStyle = DashStyle.Dot, Name = "drag" }; posLine.AddPoint(indexStart, price); posLine.AddPoint(indexEnd, price); seriesSelectedLines.data.Add(posLine); // добавить с каждой стороны масенький комент: SL или TP var comment = new ChartComment { Text = tags[priceIndex], PivotPrice = price, ArrowAngle = 180, ArrowLength = 2, PivotIndex = indexStart, ColorText = owner.visualSettings.SeriesForeColor, Color = owner.visualSettings.SeriesForeColor, DrawFrame = false, HideBox = true, HideArrow = true, Name = "drag" }; seriesCommentSelected.data.Add(comment); comment = new ChartComment { Text = tags[priceIndex], PivotPrice = price, ArrowAngle = 0, ArrowLength = 2, PivotIndex = indexEnd, ColorText = owner.visualSettings.SeriesForeColor, Color = owner.visualSettings.SeriesForeColor, DrawFrame = false, HideBox = true, HideArrow = true, Name = "drag" }; seriesCommentSelected.data.Add(comment); priceIndex++; } }
private void AdjustDealCommentArrowAngle(ChartComment comment, MarketOrder order) { if (!order.TimeExit.HasValue) { return; } // получить основное направление // (продолжение линии open - close) var ptE = Conversion.WorldToScreen( new PointD(owner.StockSeries.GetDoubleIndexByTime(order.TimeEnter), order.PriceEnter), owner.StockPane.WorldRect, owner.StockPane.CanvasRect); var ptQ = Conversion.WorldToScreen( // ReSharper disable PossibleInvalidOperationException new PointD(owner.StockSeries.GetDoubleIndexByTime(order.TimeExit.Value), order.PriceExit.Value), // ReSharper restore PossibleInvalidOperationException owner.StockPane.WorldRect, owner.StockPane.CanvasRect); var ptPivot = Conversion.WorldToScreen(new PointD(comment.PivotIndex, comment.PivotPrice), owner.StockPane.WorldRect, owner.StockPane.CanvasRect); var ptStart = (Math.Abs(ptPivot.X - ptE.X) < Math.Abs(ptPivot.X - ptQ.X)) ? ptQ : ptE; var mainAngle = Math.Atan2(ptPivot.Y - ptStart.Y, ptPivot.X - ptStart.X) * 180 / Math.PI; // "округлить" угол mainAngle = Math.Round(mainAngle / 15) * 15; // расставить направления по приоритету const int numAngles = 8; var angles = new double[numAngles]; angles[0] = mainAngle; for (var i = 0; i < numAngles / 2 - 1; i++) { var delta = (i + 1) * 360 / numAngles; angles[i * 2 + 1] = mainAngle + delta; angles[i * 2 + 2] = mainAngle - delta; } angles[numAngles - 1] = -mainAngle; // выбрать направление, для которого комментарий не вылезает за рамки экрана var foundAngle = false; var screenRect = seriesComment.Owner.CanvasRect; using (var gr = owner.CreateGraphics()) foreach (var angle in angles) { comment.ArrowAngle = angle; var commentRect = comment.GetCommentRectangle(gr, owner.StockPane.WorldRect, owner.StockPane.CanvasRect); // не вылез ли комментарий за пределы экрана? if (commentRect.Left < screenRect.Left || commentRect.Top < screenRect.Top || commentRect.Right > screenRect.Right || commentRect.Bottom > screenRect.Bottom) { continue; } foundAngle = true; break; } // угол поворота по-умолчанию if (!foundAngle) { comment.ArrowAngle = angles[0]; } }