示例#1
0
        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();
            }
        }
示例#3
0
        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);
        }