public override List <string> OnQuotesReceived(string[] names, CandleDataBidAsk[] quotes, bool isHistoryStartOff)
        {
            var events = new List <string>();

            #region получить candle из quote
            if (string.IsNullOrEmpty(ticker))
            {
                return(events);
            }
            var tickerIndex = -1;
            for (var i = 0; i < names.Length; i++)
            {
                if (names[i] == ticker)
                {
                    tickerIndex = i;
                    break;
                }
            }
            if (tickerIndex < 0)
            {
                return(events);
            }
            var quote = quotes[tickerIndex];

            var candle = packer.UpdateCandle(quote);
            if (candle == null)
            {
                return(events);
            }
            #endregion

            extremumRangeQueue.Add(candle);
            if (extremumRangeQueue.Length != extremumRangeQueue.MaxQueueLength)
            {
                return(events);
            }


            //  Если очередь заполнена полностью, значит можно проверять её среднее значение на локальный экстремум
            CheckExtremum(extremumRangeQueue);

            //  Все ли 4 экстремума сейчас имеются для анализа
            if (extremumQueue.Length != extremumQueue.MaxQueueLength)
            {
                return(events);
            }

            var extrArray = extremumQueue.ToArray();
            //  Теперь можно проверить - чередуются ли экстремумы.
            var boofSign = !extrArray[0].b;
            foreach (var extremum in extrArray)
            {
                if (extremum.b == boofSign)
                {
                    return(events); // Это значит что два максимума или два минимума идут друг за другом
                }
                boofSign = !boofSign;
            }

            //  Экстремумы чередуются друг за другом.
            //  Теперь проверяем, больше ли по модулю "а", чем "b", "с" и "d" и close.
            var priceA      = extrArray[0].a;
            var currentSide = extrArray[0].b ? 1 : -1;

            for (var i = 1; i < extrArray.Length; i++)
            {
                if (currentSide != Math.Sign(priceA - extrArray[i].a))
                {
                    return(events);
                }
            }
            if (currentSide != Math.Sign(priceA - candle.close))
            {
                return(events);
            }

            // соотнесение "плеч"
            var ab     = Math.Abs(extrArray[0].a - extrArray[1].a);
            var bc     = Math.Abs(extrArray[1].a - extrArray[2].a);
            var cd     = Math.Abs(extrArray[2].a - extrArray[3].a);
            var dClose = Math.Abs(extrArray[3].a - candle.close);

            var bcCd = cd == 0 ? float.MaxValue : bc / cd;
            if (bcCd < MinShoulder || bcCd > MaxShoulder)
            {
                return(events);
            }
            if (ab <= bc || ab <= cd || ab <= dClose)
            {
                return(events);
            }

            //  Первый экстремум является большим по модулю, чем остальные - можно совершать сделку
            if (!isHistoryStartOff) // линии скользящих средних пересеклись причём не на истории. Значит пора торговать!!!
            {
                // коментарий
                var extrNames = new [] { "A", "B", "C", "D" };
                var nameIndex = 0;
                // ReSharper disable LoopCanBeConvertedToQuery
                foreach (var extr in extrArray)
                // ReSharper restore LoopCanBeConvertedToQuery
                {
                    events.Add(new RobotHint(ticker,
                                             Graphics[0].b.ToString(),
                                             extrNames[nameIndex],
                                             extrNames[nameIndex],
                                             extrNames[nameIndex++], extr.a)
                    {
                        Time          = extr.c,
                        RobotHintType = RobotHint.HintType.Линия         //extr.b ? RobotHint.HintType.Покупка : RobotHint.HintType.Продажа
                    }.ToString());
                }

                // закрыть противонаправленные, если автозакрытие включено пользователем
                if (CloseDeal)
                {
                    List <MarketOrder> orders;
                    robotContext.GetMarketOrders(robotContext.AccountInfo.ID, out orders);
                    foreach (
                        var order in
                        orders.Where(o => o.Side != currentSide && o.Magic == Magic && o.Symbol == ticker).ToList())
                    {
                        robotContext.SendCloseRequest(protectedContext.MakeProtectedContext(),
                                                      robotContext.AccountInfo.ID, order.ID, PositionExitReason.ClosedByRobot);
                    }
                }

                OpenDeal(candle.close, currentSide);
            }
            return(events);
        }