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