示例#1
0
        private Task HandleStpOrderbook(ExternalExchangeOrderbookMessage message)
        {
            var orderbook = _convertService.Convert <ExternalExchangeOrderbookMessage, ExternalOrderBook>(message);

            _externalOrderbookService.SetOrderbook(orderbook);
            return(Task.CompletedTask);
        }
示例#2
0
        public Task SetQuote(ExternalExchangeOrderbookMessage orderBookMessage)
        {
            var isDayOff       = _assetPairDayOffService.IsDayOff(orderBookMessage.AssetPairId);
            var isEodOrderbook = orderBookMessage.ExchangeName == ExternalOrderbookService.EodExternalExchange;

            // we should process normal orderbook only if asset is currently tradable
            if (isDayOff && !isEodOrderbook)
            {
                return(Task.CompletedTask);
            }

            // and process EOD orderbook only if asset is currently not tradable
            if (!isDayOff && isEodOrderbook)
            {
                _log.WriteWarning("EOD FX quotes processing", "",
                                  $"EOD FX quote for {orderBookMessage.AssetPairId} is skipped, because instrument is within trading hours");

                return(Task.CompletedTask);
            }

            var bidAskPair = CreatePair(orderBookMessage);

            if (bidAskPair == null)
            {
                return(Task.CompletedTask);
            }

            SetQuote(bidAskPair);

            _fxBestPriceChangeEventChannel.SendEvent(this, new FxBestPriceChangeEventArgs(bidAskPair));

            return(Task.CompletedTask);
        }
        private void WriteStats(ExternalExchangeOrderbookMessage orderbook,
                                ExternalExchangeOrderbookMessage resultingOrderbook, DateTime now)
        {
            var bestPrices          = _bestPricesService.Calc(orderbook);
            var resultingBestPrices = _bestPricesService.Calc(resultingOrderbook);

            _orderbooksStatusService.SetStatus(orderbook.ExchangeName, orderbook.AssetPairId,
                                               new OrderbookStatus(bestPrices.BestBid, bestPrices.BestAsk,
                                                                   resultingBestPrices.BestBid, resultingBestPrices.BestAsk,
                                                                   orderbook.Bids.Count, orderbook.Asks.Count,
                                                                   resultingOrderbook.Bids.Count, resultingOrderbook.Asks.Count,
                                                                   now, OrderbookStatusEnum.Valid));

            Trace.Write(TraceLevelGroupEnum.Trace, orderbook.AssetPairId,
                        $"Orderbook from {orderbook.ExchangeName} for {resultingOrderbook.AssetPairId}: " +
                        $"{resultingBestPrices.BestBid}/{resultingBestPrices.BestAsk}",
                        new
            {
                ResultingAssetPairId = resultingOrderbook.AssetPairId,
                Event  = "ExternalOrderbookProcessed",
                Source = orderbook.ExchangeName,
                bestPrices.BestBid,
                bestPrices.BestAsk,
                ResultingBestBid    = resultingBestPrices.BestBid,
                ResultingBestAsk    = resultingBestPrices.BestAsk,
                BidsDepth           = orderbook.Bids.Count,
                AsksDepth           = orderbook.Asks.Count,
                ResultingsBidsDepth = resultingOrderbook.Bids.Count,
                ResultingsAsksDepth = resultingOrderbook.Asks.Count,
            });
        }
        public Task ProcessNewExternalOrderbookAsync(ExternalExchangeOrderbookMessage orderbook)
        {
            if (string.IsNullOrEmpty(orderbook.AssetPairId) || string.IsNullOrEmpty(orderbook.ExchangeName))
            {
                return(Task.CompletedTask);
            }

            var settings = _settingsService.TryGetAssetPair(orderbook.ExchangeName, orderbook.AssetPairId);

            if (settings == null || (orderbook.Bids?.Count ?? 0) == 0 || (orderbook.Asks?.Count ?? 0) == 0)
            {
                return(Task.CompletedTask);
            }

            orderbook.Bids.RemoveAll(e => e == null || e.Price <= 0 || e.Volume == 0);
            orderbook.Asks.RemoveAll(e => e == null || e.Price <= 0 || e.Volume == 0);
            orderbook.Bids.Sort((a, b) => - a.Price.CompareTo(b.Price));
            orderbook.Asks.Sort((a, b) => a.Price.CompareTo(b.Price));

            var now = _system.UtcNow;
            var externalOrderbook = new ExternalExchangeOrderbookMessage
            {
                AssetPairId  = settings.ResultingAssetPairId,
                ExchangeName = orderbook.ExchangeName,
                Timestamp    = now,
                Bids         = GetOrderbookPositions(orderbook.Bids, settings.Markups.BidMultiplier),
                Asks         = GetOrderbookPositions(orderbook.Asks, settings.Markups.AskMultiplier)
            };

            _watchdogService.OnOrderbookArrived(orderbook.ExchangeName, orderbook.AssetPairId, now);
            WriteStats(orderbook, externalOrderbook, now);
            return(_messageProducer.ProduceAsync(externalOrderbook));
        }
        public Task ProcessNewExternalOrderbookAsync(ExternalExchangeOrderbookMessage orderbook)
        {
            var quotesSource = _assetPairSourceTypeService.Get(orderbook.AssetPairId);

            if (quotesSource != AssetPairQuotesSourceTypeDomainEnum.External ||
                (orderbook.Bids?.Count ?? 0) == 0 || (orderbook.Asks?.Count ?? 0) == 0)
            {
                return(Task.CompletedTask);
            }

            var externalOrderbook = new ExternalOrderbook(orderbook.AssetPairId, orderbook.Source, _system.UtcNow,
                                                          orderbook.Bids.OrderByDescending(p => p.Price).Select(b => new OrderbookPosition(b.Price, b.Volume))
                                                          .ToImmutableArray(),
                                                          orderbook.Asks.OrderBy(p => p.Price).Select(b => new OrderbookPosition(b.Price, b.Volume))
                                                          .ToImmutableArray());
            var resultingOrderbook = _generateOrderbookService.OnNewOrderbook(externalOrderbook);

            if (resultingOrderbook == null)
            {
                return(Task.CompletedTask);
            }

            var orderbooksToSend = _crossRatesService.CalcDependentOrderbooks(resultingOrderbook)
                                   .Add(resultingOrderbook);

            return(SendOrderCommandsAsync(orderbooksToSend));
        }
