public async Task UpdateAsync(string exchange, string assetPair, DateTime timestamp,
                                      IReadOnlyList <OrderBookLevel> sellLevels, IReadOnlyList <OrderBookLevel> buyLevels)
        {
            await _semaphore.WaitAsync();

            try
            {
                ExchangeSettings exchangeSettings = await _exchangeSettingsService.GetByNameAsync(exchange);

                if (exchangeSettings == null || exchangeSettings.Status != ExchangeStatus.Active)
                {
                    return;
                }

                if (!exchangeSettings.Instruments.Contains(assetPair))
                {
                    return;
                }

                await _externalOrderBookService.UpdateAsync(exchange, assetPair, timestamp, sellLevels, buyLevels);

                await Task.WhenAll(
                    _aggregatedOrderBookService.UpdateAsync(assetPair),
                    _internalOrderBookService.UpdateAsync(assetPair));
            }
            catch (Exception exception)
            {
                _log.WarningWithDetails("An error occurred while calculating order books", exception,
                                        new { exchange, assetPair, timestamp, sellLevels, buyLevels });
            }
            finally
            {
                _semaphore.Release();
            }
        }
        public async Task UpdateAsync(string exchange, string assetPair, DateTime timestamp,
                                      IReadOnlyList <OrderBookLevel> sellLevels, IReadOnlyList <OrderBookLevel> buyLevels)
        {
            ExchangeSettings exchangeSettings = await _exchangeSettingsService.GetByNameAsync(exchange);

            if (exchangeSettings == null)
            {
                throw new FailedOperationException("Exchange setting not found");
            }

            AssetPairModel assetPairSettings = _marketInstrumentService.GetAssetPair(assetPair, exchange);

            if (assetPairSettings == null)
            {
                throw new FailedOperationException("Asset pair not found");
            }

            var externalOrderBook = new ExternalOrderBook
            {
                Exchange   = exchange,
                AssetPair  = assetPair,
                Timestamp  = timestamp,
                SellLevels = sellLevels.Select(o => new ExternalOrderBookLevel
                {
                    Price = (o.Price * (1 + exchangeSettings.MarketFee + exchangeSettings.TransactionFee))
                            .TruncateDecimalPlaces(assetPairSettings.PriceAccuracy, true),
                    Volume        = o.Volume,
                    Markup        = exchangeSettings.MarketFee + exchangeSettings.TransactionFee,
                    OriginalPrice = o.Price
                })
                             .OrderBy(o => o.Price)
                             .ToList(),
                BuyLevels = buyLevels.Select(o => new ExternalOrderBookLevel
                {
                    Price = (o.Price * (1 - exchangeSettings.MarketFee - exchangeSettings.TransactionFee))
                            .TruncateDecimalPlaces(assetPairSettings.PriceAccuracy),
                    Volume        = o.Volume,
                    Markup        = exchangeSettings.MarketFee + exchangeSettings.TransactionFee,
                    OriginalPrice = o.Price
                })
                            .OrderByDescending(o => o.Price)
                            .ToList()
            };

            lock (_sync)
            {
                if (!_orderBooks.ContainsKey(exchange))
                {
                    _orderBooks[exchange] = new Dictionary <string, ExternalOrderBook>();
                }

                _orderBooks[exchange][assetPair] = externalOrderBook;
            }
        }
        public async Task <ExchangeSettingsModel> GetByNameAsync(string name)
        {
            ExchangeSettings settings = await _exchangeSettingsService.GetByNameAsync(name);

            return(Mapper.Map <ExchangeSettingsModel>(settings));
        }
        private async Task ExecuteMarketOrderAsync(MarketOrder marketOrder)
        {
            AggregatedOrderBook aggregatedOrderBook = _aggregatedOrderBookService.GetByAssetPair(marketOrder.AssetPair);

            if (aggregatedOrderBook == null)
            {
                _log.WarningWithDetails("Can not execute market order the aggregated order book does not exist",
                                        marketOrder);
                return;
            }

            IReadOnlyList <ExternalLimitOrder> externalLimitOrders =
                await _externalLimitOrderService.GetByParentIdAsync(marketOrder.Id);

            List <ExternalLimitOrder> activeLimitOrders = externalLimitOrders
                                                          .Where(o => o.Status == ExternalLimitOrderStatus.Active)
                                                          .ToList();

            decimal executedVolume = externalLimitOrders
                                     .Where(o => o.Status == ExternalLimitOrderStatus.Filled ||
                                            o.Status == ExternalLimitOrderStatus.PartiallyFilled)
                                     .Sum(o => o.Volume);

            decimal remainingVolume = marketOrder.Volume - executedVolume;

            bool rerouteFailedLimitOrders;

            var excludedExchanges = new List <string>();

            do
            {
                rerouteFailedLimitOrders = false;

                IReadOnlyList <ExchangeVolume> volumes;

                if (marketOrder.Type == OrderType.Sell)
                {
                    volumes = aggregatedOrderBook.GetBuyVolumes(remainingVolume, activeLimitOrders, excludedExchanges);
                }
                else
                {
                    volumes = aggregatedOrderBook.GetSellVolumes(remainingVolume, activeLimitOrders, excludedExchanges);
                }

                if (volumes.Count == 0)
                {
                    _log.WarningWithDetails("Can not execute market order no liquidity in aggregated order book",
                                            new
                    {
                        MarketOrder     = marketOrder,
                        RemainingVolume = remainingVolume,
                        ActiveVolume    = activeLimitOrders.Sum(o => o.Volume)
                    });
                    break;
                }

                foreach (ExchangeVolume exchangeVolume in volumes)
                {
                    ExchangeSettings exchangeSettings =
                        await _exchangeSettingsService.GetByNameAsync(exchangeVolume.Exchange);

                    decimal price;

                    if (marketOrder.Type == OrderType.Sell)
                    {
                        price = exchangeVolume.Price * (1 - exchangeSettings.SlippageMarkup);
                    }
                    else
                    {
                        price = exchangeVolume.Price * (1 + exchangeSettings.SlippageMarkup);
                    }

                    try
                    {
                        ExternalLimitOrder externalLimitOrder =
                            await _exchangeService.CreateLimitOrderAsync(exchangeVolume.Exchange, marketOrder.AssetPair,
                                                                         price, exchangeVolume.Volume, marketOrder.Type);

                        _log.InfoWithDetails("External limit order created", externalLimitOrder);

                        await _externalLimitOrderService.AddAsync(marketOrder.Id, externalLimitOrder);

                        activeLimitOrders.Add(externalLimitOrder);
                    }
                    catch (Exception exception)
                    {
                        rerouteFailedLimitOrders = true;

                        excludedExchanges.Add(exchangeSettings.Name);

                        _log.ErrorWithDetails(exception, "An error occurred while creating external limit order",
                                              new
                        {
                            MarketOrderId = marketOrder.Id,
                            exchangeVolume.Exchange,
                            marketOrder.AssetPair,
                            Price = price,
                            exchangeVolume.Volume,
                            OrderType = marketOrder.Type.ToString()
                        });
                    }
                }
            } while (rerouteFailedLimitOrders);
        }