private void ProcessCandle(Candle candle) { try { //Добавляем новую свечку для расчетов в индикаторы //Добавляем до проверки актуальности данных, так как возможна предзагрузка исторических данных var shortSma = _shortSma.Add(candle.ClosePrice); var longSma = _longSma.Add(candle.ClosePrice); //Проверка актуальности данных if (!GetRealTimeData()) { return; } //Если индикаторы не сформированы, то ничего не делаем if (!_shortSma.IsFormed || !_longSma.IsFormed) { return; } //Рассчитываем положение скользящих стредних друг относительно друга var stateSma = shortSma - longSma; //Текущая позиция по выбранному коннектору и инструменту var position = GetTradeInfo().Position; //Если короткая SMA больше или равна длинной SMA //и предыдущее положение было обратным, то короткая пересекла длинную SMA - Покупаем! if (stateSma >= 0 && _lastSma < 0 && position <= 0) { var limitOrder = new Order { Type = OrderType.Limit, Direction = Direction.Buy, Volume = position < 0 ? -position : _volume, //Закрываем полностью позицию или открываем новую Price = candle.ClosePrice }; //Подписываемся на событие изменения заявки limitOrder.OrderChanged += order => { //Текущая позиция по выбранному коннектору и инструменту var pos = GetTradeInfo().Position; //Если заявка исполнилась, то защищаем ее алгоритмической стоп заявкой if (order.State == OrderState.Filled && pos != 0) { var stopOrderId = _algoStopOrders.Create(order, order.Price - _stopOrderOffset, order.Price - _stopOrderOffset - _offset, _isTrailing); MessageToLog($"Algo stop order ID{stopOrderId} - stop price {order.Price - _stopOrderOffset}, order price {order.Price - _stopOrderOffset - _offset}"); } //Обрабатываем алгоритмические заявки CheckAlgoOrders(order, pos); }; //Создаем защитную алгоритмическую заявку на снятие лимитной заявки по таймеру var cancelOrderId = _algoCancelOrdersByTimer.Create(limitOrder); MessageToLog($"Algo cancel by timer ID{cancelOrderId} - waiting time {_algoCancelOrdersByTimer.Timer.TotalSeconds} sec"); //Отправляем лимитную заявку на регистрацию RegisterOrder(limitOrder); } //Если короткая SMA меньше или равна длинной SMA //и предыдущее положение было обратным, то длинная пересекла короткую SMA - Продаем! if (stateSma <= 0 && _lastSma > 0 && position >= 0) { var limitOrder = new Order() { Type = OrderType.Limit, Direction = Direction.Sell, Volume = position < 0 ? -position : _volume, Price = candle.ClosePrice }; //Подписываемся на событие изменения заявки limitOrder.OrderChanged += order => { //Текущая позиция по выбранному коннектору и инструменту var pos = GetTradeInfo().Position; //Если заявка исполнилась, то защищаем ее алгоритмической стоп заявкой if (order.State == OrderState.Filled && pos != 0) { var stopOrderId = _algoStopOrders.Create(order, order.Price + _stopOrderOffset, order.Price + _stopOrderOffset + _offset, _isTrailing); MessageToLog($"Algo stop order ID{stopOrderId} - stop price {order.Price + _stopOrderOffset}, order price {order.Price + _stopOrderOffset + _offset}"); } //Обрабатываем алгоритмические заявки CheckAlgoOrders(order, pos); }; //Создаем защитную алгоритмическую заявку на снятие лимитной заявки по таймеру var cancelOrderId = _algoCancelOrdersByTimer.Create(limitOrder); MessageToLog($"Algo cancel by timer ID{cancelOrderId} - waiting time {_algoCancelOrdersByTimer.Timer.TotalSeconds} sec"); //Отправляем лимитную заявку на регистрацию RegisterOrder(limitOrder); } //Запоминаем текущее положение SMA относительно друг друга _lastSma = stateSma; } catch (Exception ex) { ExceptionMessage(ex, "ProcessCandle"); } }
public void ProcessCandle(Candle candle) { try { //Добавляем новую свечку для расчетов в индикаторы //Добавляем до проверки актуальности данных, так как возможна предзагрузка исторических данных var upperChannelOne = _upperChannelOne.Add(candle); var lowerChannelOne = _lowerChannelOne.Add(candle); var upperChannelTwo = _upperChannelTwo.Add(candle); var lowerChannelTwo = _lowerChannelTwo.Add(candle); var adxLookBack = _adxLookBack.Add(candle); var adxThreshold = _adxThreshold.Add(candle); var atr = _atr.Add(candle); //Проверка актуальности данных if (!GetRealTimeData()) { return; } //Если индикаторы еще не сформировались, то ждем пока сформируются if (!_upperChannelOne.IsFormed || !_lowerChannelOne.IsFormed || !_upperChannelTwo.IsFormed || !_lowerChannelTwo.IsFormed || !_adxLookBack.IsFormed || !_adxThreshold.IsFormed || !_atr.IsFormed) { return; } //Фильтр. Торгуем только если ADX_Look_Back > ADX_Threshold. if (adxLookBack <= adxThreshold) { return; } //Текущая позиция по выбранному коннектору и инструменту var position = GetTradeInfo().Position; //Берем цену последней сделки как цену для заявки var price = HistoricalDataType == HistoricalDataType.Candles ? GetLastCandle().ClosePrice : GetLastTick().Price; //Входим в позицию. Покупаем, когда цена касается канала UpperChannelOne. if (candle.HighPrice >= upperChannelOne && position == 0) { var order = new Order { Type = OrderType.Limit, Direction = Direction.Buy, Volume = _volume, Price = price + _offset }; //Подписываемся на событие изменения заявки order.OrderChanged += newOrder => { //Текущая позиция по выбранному коннектору и инструменту var pos = GetTradeInfo().Position; //Если заявка исполнилась и мы вошли в позицию, то защищаем ее алгоритмической стоп заявкой if (newOrder.State == OrderState.Filled && pos != 0) { var stopPrice = newOrder.Price - atr * _atrStop; var stopOrderId = _algoStopOrders.Create(order, stopPrice, stopPrice - _offset, false); MessageToLog($"Created algorithmic stop order ID{stopOrderId} - stop price {stopPrice}, order price {stopPrice - _offset}"); } }; //Отправляем лимитную заявку на регистрацию RegisterOrder(order); } //Входим в позицию. Продаем, когда цена касается канала LowerChannelOne. else if (candle.LowPrice <= lowerChannelOne && position == 0) { var order = new Order { Type = OrderType.Limit, Direction = Direction.Sell, Volume = _volume, Price = price - _offset }; //Подписываемся на событие изменения заявки order.OrderChanged += newOrder => { //Текущая позиция по выбранному коннектору и инструменту var pos = GetTradeInfo().Position; //Если заявка исполнилась и мы вошли в позицию, то защищаем ее Стоп-лимит заявкой if (newOrder.State == OrderState.Filled && pos != 0) { var stopPrice = newOrder.Price + atr * _atrStop; var stopOrderId = _algoStopOrders.Create(order, stopPrice, stopPrice + _offset, false); MessageToLog($"Create algorithmic stop order ID{stopOrderId} - stop price {stopPrice}, order price {stopPrice + _offset}"); } }; //Отправляем лимитную заявку на регистрацию RegisterOrder(order); } //Закрываем позиции. Продаем, когда цена касается канала LowerChannelTwo. else if (candle.LowPrice <= lowerChannelTwo && position > 0) { var limitOrder = new Order { Type = OrderType.Limit, Direction = Direction.Sell, Volume = Math.Abs(position), Price = price - _offset }; //Снимаем все лимитные заявки CancelOrders(); //Снимаем все алгоритмические стоп заявки _algoStopOrders.CancelAllOrders(); //Отправляем лимитную заявку на регистрацию RegisterOrder(limitOrder); } //Закрываем позиции. Покупаем, когда цена касается канала UpperChannelTwo. else if (candle.HighPrice >= upperChannelTwo && position < 0) { var limitOrder = new Order { Type = OrderType.Limit, Direction = Direction.Buy, Volume = Math.Abs(position), Price = price + _offset }; //Снимаем все лимитные заявки CancelOrders(); //Снимаем все алгоритмические стоп заявки _algoStopOrders.CancelAllOrders(); //Отправляем лимитную заявку на регистрацию RegisterOrder(limitOrder); } } catch (Exception ex) { ExceptionMessage(ex, "ProcessCandle"); } }