示例#6
0
 public static ExternalOrderBook ToDomain(this ExternalExchangeOrderbookMessage orderBookMessage, DateTime now)
 {
     return(new ExternalOrderBook
     {
         ExchangeName = orderBookMessage.ExchangeName,
         AssetPairId = orderBookMessage.AssetPairId,
         Timestamp = orderBookMessage.Timestamp,
         ReceiveTimestamp = now,
         Asks = orderBookMessage.Asks.ToDomain(),
         Bids = orderBookMessage.Bids.ToDomain(),
     });
 }
        public async Task IfResultingOrderbookIsNull_ShouldSkip()
        {
            //arrange
            var incomingMessage = new ExternalExchangeOrderbookMessage
            {
                AssetPairId = "pair",
                Bids        = new List <VolumePrice>
                {
                    new VolumePrice {
                        Price = 1, Volume = 2
                    },
                    new VolumePrice {
                        Price = 3, Volume = 4
                    }
                },
                Asks = new List <VolumePrice>
                {
                    new VolumePrice {
                        Price = 5, Volume = 6
                    },
                    new VolumePrice {
                        Price = 7, Volume = 8
                    }
                },
                Source = "source",
            };

            var externalOrderbook = new ExternalOrderbook("pair", "source", _now,
                                                          ImmutableArray.Create(new OrderbookPosition(1, 2), new OrderbookPosition(3, 4)),
                                                          ImmutableArray.Create(new OrderbookPosition(5, 6), new OrderbookPosition(7, 8)));

            _testSuit
            .Setup <IAssetPairSourceTypeService>(s =>
                                                 s.Get("pair") == AssetPairQuotesSourceTypeDomainEnum.External)
            .Setup <ISystem>(s => s.UtcNow == _now)
            .Setup <IGenerateOrderbookService>(s => s.OnNewOrderbook(externalOrderbook.Equivalent()) == null);


            //act
            await _testSuit.Sut.ProcessNewExternalOrderbookAsync(incomingMessage);

            //assert
            _sentMessages.Should().BeEmpty();
        }
示例#8
0
        private InstrumentBidAskPair CreatePair(ExternalExchangeOrderbookMessage message)
        {
            if (!ValidateOrderbook(message))
            {
                return(null);
            }

            var ask = GetBestPrice(true, message.Asks);
            var bid = GetBestPrice(false, message.Bids);

            return(ask == null || bid == null
                ? null
                : new InstrumentBidAskPair
            {
                Instrument = message.AssetPairId,
                Date = message.Timestamp,
                Ask = ask.Value,
                Bid = bid.Value
            });
        }
        public async Task IfBidsNull_ShouldSkip()
        {
            //arrange
            _testSuit.Setup <IAssetPairSourceTypeService>(s =>
                                                          s.Get("pair") == AssetPairQuotesSourceTypeDomainEnum.External);

            var incomingMessage = new ExternalExchangeOrderbookMessage
            {
                AssetPairId = "pair",
                Asks        = new List <VolumePrice> {
                    new VolumePrice()
                },
                Bids   = null,
                Source = "source",
            };

            //act
            await _testSuit.Sut.ProcessNewExternalOrderbookAsync(incomingMessage);

            //assert
            _sentMessages.Should().BeEmpty();
        }
