/// <summary> /// Отрисовать индекс на графике /// </summary> /// <param name="isHigh">тру - если фрактал верха</param> /// <param name="candle">текущая свеча</param> /// <param name="index">индекс текущей свечи</param> private void DrawIndi(bool isHigh, CandleData candle, int index) { //? Из за недостатка комментариев я не очень понял какие поля зачему нужны у этого класса // Думаю, что ты и так знаешь, но классы которыми нужно оперировать при отрисовке, надо бы описать в документации /* А * Конечно, надо :) */ var tip = new AsteriskTooltip("fractal", string.Empty) { Price = isHigh ? (candle.high + offset) : (candle.low - offset), CandleIndex = index, //DateStart = candle.timeOpen,//? Что это и нужно ли здесь Sign = "", //? Что это и нужно ли здесь Shape = isHigh ? AsteriskTooltip.ShapeType.ГалкаВверх : AsteriskTooltip.ShapeType.ГалкаВниз, ColorFill = isHigh ? clArrowHigh : clArrowLow, ColorLine = Color.Black, ColorText = Color.Black, Radius = 6 //? Что это и нужно ли здесь }; /* А * Sign - текст, выводимый посередине * Radius - каждая фигурка задана в "векторной" форме, Radius задает ее измерение (ширину, вроде) в пикс., пропорционально меняется другое измерение * DateStart можно не указывать, если задан CandleIndex */ series.data.Add(tip); }
/// <summary> /// поставить отметку на графике /// </summary> private void ShowPriceOnChart(CandleChartControl chart, PointD worldCoords) { var text = PricePoint == PricePointType.A ? "A" : "B"; var name = "ScriptSetFiboMark" + text; // удалить старую отметку с графика int index; chart.seriesAsteriks.FindObject(a => a.Name == name, out index); if (index >= 0) { chart.seriesAsteriks.RemoveObjectByNum(index); } // добавить новую отметку var mark = new AsteriskTooltip(name, text) { Price = (float)worldCoords.Y, CandleIndex = (int)(Math.Round(worldCoords.X)), ColorLine = chart.chart.visualSettings.SeriesForeColor, Sign = text }; mark.ColorText = mark.ColorLine; mark.ColorFill = chart.chart.visualSettings.ChartBackColor; mark.Shape = AsteriskTooltip.ShapeType.Звезда; chart.seriesAsteriks.data.Add(mark); }
private void AddRobotHintOnChart(RobotHint hint, ChartForm chart) { // добавить отрезочек с комментарием if (hint.RobotHintType == RobotMark.HintType.Линия) { AddRobotHintLineOnChart(hint, chart); return; } // добавить звездочку var toolTip = new AsteriskTooltip(hint.Title, hint.Text) { Owner = chart.chart.seriesAsteriks, Price = hint.Price.Value, CandleIndex = chart.chart.chart.StockSeries.GetIndexByCandleOpen( hint.Time.Value), DateStart = hint.Time.Value, Sign = hint.Sign, Radius = 5, Shape = hint.RobotHintType == RobotMark.HintType.Стоп ? AsteriskTooltip.ShapeType.Квадрат : hint.RobotHintType == RobotMark.HintType.Тейк ? AsteriskTooltip.ShapeType.Квадрат : hint.RobotHintType == RobotMark.HintType.Покупка ? AsteriskTooltip.ShapeType.СтрелкаВверх : hint.RobotHintType == RobotMark.HintType.Продажа ? AsteriskTooltip.ShapeType.СтрелкаВниз : hint.RobotHintType == RobotMark.HintType.Поджатие ? AsteriskTooltip.ShapeType.Звезда : AsteriskTooltip.ShapeType.Круг }; if (!string.IsNullOrEmpty(hint.HintCode)) { toolTip.Name = hint.HintCode; } if (hint.ColorFill.HasValue) { toolTip.ColorFill = hint.ColorFill.Value; } if (hint.ColorLine.HasValue) { toolTip.ColorLine = hint.ColorLine.Value; } if (hint.ColorText.HasValue) { toolTip.ColorText = hint.ColorText.Value; } chart.chart.seriesAsteriks.data.Add(toolTip); }
//public void BuildSeries2(ChartControl chart) //{ // tooltipSeries.data.Clear(); // averageRangeList.Clear(); // var candles = chart.StockSeries.Data.Candles; // var minimumCandles = PointsRange > 0 ? 5 : MaPeriod + 5; // if (candles == null || candles.Count < minimumCandles) return; // var lastRanges = new RestrictedQueue<float>(MaPeriod); // float targetSize = PointsRange > 0 ? DalSpot.Instance.GetAbsValue(chart.Symbol, (float)PointsRange) : 0; // var series = 0; // for (var i = 0; i < candles.Count; i++) // { // var candle = candles[i]; // var range = candle.high - candle.low; // lastRanges.Add(range); // if (lastRanges.Length < lastRanges.MaxQueueLength && PointsRange == 0) // { // averageRangeList.Add(0); // continue; // } // var avgRange = lastRanges.Average(); // averageRangeList.Add(avgRange); // var candlePercent = range * 100 / avgRange; // var isNarrow = PointsRange > 0 ? range < targetSize // : candlePercent <= NarrowPercent; // if (!isNarrow && series > 0) // { // if (series >= CandlesToSignal) // { // // отметить, сколько процентов данная свеча составила от обычной волатильности // AddMark($"{candlePercent:F0}%", $"{candlePercent:F0}%", i, candle.open, false); // } // series = 0; // continue; // } // if (!isNarrow) continue; // series++; // if (series >= CandlesToSignal) // { // AddMark($"{series}", $"{series}", i, candle.close, true); // } // } //} private void AddMark(string name, string text, int index, float price, bool inversed) { var line = new AsteriskTooltip(name, text) { ColorLine = Color.White, ColorText = inversed ? Color.White : Color.Black, ColorFill = inversed ? Color.Black : Color.White, CandleIndex = index, Price = price, Sign = name, Shape = inversed ? AsteriskTooltip.ShapeType.Круг : AsteriskTooltip.ShapeType.Квадрат, TransparencyText = 255 }; tooltipSeries.data.Add(line); }
private void SetupGrid() { var blank = new AsteriskTooltip(); grid.Columns.Add(new FastColumn(blank.Property(p => p.Name), Localizer.GetString("TitleName")) { ColumnMinWidth = 50, SortOrder = FastColumnSort.Ascending }); grid.Columns.Add(new FastColumn(blank.Property(p => p.ClassName), Localizer.GetString("TitleType")) { ColumnMinWidth = 50 }); grid.Columns.Add(new FastColumn(blank.Property(p => p.DateStart), Localizer.GetString("TitleTime")) { ColumnWidth = 97 }); grid.CalcSetTableMinWidth(); grid.UserHitCell += GridUserHitCell; }
private void ShowRobotDataOnChart() { var orders = MarketOrdersStorage.Instance.MarketOrders.Where(o => o.Symbol == chart.Symbol && o.Magic == selectedBot.Magic).ToList(); // показать ордера на графике foreach (var order in orders) { var candleIndex = (int)Math.Round(chart.chart.StockSeries.GetDoubleIndexByTime(order.TimeEnter)); var shape = order.Side > 0 ? AsteriskTooltip.ShapeType.СтрелкаВверх : AsteriskTooltip.ShapeType.СтрелкаВниз; var objectExists = chart.seriesAsteriks.data.Any(a => a.Price.RoughCompares(order.PriceEnter, 0.00001f) && a.CandleIndex == candleIndex && a.Shape == shape); if (objectExists) { continue; } var asterisk = new AsteriskTooltip(order.Side < 0 ? "SELL" : "BUY", selectedBot.GetUniqueName() + ": " + order.ToStringShort()) { ColorFill = order.Side < 0 ? colorSell : colorBuy, Price = order.PriceEnter, CandleIndex = candleIndex, DateStart = order.TimeEnter, Shape = shape, Sign = order.Side > 0 ? "b" : "s", Owner = chart.seriesAsteriks }; chart.seriesAsteriks.data.Add(asterisk); } AddChartCommentOnRobotState(orders.Count); }
private void BuildChannel(List <CandleData> candles, int from) { var channel = new Cortege3 <PointD, PointD, PointD> { }; var state = ChannelStateInfo.НетКанала; switch (ChannelState) { case ChannelStateInfo.НетКанала: // канала еще нет, ищем сначала по верхним точкам, если не находим, пробуем по нижним найти var channelhigh = GetHighLineChannel(candles, from); var channellow = GetLowLineChannel(candles, from); if ((channelhigh.a == new PointD(0, 0) || channelhigh.b == new PointD(0, 0)) && (channellow.a == new PointD(0, 0) || channellow.b == new PointD(0, 0))) { break; } if (channelhigh.a == new PointD(0, 0) || channelhigh.b == new PointD(0, 0)) { state = ChannelStateInfo.ПостроенПоМинимумам; channel = channellow; break; } if ((channellow.a == new PointD(0, 0) || channellow.b == new PointD(0, 0))) { state = ChannelStateInfo.ПостроенПоМаксимумам; channel = channelhigh; break; } if (channelhigh.b.X > channellow.b.X || (channelhigh.b.X == channellow.b.X && channelhigh.a.X > channellow.a.X)) { state = ChannelStateInfo.ПостроенПоМаксимумам; channel = channelhigh; break; } state = ChannelStateInfo.ПостроенПоМинимумам; channel = channellow; break; case ChannelStateInfo.ПостроенПоМаксимумам: channel = GetLowLineChannel(candles, from); if (channel.a == new PointD(0, 0) || channel.b == new PointD(0, 0)) { return; } state = ChannelStateInfo.ПостроенПоМинимумам; break; case ChannelStateInfo.ПостроенПоМинимумам: channel = GetHighLineChannel(candles, from); if (channel.a == new PointD(0, 0) || channel.b == new PointD(0, 0)) { return; } state = ChannelStateInfo.ПостроенПоМаксимумам; break; } if (lastIndexB == 0 && lastIndexA == 0) { lastIndexA = channel.a.X; lastIndexB = channel.b.X; } if (lastIndexB == channel.b.X && lastIndexA < channel.a.X) { // найденный канал старше текущего, игнорируем return; } if (!ShowAllChannels && lastIndexB > channel.b.X) { // найденный канал уже старый, игнорируем его channel = currChannel; } var a = channel.a; var b = channel.b; var k = (b.Y - a.Y) / (b.X - a.X); // стираем старый канал if (!ShowAllChannels) { series.data.Clear(); } // находим точку на текущей свече + 1, туда и продлим канал var bx = 0; if (series.data.Count == 0) { bx = candles.Count; } else { bx = (int)b.X + CountAfter + CountForward; } if (TradePoints) { // просто покажем точки входа и все // посчитаем точки входа если они есть for (var i = (int)b.X + CountAfter + 1; i < b.X + CountAfter + CountForward; i++) { if (candles.Count <= i) { break; } var highY = b.Y + k * (i - b.X); var lowY = channel.c.Y + k * (i - channel.c.X); if (highY <= candles[i].high && highY >= candles[i].low) { var detailed = string.Format("S {0}", highY); var tip = new AsteriskTooltip(detailed, detailed) { Price = (float)highY, CandleIndex = i, DateStart = candles[i].timeOpen, Sign = "e", Shape = AsteriskTooltip.ShapeType.СтрелкаВниз, ColorFill = Color.Pink, ColorLine = Color.Black, ColorText = Color.Black }; commentSeries.data.Add(tip); break; } if (lowY >= candles[i].low && lowY <= candles[i].high) { var detailed = string.Format("B {0}", lowY); var tip = new AsteriskTooltip(detailed, detailed) { Price = (float)lowY, CandleIndex = i, DateStart = candles[i].timeOpen, Sign = "e", Shape = AsteriskTooltip.ShapeType.СтрелкаВверх, ColorFill = Color.Green, ColorLine = Color.Black, ColorText = Color.Black }; commentSeries.data.Add(tip); break; } } } else { var line = new TrendLine { LineColor = state == ChannelStateInfo.ПостроенПоМаксимумам ? ClUpLine : ClDownLine, LineStyle = TrendLine.TrendLineStyle.Отрезок }; // линия по двум экстремумам line.linePoints.Add(new PointD(a.X - countBefore, b.Y + k * (a.X - countBefore - b.X))); line.linePoints.Add(new PointD(bx, b.Y + k * (bx - b.X))); series.data.Add(line); line = new TrendLine { LineColor = state == ChannelStateInfo.ПостроенПоМаксимумам ? ClUpLine : ClDownLine, LineStyle = TrendLine.TrendLineStyle.Отрезок }; var c = channel.c; // линия по одному экстремуму line.linePoints.Add(new PointD(a.X - countBefore, c.Y + k * (a.X - countBefore - c.X))); line.linePoints.Add(new PointD(bx, c.Y + k * (bx - c.X))); series.data.Add(line); } ChannelState = state; currChannel = channel; lastIndexA = currChannel.a.X; lastIndexB = currChannel.b.X; }
/// <summary> /// результаты моделирования необходимо отобразить на графиках /// (в виде коментариев и т.д.) /// </summary> private void OnRobotResultsBoundToCharts(Dictionary <BaseRobot, ChartWindowSettings> robotBindings, List <RobotLogEntry> robotLogEntries, List <MarketOrder> posClosed, List <MarketOrder> posOpened) { // предложить убрать коментарии с графиков, // выбрать коментарии var dlg = new ResultDisplaySettingsForm(); if (dlg.ShowDialog() == DialogResult.Cancel) { return; } // убрать коментарии? var removeOldComments = dlg.RemoveOldMarkers; if (removeOldComments) { var chartsToPurge = Charts.Where(c => robotBindings.Any( rb => rb.Value.TabPageId == c.bookmarkId && rb.Value.Symbol == c.chart.Symbol && rb.Value.Timeframe == c.chart.timeframeString)); foreach (var chart in chartsToPurge) { chart.chart.seriesAsteriks.data.Clear(); } } foreach (var robotBinding in robotBindings) { // найти график для робота var timeframe = robotBinding.Value.Timeframe; var ticker = robotBinding.Value.Symbol; var pageId = robotBinding.Value.TabPageId; var robot = robotBinding.Key; var chart = Charts.First(c => c.chart.timeframeString == timeframe && c.chart.Symbol == ticker && c.bookmarkId == pageId); // выбрать все сообщения для графика var logEntries = robotLogEntries.Where(l => l.Robot == robot); var messagesPlainList = new List <string>(); foreach (var logEntry in logEntries) { messagesPlainList.AddRange(logEntry.Messages); } // добавить коментарии на график foreach (var msg in messagesPlainList) { var mark = RobotMark.ParseString(msg); if (mark == null || mark is RobotHint == false) { continue; } var hint = (RobotHint)mark; if (!hint.Price.HasValue || !hint.Time.HasValue) { continue; } AddOrRemoveRobotHintOnChart(hint, chart); } // добавить информацию о сделках if (!dlg.ShowMarkers) { continue; } // выбрать сделки для отображения var deals4Chart = posClosed.Where(p => p.Symbol == chart.chart.Symbol).ToList(); deals4Chart.AddRange(posOpened.Where(p => p.Symbol == chart.chart.Symbol)); foreach (var deal in deals4Chart) { // отметки входа if (dlg.ShowEnters) { var detailed = string.Format("#{0} {1} {2:dd.MM.yyyy HH:mm} по {3}", deal.ID, deal.Side > 0 ? "B" : "S", deal.TimeEnter, deal.PriceEnter); if (deal.IsClosed) { var result = (deal.PriceExit.Value - deal.PriceEnter) * deal.Side; var points = DalSpot.Instance.GetPointsValue(deal.Symbol, result); detailed += string.Format(" - {0:dd.MM.yyyy HH:mm} по {1}, {2:f0} пп", deal.TimeExit.Value, deal.PriceExit.Value, points); } var tip = new AsteriskTooltip(detailed, detailed) { Price = deal.PriceEnter, CandleIndex = chart.chart.chart.StockSeries.GetIndexByCandleOpen(deal.TimeEnter), DateStart = deal.TimeEnter, Sign = "e", Shape = deal.Side > 0 ? AsteriskTooltip.ShapeType.СтрелкаВверх : AsteriskTooltip.ShapeType.СтрелкаВниз, ColorFill = deal.Side > 0 ? Color.Green : Color.Salmon, ColorLine = Color.Black, ColorText = Color.Black, Radius = 5 }; if (!dlg.ShowDetailedEnters) { tip.Name = string.Format("Сделка #{0}, {1}", deal.ID, deal.Side > 0 ? "Buy" : "Sell"); } chart.chart.seriesAsteriks.data.Add(tip); } if (dlg.ShowExits && deal.IsClosed) { var detailed = string.Format("Выход #{0} {1} {2:dd.MM.yyyy HH:mm} по {3} - {4:dd.MM.yyyy HH:mm} по {5}", deal.ID, deal.Side > 0 ? "B" : "S", deal.TimeEnter, deal.PriceEnter, deal.TimeExit.Value, deal.PriceExit.Value); var tip = new AsteriskTooltip(detailed, detailed) { Price = deal.PriceExit.Value, CandleIndex = chart.chart.chart.StockSeries.GetIndexByCandleOpen( deal.TimeExit.Value), DateStart = deal.TimeExit.Value, Sign = "q", Shape = AsteriskTooltip.ShapeType.Квадрат, ColorFill = deal.Side > 0 ? Color.Green : Color.Salmon, ColorLine = Color.Black, ColorText = Color.Black, Radius = 5 }; if (!dlg.ShowDetailedExits) { tip.Name = string.Format("Закрытие #{0}, {1}", deal.ID, deal.Side > 0 ? "Buy" : "Sell"); } chart.chart.seriesAsteriks.data.Add(tip); } } } }
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> /// поставить отметку на графике /// </summary> private void ShowPriceOnChart(CandleChartControl chart, PointD worldCoords) { var text = PricePoint == PricePointType.A ? "A" : "B"; var name = "ScriptSetFiboMark" + text; // удалить старую отметку с графика int index; chart.seriesAsteriks.FindObject(a => a.Name == name, out index); if (index >= 0) chart.seriesAsteriks.RemoveObjectByNum(index); // добавить новую отметку var mark = new AsteriskTooltip(name, text) { Price = (float) worldCoords.Y, CandleIndex = (int) (Math.Round(worldCoords.X)), ColorLine = chart.chart.visualSettings.SeriesForeColor, Sign = text }; mark.ColorText = mark.ColorLine; mark.ColorFill = chart.chart.visualSettings.ChartBackColor; mark.Shape = AsteriskTooltip.ShapeType.Звезда; chart.seriesAsteriks.data.Add(mark); }
/// <summary> /// Отрисовать индекс на графике /// </summary> /// <param name="isHigh">тру - если фрактал верха</param> /// <param name="candle">текущая свеча</param> /// <param name="index">индекс текущей свечи</param> private void DrawIndi(bool isHigh, CandleData candle, int index) { //? Из за недостатка комментариев я не очень понял какие поля зачему нужны у этого класса // Думаю, что ты и так знаешь, но классы которыми нужно оперировать при отрисовке, надо бы описать в документации /* А Конечно, надо :) */ var tip = new AsteriskTooltip("fractal", string.Empty) { Price = isHigh ? (candle.high + offset) : (candle.low - offset), CandleIndex = index, //DateStart = candle.timeOpen,//? Что это и нужно ли здесь Sign = "", //? Что это и нужно ли здесь Shape = isHigh ? AsteriskTooltip.ShapeType.ГалкаВверх : AsteriskTooltip.ShapeType.ГалкаВниз, ColorFill = isHigh ? clArrowHigh : clArrowLow, ColorLine = Color.Black, ColorText = Color.Black, Radius = 6 //? Что это и нужно ли здесь }; /* А Sign - текст, выводимый посередине Radius - каждая фигурка задана в "векторной" форме, Radius задает ее измерение (ширину, вроде) в пикс., пропорционально меняется другое измерение DateStart можно не указывать, если задан CandleIndex */ series.data.Add(tip); }
private void BuildChannel(List<CandleData> candles, int from) { var channel = new Cortege3<PointD, PointD, PointD> { }; var state = ChannelStateInfo.НетКанала; switch (ChannelState) { case ChannelStateInfo.НетКанала: // канала еще нет, ищем сначала по верхним точкам, если не находим, пробуем по нижним найти var channelhigh = GetHighLineChannel(candles, from); var channellow = GetLowLineChannel(candles, from); if ((channelhigh.a == new PointD(0, 0) || channelhigh.b == new PointD(0, 0)) && (channellow.a == new PointD(0, 0) || channellow.b == new PointD(0, 0))) break; if (channelhigh.a == new PointD(0, 0) || channelhigh.b == new PointD(0, 0)) { state = ChannelStateInfo.ПостроенПоМинимумам; channel = channellow; break; } if ((channellow.a == new PointD(0, 0) || channellow.b == new PointD(0, 0))) { state = ChannelStateInfo.ПостроенПоМаксимумам; channel = channelhigh; break; } if (channelhigh.b.X > channellow.b.X || (channelhigh.b.X == channellow.b.X && channelhigh.a.X > channellow.a.X)) { state = ChannelStateInfo.ПостроенПоМаксимумам; channel = channelhigh; break; } state = ChannelStateInfo.ПостроенПоМинимумам; channel = channellow; break; case ChannelStateInfo.ПостроенПоМаксимумам: channel = GetLowLineChannel(candles, from); if (channel.a == new PointD(0, 0) || channel.b == new PointD(0, 0)) return; state = ChannelStateInfo.ПостроенПоМинимумам; break; case ChannelStateInfo.ПостроенПоМинимумам: channel = GetHighLineChannel(candles, from); if (channel.a == new PointD(0, 0) || channel.b == new PointD(0, 0)) return; state = ChannelStateInfo.ПостроенПоМаксимумам; break; } if (lastIndexB == 0 && lastIndexA == 0) { lastIndexA = channel.a.X; lastIndexB = channel.b.X; } if (lastIndexB == channel.b.X && lastIndexA < channel.a.X) { // найденный канал старше текущего, игнорируем return; } if (!ShowAllChannels && lastIndexB > channel.b.X) { // найденный канал уже старый, игнорируем его channel = currChannel; } var a = channel.a; var b = channel.b; var k = (b.Y - a.Y) / (b.X - a.X); // стираем старый канал if (!ShowAllChannels) series.data.Clear(); // находим точку на текущей свече + 1, туда и продлим канал var bx = 0; if (series.data.Count == 0) bx = candles.Count; else bx = (int)b.X + CountAfter + CountForward; if (TradePoints) { // просто покажем точки входа и все // посчитаем точки входа если они есть for (var i = (int)b.X + CountAfter + 1; i < b.X + CountAfter + CountForward; i++) { if (candles.Count <= i) break; var highY = b.Y + k*(i - b.X); var lowY = channel.c.Y + k*(i - channel.c.X); if (highY <= candles[i].high && highY >= candles[i].low) { var detailed = string.Format("S {0}", highY); var tip = new AsteriskTooltip(detailed, detailed) { Price = (float)highY, CandleIndex = i, DateStart = candles[i].timeOpen, Sign = "e", Shape = AsteriskTooltip.ShapeType.СтрелкаВниз, ColorFill = Color.Pink, ColorLine = Color.Black, ColorText = Color.Black }; commentSeries.data.Add(tip); break; } if (lowY >= candles[i].low && lowY <= candles[i].high) { var detailed = string.Format("B {0}", lowY); var tip = new AsteriskTooltip(detailed, detailed) { Price = (float)lowY, CandleIndex = i, DateStart = candles[i].timeOpen, Sign = "e", Shape = AsteriskTooltip.ShapeType.СтрелкаВверх, ColorFill = Color.Green, ColorLine = Color.Black, ColorText = Color.Black }; commentSeries.data.Add(tip); break; } } } else { var line = new TrendLine { LineColor = state == ChannelStateInfo.ПостроенПоМаксимумам ? ClUpLine : ClDownLine, LineStyle = TrendLine.TrendLineStyle.Отрезок }; // линия по двум экстремумам line.linePoints.Add(new PointD(a.X - countBefore, b.Y + k*(a.X - countBefore - b.X))); line.linePoints.Add(new PointD(bx, b.Y + k*(bx - b.X))); series.data.Add(line); line = new TrendLine { LineColor = state == ChannelStateInfo.ПостроенПоМаксимумам ? ClUpLine : ClDownLine, LineStyle = TrendLine.TrendLineStyle.Отрезок }; var c = channel.c; // линия по одному экстремуму line.linePoints.Add(new PointD(a.X - countBefore, c.Y + k*(a.X - countBefore - c.X))); line.linePoints.Add(new PointD(bx, c.Y + k*(bx - c.X))); series.data.Add(line); } ChannelState = state; currChannel = channel; lastIndexA = currChannel.a.X; lastIndexB = currChannel.b.X; }
/// <summary> /// результаты моделирования необходимо отобразить на графиках /// (в виде коментариев и т.д.) /// </summary> private void OnRobotResultsBoundToCharts(Dictionary<BaseRobot, ChartWindowSettings> robotBindings, List<RobotLogEntry> robotLogEntries, List<MarketOrder> posClosed, List<MarketOrder> posOpened) { // предложить убрать коментарии с графиков, // выбрать коментарии var dlg = new ResultDisplaySettingsForm(); if (dlg.ShowDialog() == DialogResult.Cancel) return; // убрать коментарии? var removeOldComments = dlg.RemoveOldMarkers; if (removeOldComments) { var chartsToPurge = Charts.Where(c => robotBindings.Any( rb => rb.Value.TabPageId == c.bookmarkId && rb.Value.Symbol == c.chart.Symbol && rb.Value.Timeframe == c.chart.timeframeString)); foreach (var chart in chartsToPurge) chart.chart.seriesAsteriks.data.Clear(); } foreach (var robotBinding in robotBindings) { // найти график для робота var timeframe = robotBinding.Value.Timeframe; var ticker = robotBinding.Value.Symbol; var pageId = robotBinding.Value.TabPageId; var robot = robotBinding.Key; var chart = Charts.First(c => c.chart.timeframeString == timeframe && c.chart.Symbol == ticker && c.bookmarkId == pageId); // выбрать все сообщения для графика var logEntries = robotLogEntries.Where(l => l.Robot == robot); var messagesPlainList = new List<string>(); foreach (var logEntry in logEntries) messagesPlainList.AddRange(logEntry.Messages); // добавить коментарии на график foreach (var msg in messagesPlainList) { var mark = RobotMark.ParseString(msg); if (mark == null || mark is RobotHint == false) continue; var hint = (RobotHint) mark; if (!hint.Price.HasValue || !hint.Time.HasValue) continue; AddOrRemoveRobotHintOnChart(hint, chart); } // добавить информацию о сделках if (!dlg.ShowMarkers) continue; // выбрать сделки для отображения var deals4Chart = posClosed.Where(p => p.Symbol == chart.chart.Symbol).ToList(); deals4Chart.AddRange(posOpened.Where(p => p.Symbol == chart.chart.Symbol)); foreach (var deal in deals4Chart) { // отметки входа if (dlg.ShowEnters) { var detailed = string.Format("#{0} {1} {2:dd.MM.yyyy HH:mm} по {3}", deal.ID, deal.Side > 0 ? "B" : "S", deal.TimeEnter, deal.PriceEnter); if (deal.IsClosed) { var result = (deal.PriceExit.Value - deal.PriceEnter) * deal.Side; var points = DalSpot.Instance.GetPointsValue(deal.Symbol, result); detailed += string.Format(" - {0:dd.MM.yyyy HH:mm} по {1}, {2:f0} пп", deal.TimeExit.Value, deal.PriceExit.Value, points); } var tip = new AsteriskTooltip(detailed, detailed) { Price = deal.PriceEnter, CandleIndex = chart.chart.chart.StockSeries.GetIndexByCandleOpen(deal.TimeEnter), DateStart = deal.TimeEnter, Sign = "e", Shape = deal.Side > 0 ? AsteriskTooltip.ShapeType.СтрелкаВверх : AsteriskTooltip.ShapeType.СтрелкаВниз, ColorFill = deal.Side > 0 ? Color.Green : Color.Salmon, ColorLine = Color.Black, ColorText = Color.Black, Radius = 5 }; if (!dlg.ShowDetailedEnters) tip.Name = string.Format("Сделка #{0}, {1}", deal.ID, deal.Side > 0 ? "Buy" : "Sell"); chart.chart.seriesAsteriks.data.Add(tip); } if (dlg.ShowExits && deal.IsClosed) { var detailed = string.Format("Выход #{0} {1} {2:dd.MM.yyyy HH:mm} по {3} - {4:dd.MM.yyyy HH:mm} по {5}", deal.ID, deal.Side > 0 ? "B" : "S", deal.TimeEnter, deal.PriceEnter, deal.TimeExit.Value, deal.PriceExit.Value); var tip = new AsteriskTooltip(detailed, detailed) { Price = deal.PriceExit.Value, CandleIndex = chart.chart.chart.StockSeries.GetIndexByCandleOpen( deal.TimeExit.Value), DateStart = deal.TimeExit.Value, Sign = "q", Shape = AsteriskTooltip.ShapeType.Квадрат, ColorFill = deal.Side > 0 ? Color.Green : Color.Salmon, ColorLine = Color.Black, ColorText = Color.Black, Radius = 5 }; if (!dlg.ShowDetailedExits) tip.Name = string.Format("Закрытие #{0}, {1}", deal.ID, deal.Side > 0 ? "Buy" : "Sell"); chart.chart.seriesAsteriks.data.Add(tip); } } } }
private void AddRobotHintOnChart(RobotHint hint, ChartForm chart) { // добавить отрезочек с комментарием if (hint.RobotHintType == RobotMark.HintType.Линия) { AddRobotHintLineOnChart(hint, chart); return; } // добавить звездочку var toolTip = new AsteriskTooltip(hint.Title, hint.Text) { Owner = chart.chart.seriesAsteriks, Price = hint.Price.Value, CandleIndex = chart.chart.chart.StockSeries.GetIndexByCandleOpen( hint.Time.Value), DateStart = hint.Time.Value, Sign = hint.Sign, Radius = 5, Shape = hint.RobotHintType == RobotMark.HintType.Стоп ? AsteriskTooltip.ShapeType.Квадрат : hint.RobotHintType == RobotMark.HintType.Тейк ? AsteriskTooltip.ShapeType.Квадрат : hint.RobotHintType == RobotMark.HintType.Покупка ? AsteriskTooltip.ShapeType.СтрелкаВверх : hint.RobotHintType == RobotMark.HintType.Продажа ? AsteriskTooltip.ShapeType.СтрелкаВниз : hint.RobotHintType == RobotMark.HintType.Поджатие ? AsteriskTooltip.ShapeType.Звезда : AsteriskTooltip.ShapeType.Круг }; if (!string.IsNullOrEmpty(hint.HintCode)) toolTip.Name = hint.HintCode; if (hint.ColorFill.HasValue) toolTip.ColorFill = hint.ColorFill.Value; if (hint.ColorLine.HasValue) toolTip.ColorLine = hint.ColorLine.Value; if (hint.ColorText.HasValue) toolTip.ColorText = hint.ColorText.Value; chart.chart.seriesAsteriks.data.Add(toolTip); }
private void ShowRobotDataOnChart() { var orders = MarketOrdersStorage.Instance.MarketOrders.Where(o => o.Symbol == chart.Symbol && o.Magic == selectedBot.Magic).ToList(); // показать ордера на графике foreach (var order in orders) { var candleIndex = (int)Math.Round(chart.chart.StockSeries.GetDoubleIndexByTime(order.TimeEnter)); var shape = order.Side > 0 ? AsteriskTooltip.ShapeType.СтрелкаВверх : AsteriskTooltip.ShapeType.СтрелкаВниз; var objectExists = chart.seriesAsteriks.data.Any(a => a.Price.RoughCompares(order.PriceEnter, 0.00001f) && a.CandleIndex == candleIndex && a.Shape == shape); if (objectExists) continue; var asterisk = new AsteriskTooltip(order.Side < 0 ? "SELL" : "BUY", selectedBot.GetUniqueName() + ": " + order.ToStringShort()) { ColorFill = order.Side < 0 ? colorSell : colorBuy, Price = order.PriceEnter, CandleIndex = candleIndex, DateStart = order.TimeEnter, Shape = shape, Sign = order.Side > 0 ? "b" : "s", Owner = chart.seriesAsteriks }; chart.seriesAsteriks.data.Add(asterisk); } AddChartCommentOnRobotState(orders.Count); }