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