private async Task ProcessMessageAsync(OrderBookUpdateReport orderBookUpdateReport) { //_log.InfoWithDetails("OrderBookUpdateReport is about to be saved...", orderBookUpdateReport); await _orderBookUpdateReportRepository.InsertAsync(orderBookUpdateReport); //_log.InfoWithDetails("OrderBookUpdateReport was saved.", orderBookUpdateReport); }
public async Task InsertAsync(OrderBookUpdateReport orderBookUpdateReport) { OrderBookUpdateReportEntity entity = Mapper.Map <OrderBookUpdateReportEntity>(orderBookUpdateReport); using (DataContext context = _connectionFactory.CreateDataContext()) { await context.OrderBookUpdateReports.AddAsync(entity); await context.SaveChangesAsync(); } }
private async Task <OrderBook> CalculateDirectOrderBookAsync(Instrument instrument, DateTime iterationDateTime) { Quote[] quotes = _b2C2OrderBookService.GetQuotes(instrument.AssetPairId); if (quotes == null || quotes.Length != 2) { _log.WarningWithDetails("No quotes for instrument", instrument.AssetPairId); return(null); } Balance baseAssetBalance = null; Balance quoteAssetBalance = null; TimeSpan timeSinceLastTrade = TimeSpan.Zero; if (instrument.AllowSmartMarkup) { AssetPairLink assetPairLink = await _assetPairLinkService.GetByInternalAssetPairIdAsync(instrument.AssetPairId); if (assetPairLink != null && !assetPairLink.IsEmpty()) { baseAssetBalance = await _balanceService.GetByAssetIdAsync(ExchangeNames.B2C2, assetPairLink.ExternalBaseAssetId); quoteAssetBalance = await _balanceService.GetByAssetIdAsync(ExchangeNames.B2C2, assetPairLink.ExternalQuoteAssetId); timeSinceLastTrade = DateTime.UtcNow - _tradeService.GetLastInternalTradeTime(instrument.AssetPairId); } else { _log.WarningWithDetails("The asset pair link does not configured", new { instrument.AssetPairId }); } } AssetPair assetPair = await _assetsServiceWithCache.TryGetAssetPairAsync(instrument.AssetPairId); Asset baseAsset = await _assetsServiceWithCache.TryGetAssetAsync(assetPair.BaseAssetId); MarketMakerSettings marketMakerSettings = await _marketMakerSettingsService.GetAsync(); decimal globalMarkup = marketMakerSettings.LimitOrderPriceMarkup; decimal noQuotesMarkup = await _noFreshQuotesStopLossService.GetNoFreshQuotesMarkup(assetPair.Id); decimal pnLStopLossMarkup = await _pnLStopLossEngineService.GetTotalMarkupByAssetPairIdAsync(assetPair.Id); decimal fiatEquityStopLossMarkup = await _fiatEquityStopLossService.GetFiatEquityMarkup(assetPair.Id); _log.InfoWithDetails("Arguments for Calculator.CalculateLimitOrders(...).", new { instrument.AssetPairId, quotes, levels = instrument.Levels.ToArray(), baseAmountBalance = baseAssetBalance?.Amount ?? 0, quoteAmountBalance = quoteAssetBalance?.Amount ?? 0, timeSinceLastTradeTotalSeconds = (int)timeSinceLastTrade.TotalSeconds, instrumentHalfLifePeriod = instrument.HalfLifePeriod, instrumentAllowSmartMarkup = instrument.AllowSmartMarkup, marketMakerSettingsLimitOrderPriceMarkup = globalMarkup, pnLStopLossMarkup, fiatEquityStopLossMarkup, noQuotesMarkup, assetPairAccuracy = assetPair.Accuracy, baseAssetAccuracy = baseAsset.Accuracy, instrument }); OrderBookUpdateReport orderBookUpdateReport = null; if (_isOrderBooksUpdateReportEnabled) { orderBookUpdateReport = new OrderBookUpdateReport(iterationDateTime); orderBookUpdateReport.AssetPair = instrument.AssetPairId; orderBookUpdateReport.FirstQuoteAsk = quotes[0].Ask; orderBookUpdateReport.FirstQuoteBid = quotes[0].Bid; orderBookUpdateReport.SecondQuoteAsk = quotes[1].Ask; orderBookUpdateReport.SecondQuoteBid = quotes[1].Bid; orderBookUpdateReport.QuoteDateTime = quotes[0].Time; orderBookUpdateReport.GlobalMarkup = globalMarkup; orderBookUpdateReport.NoFreshQuoteMarkup = noQuotesMarkup; orderBookUpdateReport.PnLStopLossMarkup = pnLStopLossMarkup; orderBookUpdateReport.FiatEquityMarkup = fiatEquityStopLossMarkup; } IReadOnlyCollection <LimitOrder> limitOrders = Calculator.CalculateLimitOrders( quotes[0], quotes[1], instrument.Levels.ToArray(), baseAssetBalance?.Amount ?? 0, quoteAssetBalance?.Amount ?? 0, (int)timeSinceLastTrade.TotalSeconds, instrument.HalfLifePeriod, instrument.AllowSmartMarkup, globalMarkup, pnLStopLossMarkup, fiatEquityStopLossMarkup, noQuotesMarkup, assetPair.Accuracy, baseAsset.Accuracy, orderBookUpdateReport); await ValidateQuoteTimeoutAsync(limitOrders, quotes[0]); await ValidateQuoteTimeoutAsync(limitOrders, quotes[1]); ValidateMinVolume(limitOrders, (decimal)assetPair.MinVolume); await ValidatePriceAsync(limitOrders); await ValidateBalanceAsync(limitOrders, assetPair); WriteInfoLog(instrument.AssetPairId, quotes, timeSinceLastTrade, limitOrders, "Direct limit orders calculated"); if (orderBookUpdateReport != null) { await _orderBooksUpdatesReportPublisher.PublishAsync(orderBookUpdateReport); } return(new OrderBook { AssetPairId = instrument.AssetPairId, Time = DateTime.UtcNow, LimitOrders = limitOrders, IsDirect = true }); }
public Task PublishAsync(OrderBookUpdateReport orderBookUpdateReport) { return(_publisher.ProduceAsync(orderBookUpdateReport)); }
/// <summary> /// Creates dynamically distributed limit orders using price levels of an instrument. /// </summary> /// <param name="quote1">Best prices of instrument on first level.</param> /// <param name="quote2">Best prices of instrument on second level.</param> /// <param name="levels">A collection of price levels.</param> /// <param name="baseAssetBalance">The amount of the base asset on a hedge exchange.</param> /// <param name="quoteAssetBalance">The amount of the quote asset on a hedge exchange.</param> /// <param name="timeSinceLastTrade">The total seconds from last trade in order book.</param> /// <param name="halfLifePeriod">The half life period in seconds.</param> /// <param name="allowSmartMarkup">If <c>true</c> then smart markup for first level will be applied; otherwise default markup.</param> /// <param name="globalMarkup">Global markup.</param> /// <param name="pnLStopLossMarkup">Total pnl stop loss markup.</param> /// <param name="fiatEquityStopLossMarkup">Fiat equity stop loss markup.</param> /// <param name="noFreshQuotesStopLossMarkup">No fresh quotes stop loss markup.</param> /// <param name="priceAccuracy">The accuracy of price.</param> /// <param name="volumeAccuracy">The accuracy of volume.</param> /// <param name="orderBookUpdateReport">OrderBooksUpdateReport.</param> /// <returns>A collection of limit orders.</returns> public static IReadOnlyCollection <LimitOrder> CalculateLimitOrders( Quote quote1, Quote quote2, InstrumentLevel[] levels, decimal baseAssetBalance, decimal quoteAssetBalance, int timeSinceLastTrade, int halfLifePeriod, bool allowSmartMarkup, decimal globalMarkup, decimal pnLStopLossMarkup, decimal fiatEquityStopLossMarkup, decimal noFreshQuotesStopLossMarkup, int priceAccuracy, int volumeAccuracy, OrderBookUpdateReport orderBookUpdateReport) { var limitOrders = new List <LimitOrder>(); if (levels.Length == 0) { return(limitOrders); } decimal sumSideVolume = levels.Sum(o => o.Volume); decimal deltaVolume = sumSideVolume - levels[0].Volume; decimal sellDeltaPrice = (quote2.Ask - quote1.Ask) / quote1.Ask; decimal buyDeltaPrice = (quote1.Bid - quote2.Bid) / quote1.Bid; decimal sellMarketPrice = quote1.Ask; decimal sellRawPrice = quote1.Ask; decimal buyMarketPrice = quote1.Bid; decimal buyRawPrice = quote1.Bid; decimal sumVolume = levels[0].Volume; decimal sellFirstLevelMarkup = levels[0].Markup; decimal buyFirstLevelMarkup = levels[0].Markup; if (allowSmartMarkup) { double alpha = Math.Log(2) / halfLifePeriod; decimal relativeSpread = (quote1.Ask - quote1.Bid) / quote1.Mid; decimal smartMarkup = levels[0].Markup - relativeSpread / 2m + relativeSpread * (decimal)Math.Exp(-alpha * timeSinceLastTrade); if (-baseAssetBalance * quote1.Mid >= quoteAssetBalance) { sellFirstLevelMarkup = smartMarkup; } else { buyFirstLevelMarkup = smartMarkup; } } decimal commonMarkup = globalMarkup + pnLStopLossMarkup + noFreshQuotesStopLossMarkup; decimal sellFirstLevelPrice = (sellRawPrice * (1 + sellFirstLevelMarkup + commonMarkup + fiatEquityStopLossMarkup)) .TruncateDecimalPlaces(priceAccuracy, true); LimitOrder sellFirstLevelLimitOrder = LimitOrder.CreateSell(sellFirstLevelPrice, Math.Round(levels[0].Volume, volumeAccuracy)); if (fiatEquityStopLossMarkup != decimal.MinusOne) // don't create sell orders if 'FiatEquityThresholdTo' exceeded { limitOrders.Add(sellFirstLevelLimitOrder); } decimal buyFirstLevelPrice = (buyRawPrice * (1 - (buyFirstLevelMarkup + commonMarkup))) .TruncateDecimalPlaces(priceAccuracy); LimitOrder buyFirstLevelLimitOrder = LimitOrder.CreateBuy(buyFirstLevelPrice, Math.Round(levels[0].Volume, volumeAccuracy)); limitOrders.Add(buyFirstLevelLimitOrder); if (orderBookUpdateReport != null) { orderBookUpdateReport.Orders.Add( new PlacedOrderReport(sellFirstLevelLimitOrder, sellFirstLevelMarkup, orderBookUpdateReport.Id)); orderBookUpdateReport.Orders.Add( new PlacedOrderReport(buyFirstLevelLimitOrder, buyFirstLevelMarkup, orderBookUpdateReport.Id)); } for (int i = 1; i < levels.Length; i++) { decimal prevSellMarketPrice = sellMarketPrice; decimal prevBuyMarketPrice = buyMarketPrice; decimal prevSumVolume = sumVolume; sumVolume += levels[i].Volume; sellMarketPrice = (1 + (sumVolume - levels[0].Volume) / deltaVolume * sellDeltaPrice) * quote1.Ask; buyMarketPrice = (1 - (sumVolume - levels[0].Volume) / deltaVolume * buyDeltaPrice) * quote1.Bid; sellRawPrice = (sellMarketPrice * sumVolume - prevSellMarketPrice * prevSumVolume) / levels[i].Volume; buyRawPrice = (buyMarketPrice * sumVolume - prevBuyMarketPrice * prevSumVolume) / levels[i].Volume; decimal buyLevelMarkup = 1 - (levels[i].Markup + commonMarkup); decimal buyPrice = (buyRawPrice * buyLevelMarkup) .TruncateDecimalPlaces(priceAccuracy); LimitOrder buyLimitOrder = LimitOrder.CreateBuy(Math.Min(buyFirstLevelPrice, buyPrice), Math.Round(levels[i].Volume, volumeAccuracy)); limitOrders.Add(buyLimitOrder); if (fiatEquityStopLossMarkup == decimal.MinusOne) // don't create sell orders if 'FiatEquityThresholdTo' exceeded { continue; } decimal sellLevelMarkup = 1 + levels[i].Markup + commonMarkup + fiatEquityStopLossMarkup; decimal sellPrice = (sellRawPrice * sellLevelMarkup) .TruncateDecimalPlaces(priceAccuracy, true); LimitOrder sellLimitOrder = LimitOrder.CreateSell(Math.Max(sellFirstLevelPrice, sellPrice), Math.Round(levels[i].Volume, volumeAccuracy)); limitOrders.Add(sellLimitOrder); if (orderBookUpdateReport != null) { orderBookUpdateReport.Orders.Add( new PlacedOrderReport(buyLimitOrder, buyLevelMarkup, orderBookUpdateReport.Id)); orderBookUpdateReport.Orders.Add( new PlacedOrderReport(sellLimitOrder, sellLevelMarkup, orderBookUpdateReport.Id)); } } return(limitOrders); }