private Task HandleStpOrderbook(ExternalExchangeOrderbookMessage message) { var orderbook = _convertService.Convert <ExternalExchangeOrderbookMessage, ExternalOrderBook>(message); _externalOrderbookService.SetOrderbook(orderbook); return(Task.CompletedTask); }
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)); }
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(); }
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(); }
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()); }