示例#10
0
        private bool ValidateOrderbook(ExternalExchangeOrderbookMessage orderbook)
        {
            try
            {
                orderbook.AssetPairId.RequiredNotNullOrWhiteSpace("orderbook.AssetPairId");
                orderbook.ExchangeName.RequiredNotNullOrWhiteSpace("orderbook.ExchangeName");
                orderbook.RequiredNotNull(nameof(orderbook));

                orderbook.Bids.RequiredNotNullOrEmpty("orderbook.Bids");
                orderbook.Bids.RemoveAll(e => e == null || e.Price <= 0 || e.Volume == 0);
                orderbook.Bids.RequiredNotNullOrEmptyEnumerable("orderbook.Bids");

                orderbook.Asks.RequiredNotNullOrEmpty("orderbook.Asks");
                orderbook.Asks.RemoveAll(e => e == null || e.Price <= 0 || e.Volume == 0);
                orderbook.Asks.RequiredNotNullOrEmptyEnumerable("orderbook.Asks");

                return(true);
            }
            catch (Exception e)
            {
                _log.WriteError(nameof(ExternalExchangeOrderbookMessage), orderbook.ToJson(), e);
                return(false);
            }
        }
 public BestPrices Calc(ExternalExchangeOrderbookMessage orderbook)
 {
     // note: assume prices sorted best first
     return(new BestPrices(orderbook.Bids.First().Price, orderbook.Asks.First().Price));
 }
        public async Task IfResultingOrderbookNotNull_ShouldSendItWithDependentOnes()
        {
            //arrange
            var incomingMessage = new ExternalExchangeOrderbookMessage
            {
                AssetPairId = "pair",
                Bids        = new List <VolumePrice>
                {
                    new VolumePrice {
                        Price = 1, Volume = 2
                    },
                    new VolumePrice {
                        Price = 3, Volume = 4
                    }
                },
                Asks = new List <VolumePrice>
                {
                    new VolumePrice {
                        Price = 5, Volume = 6
                    },
                    new VolumePrice {
                        Price = 7, Volume = 8
                    }
                },
                Source = "source",
            };

            var externalOrderbook = new ExternalOrderbook("pair", "source", _now,
                                                          ImmutableArray.Create(new OrderbookPosition(1, 2), new OrderbookPosition(3, 4)),
                                                          ImmutableArray.Create(new OrderbookPosition(5, 6), new OrderbookPosition(7, 8)));

            var dependentOrderbooks = ImmutableList.Create(
                new Orderbook("dependent pair 1",
                              ImmutableArray.Create(new OrderbookPosition(11, 12), new OrderbookPosition(13, 14)),
                              ImmutableArray.Create(new OrderbookPosition(15, 16), new OrderbookPosition(17, 18))),
                new Orderbook("dependent pair 2",
                              ImmutableArray.Create(new OrderbookPosition(21, 22), new OrderbookPosition(23, 24)),
                              ImmutableArray.Create(new OrderbookPosition(25, 26), new OrderbookPosition(27, 28))));
            var resultingOrderbook = new Orderbook("resulting pair",
                                                   ImmutableArray.Create(new OrderbookPosition(31, 32), new OrderbookPosition(33, 34)),
                                                   ImmutableArray.Create(new OrderbookPosition(35, 36), new OrderbookPosition(37, 38)));

            _testSuit
            .Setup <IAssetPairSourceTypeService>(s =>
                                                 s.Get("pair") == AssetPairQuotesSourceTypeDomainEnum.External)
            .Setup <ISystem>(s => s.UtcNow == _now)
            .Setup <IGenerateOrderbookService>(s =>
                                               s.OnNewOrderbook(externalOrderbook.Equivalent()) == resultingOrderbook)
            .Setup <ICrossRatesService>(s => s.CalcDependentOrderbooks(resultingOrderbook) == dependentOrderbooks)
            .Setup <IReloadingManager <MarginTradingMarketMakerSettings> >(s =>
                                                                           s.CurrentValue == new MarginTradingMarketMakerSettings {
                MarketMakerId = "mm id"
            })
            .Setup <IPriceRoundingService>(m => m.Setup(s => s.GetRoundFunc(It.IsNotNull <string>())).Returns(p => p));


            //act
            await _testSuit.Sut.ProcessNewExternalOrderbookAsync(incomingMessage);

            //assert
            var expectation = new List <OrderCommandsBatchMessage>
            {
                MakeOrderCommandsBatchMessage("dependent pair 1", 10),
                MakeOrderCommandsBatchMessage("dependent pair 2", 20),
                MakeOrderCommandsBatchMessage("resulting pair", 30),
            };

            _sentMessages.Should().BeEquivalentTo(expectation, o => o.WithAutoConversion());
        }