private async Task ProcessTradeAsync(MtTradeMessage message)
        {
            // Just discarding trades with negative or zero prices and\or volumes.
            if (message.Price <= 0 ||
                message.Volume <= 0)
            {
                await _log.WriteWarningAsync(nameof(ProcessTradeAsync), message.ToJson(), "Got an MT trade with non-positive price or volume value.");

                return;
            }

            var quotingVolume = (double)(message.Volume * message.Price);

            var trade = new Trade(
                message.AssetPairId,
                message.Date,
                (double)message.Volume,
                quotingVolume,
                (double)message.Price);

            await _candlesManager.ProcessTradeAsync(trade);
        }
        private async Task ProcessLimitTradesAsync(LimitOrdersMessage message)
        {
            if (message.Orders == null || !message.Orders.Any())
            {
                return;
            }

            var limitOrderIds = message.Orders
                                .Select(o => o.Order.Id)
                                .ToHashSet();

            foreach (var orderMessage in message.Orders)
            {
                if (orderMessage.Trades == null)
                {
                    continue;
                }

                var assetPair = await _assetPairsManager.TryGetEnabledPairAsync(orderMessage.Order.AssetPairId);

                foreach (var tradeMessage in orderMessage.Trades.OrderBy(t => t.Timestamp).ThenBy(t => t.Index))
                {
                    // If both orders of the trade are limit, then both of them should be contained in the single message,
                    // this is by design.

                    var isOppositeOrderIsLimit = limitOrderIds.Contains(tradeMessage.OppositeOrderId);

                    // If opposite order is market order, then unconditionally takes the given limit order.
                    // But if both of orders are limit orders, we should take only one of them.

                    if (isOppositeOrderIsLimit)
                    {
                        var isBuyOrder = orderMessage.Order.Volume > 0;

                        // Takes trade only for the sell limit orders

                        if (isBuyOrder)
                        {
                            continue;
                        }
                    }

                    // Volumes in the asset pair base and quoting assets
                    double baseVolume;
                    double quotingVolume;

                    if (tradeMessage.Asset == assetPair.BaseAssetId)
                    {
                        baseVolume    = tradeMessage.Volume;
                        quotingVolume = tradeMessage.OppositeVolume;
                    }
                    else
                    {
                        baseVolume    = tradeMessage.OppositeVolume;
                        quotingVolume = tradeMessage.Volume;
                    }

                    // Just discarding trades with negative prices and\or volumes.  It's better to do it here instead of
                    // at the first line of foreach 'case we have some additional trade selection logic in the begining.
                    // ReSharper disable once InvertIf
                    if (tradeMessage.Price > 0 && baseVolume > 0 && quotingVolume > 0)
                    {
                        var trade = new Trade(
                            orderMessage.Order.AssetPairId,
                            tradeMessage.Timestamp,
                            baseVolume,
                            quotingVolume,
                            tradeMessage.Price
                            );

                        await _candlesManager.ProcessTradeAsync(trade);
                    }
                    else
                    {
                        await _log.WriteWarningAsync(nameof(ProcessLimitTradesAsync), tradeMessage.ToJson(), "Got a Spot trade with non-positive price or volume value.");
                    }
                }
            }
        }