private async Task ProcessMessageAsync(LimitOrders arg) { try { var start = DateTime.UtcNow; var trades = await _tradesConverter.ConvertAsync(arg); if (trades == null || trades.Count == 0) { return; } await _tradesPublisher.PublishAsync(trades); if (DateTime.UtcNow.Subtract(start) > TimeSpan.FromSeconds(10)) { await _log.WriteWarningAsync( nameof(LimitOrdersSubscriberForPublishing), nameof(ProcessMessageAsync), $"Long processing: {arg.ToJson()}"); } } catch (Exception ex) { await _log.WriteErrorAsync(nameof(LimitOrdersSubscriberForPublishing), nameof(ProcessMessageAsync), ex); throw; } }
private void HandleDeletedLimitOrder(ILimitOrder order) { order.UnRegisterDeleteNotificationHandler(HandleDeletedLimitOrder); order.UnRegisterFilledNotification(HandleDeletedLimitOrder); LimitOrders.Remove(order.ExchangeOrderId); ClientOrders[order.ClientId].Remove(order); }
void client_LimitOrderAccepted(object sender, LimitOrderDto e) { UiDispatcher.Dispatcher.Invoke(() => { LimitOrders.Add(new LimitOrderViewModel(e, client)); }); }
public void HandleMessage(LimitOrders message) { lock (_gate) { foreach (var order in message.Orders) { if (order.Order == null) { continue; } if (!_instrumentIndex.ContainsKey(order.Order.AssetPairId)) { _instrumentIndex[order.Order.AssetPairId] = new Stat(order.Order.AssetPairId); } UpdateStat(_instrumentIndex[order.Order.AssetPairId], order); if (!_clientIndex.ContainsKey(order.Order.ClientId)) { _clientIndex[order.Order.ClientId] = new Stat(order.Order.ClientId); } UpdateStat(_clientIndex[order.Order.ClientId], order); } } }
public ILimitOrder TryGetLimitOrder(uint exchangeOrderId) { if (!LimitOrders.ContainsKey(exchangeOrderId)) { return(null); } return(LimitOrders[exchangeOrderId]); }
void client_LimitOrderDeleted(object sender, LimitOrderDto e) { UiDispatcher.Dispatcher.Invoke(() => { var order = LimitOrders.FirstOrDefault(a => a.OrderId == e.ExchangeOrderId); if (order == null) { return; } LimitOrders.Remove(order); order.Dispose(); }); }
void client_LimitOrderSnapshot(object sender, System.Collections.Generic.List <LimitOrderDto> e) { UiDispatcher.Dispatcher.Invoke(() => { LimitOrders.Clear(); foreach (var limitOrderViewModel in LimitOrders) { LimitOrders.Remove(limitOrderViewModel); limitOrderViewModel.Dispose(); } foreach (var limitOrder in e) { LimitOrders.Add(new LimitOrderViewModel(limitOrder, client)); } }); }
private async Task ProcessMessageAsync(LimitOrders limitOrders) { try { if (limitOrders.Orders == null || limitOrders.Orders.Count == 0) { return; } string walletId = _settingsService.GetWalletId(); if (string.IsNullOrEmpty(walletId)) { return; } if (limitOrders.Orders.Any(o => o.Order?.ClientId == walletId)) { _log.Info("Message received", context: $"data: {limitOrders.ToJson()}"); } List <LimitOrderWithTrades> clientLimitOrders = limitOrders.Orders .Where(o => o.Order?.ClientId == walletId) .Where(o => o.Trades?.Count > 0) .ToList(); if (!clientLimitOrders.Any()) { return; } _log.Info("LimitOrders received", context: $"{clientLimitOrders.ToJson()}"); IReadOnlyCollection <Trade> trades = ExtractTrades(clientLimitOrders); if (trades.Any()) { await _lykkeTradeService.HandleAsync(trades); } } catch (Exception exception) { _log.Error(exception, "An error occurred during processing trades", limitOrders); } }
public ILimitOrder NewLimitOrder(string symbol, int clientId, double price, int quantity, WayEnum way) { if (!ClientOrders.ContainsKey(clientId)) { ClientOrders.Add(clientId, new EditableList <ILimitOrder>()); } ILimitOrder toReturn = new LimitOrder(symbol, quantity, price, way, clientId); toReturn.SetExchangeOrderId(globalItemCounter); LimitOrders.Add(toReturn.ExchangeOrderId, toReturn); ClientOrders[clientId].Add(toReturn); toReturn.RegisterDeleteNotificationHandler(HandleDeletedLimitOrder); toReturn.RegisterFilledNotification(HandleDeletedLimitOrder); globalItemCounter++; return(toReturn); }
private static bool MatchedWithMarketOrder(LimitOrders orders) { var result = false; foreach (var order in orders.Orders) { if (order.Trades == null || order.Trades.Count == 0) { continue; } if (orders.Orders.All( x => x.Trades == null || x.Trades.Count == 0 || x.Trades.All(y => y.OppositeOrderId != order.Order.Id))) { result = true; } } return(result); }
public async Task Publish(LimitOrders limitOrders) { if (limitOrders?.Orders == null || !limitOrders.Orders.Any()) { return; } var idsMappings = new Dictionary <string, string>(); var ordersByClients = new Dictionary <string, List <LimitOrderWithTrades> >(); foreach (var order in limitOrders.Orders) { var walletId = order.Order.ClientId; if (!idsMappings.ContainsKey(walletId)) { idsMappings[walletId] = await _clientAccountClient.GetClientByWalletAsync(walletId); } var clientId = idsMappings[walletId]; if (!ordersByClients.ContainsKey(clientId)) { ordersByClients[clientId] = new List <LimitOrderWithTrades>(); } ordersByClients[clientId].Add(order); } foreach (var clientId in ordersByClients.Keys) { var orders = await ordersByClients[clientId].SelectAsync(async x => await _ordersConverter.ConvertAsync(x.Order, x.Trades.Any())); PublishOrdersToClient(clientId, orders.ToArray()); } }
public async Task LimitOrderBuy() // and CancelLimitOrder { AccountEntity testAccount = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); Assert.NotNull(testAccount); BalanceDTO accountBalance = testAccount.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); Assert.NotNull(accountBalance); string limitOrderID = Guid.NewGuid().ToString(); string badLimitOrderID = Guid.NewGuid().ToString(); double amount = 0.01; double price = Helpers.Random.NextDouble() / 100; //Attempt bad buy MeResponseModel badOrderResponse = await this.Consumer.Client.PlaceLimitOrderAsync( badLimitOrderID, this.TestAccountId1, this.TestAssetPair.Id, OrderAction.Buy, accountBalance.Balance + 10, 2); Assert.False(badOrderResponse.Status == MeStatusCodes.Ok); LimitOrdersResponse badMessage = (LimitOrdersResponse)await this.WaitForRabbitMQ <LimitOrdersResponse>( o => o.orders.Any(m => m.order.externalId == badLimitOrderID && m.order.status == "NotEnoughFunds")); Assert.NotNull(badMessage); LimitOrders badSubMessage = badMessage.orders.Where(m => m.order.externalId == badLimitOrderID && m.order.status == "NotEnoughFunds").FirstOrDefault(); Assert.NotNull(badSubMessage); Assert.True(badSubMessage.order.clientId == this.TestAccountId1); Assert.True(badSubMessage.order.assetPairId == this.TestAssetPair.Id); Assert.True(badSubMessage.order.volume == accountBalance.Balance + 10); //Assert.True(badSubMessage.order.price == price); //Attempt buy stored in order book MeResponseModel LimitOrderResponse = await this.Consumer.Client.PlaceLimitOrderAsync( limitOrderID, this.TestAccountId1, this.TestAssetPair.Id, OrderAction.Buy, amount, price); Assert.True(LimitOrderResponse.Status == MeStatusCodes.Ok); AccountEntity checkTestAccount = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); BalanceDTO checkAccountBalance = checkTestAccount.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); Assert.True(Math.Round(checkAccountBalance.Reserved, this.AssetPrecission) == Math.Round(accountBalance.Reserved + amount, this.AssetPrecission)); LimitOrdersResponse message = (LimitOrdersResponse)await this.WaitForRabbitMQ <LimitOrdersResponse>( o => o.orders.Any(m => m.order.externalId == limitOrderID && m.order.status == "InOrderBook")); Assert.NotNull(message); LimitOrders subMessage = message.orders.Where(m => m.order.externalId == limitOrderID && m.order.status == "InOrderBook").FirstOrDefault(); Assert.NotNull(subMessage); Assert.True(subMessage.order.clientId == this.TestAccountId1); Assert.True(subMessage.order.assetPairId == this.TestAssetPair.Id); Assert.True(subMessage.order.volume == amount); Assert.True(subMessage.order.price == price); //Cancel proper buy MeResponseModel LimitOrderCancelResponse = await this.Consumer.Client.CancelLimitOrderAsync(limitOrderID); Assert.True(LimitOrderCancelResponse.Status == MeStatusCodes.Ok); checkTestAccount = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); checkAccountBalance = checkTestAccount.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); Assert.True(checkAccountBalance.Reserved == accountBalance.Reserved); LimitOrdersResponse cancelMessage = (LimitOrdersResponse)await this.WaitForRabbitMQ <LimitOrdersResponse>( o => o.orders.Any(m => m.order.externalId == limitOrderID && m.order.status == "Cancelled")); Assert.NotNull(cancelMessage); LimitOrders cancelSubMessage = cancelMessage.orders.Where(m => m.order.externalId == limitOrderID && m.order.status == "Cancelled").FirstOrDefault(); Assert.NotNull(cancelSubMessage); Assert.True(cancelSubMessage.order.clientId == this.TestAccountId1); Assert.True(cancelSubMessage.order.assetPairId == this.TestAssetPair.Id); Assert.True(cancelSubMessage.order.volume == amount); Assert.True(cancelSubMessage.order.price == price); }
public async Task LimitOrderSell() // and CancelLimitOrder { AccountEntity testAccount = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); Assert.NotNull(testAccount); BalanceDTO accountBalance = testAccount.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.NotNull(accountBalance); string limitOrderID = Guid.NewGuid().ToString(); string badLimitOrderID = Guid.NewGuid().ToString(); double amount = 0.2; double price = Helpers.Random.Next(100, 999); double matchedPrice = Helpers.Random.NextDouble(); //Attempt bad sell MeResponseModel badOrderResponse = await this.Consumer.Client.PlaceLimitOrderAsync( badLimitOrderID, this.TestAccountId1, this.TestAssetPair.Id, OrderAction.Sell, accountBalance.Balance + 10, price); Assert.False(badOrderResponse.Status == MeStatusCodes.Ok); LimitOrdersResponse badMessage = (LimitOrdersResponse)await this.WaitForRabbitMQ <LimitOrdersResponse>( o => o.orders.Any(m => m.order.externalId == badLimitOrderID && m.order.status == "NotEnoughFunds")); Assert.NotNull(badMessage); LimitOrders badSubMessage = badMessage.orders.Where(m => m.order.externalId == badLimitOrderID && m.order.status == "NotEnoughFunds").FirstOrDefault(); Assert.NotNull(badSubMessage); Assert.True(badSubMessage.order.clientId == this.TestAccountId1); Assert.True(badSubMessage.order.assetPairId == this.TestAssetPair.Id); Assert.True(badSubMessage.order.volume == (accountBalance.Balance + 10) * -1); Assert.True(badSubMessage.order.price == price); //Attempt sell stored in order book MeResponseModel LimitOrderResponse = await this.Consumer.Client.PlaceLimitOrderAsync( limitOrderID, this.TestAccountId1, this.TestAssetPair.Id, OrderAction.Sell, amount, price); Assert.True(LimitOrderResponse.Status == MeStatusCodes.Ok); AccountEntity checkTestAccount = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); BalanceDTO checkAccountBalance = checkTestAccount.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.True(Math.Round(checkAccountBalance.Reserved, this.AssetPrecission) == Math.Round(accountBalance.Reserved + amount, this.AssetPrecission)); LimitOrdersResponse message = (LimitOrdersResponse)await this.WaitForRabbitMQ <LimitOrdersResponse>( o => o.orders.Any(m => m.order.externalId == limitOrderID && m.order.status == "InOrderBook")); Assert.NotNull(message); LimitOrders subMessage = message.orders.Where(m => m.order.externalId == limitOrderID && m.order.status == "InOrderBook").FirstOrDefault(); Assert.NotNull(subMessage); Assert.True(subMessage.order.clientId == this.TestAccountId1); Assert.True(subMessage.order.assetPairId == this.TestAssetPair.Id); Assert.True(subMessage.order.volume == amount * -1); Assert.True(subMessage.order.price == price); //LimitOrderEntity limitOrderFromDB = (LimitOrderEntity)await fixture.LimitOrdersRepository.TryGetAsync("Active_" + fixture.TestAccountId1, subMessage.order.id); //Assert.NotNull(limitOrderFromDB); //Assert.True(limitOrderFromDB.AssetPairId == fixture.TestAssetPair.Id); //Assert.True(limitOrderFromDB.Price == subMessage.order.price); //Assert.True(limitOrderFromDB.Status == subMessage.order.status); //Assert.True(limitOrderFromDB.Volume == amount); //Assert.True(limitOrderFromDB.RemainingVolume == subMessage.order.remainingVolume); //Assert.True(limitOrderFromDB.Straight == ); //Assert.True(limitOrderFromDB.MatchingId == subMessage.order.); //Cancel the proper sell MeResponseModel LimitOrderCancelResponse = await this.Consumer.Client.CancelLimitOrderAsync(limitOrderID); Assert.True(LimitOrderCancelResponse.Status == MeStatusCodes.Ok); checkTestAccount = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); checkAccountBalance = checkTestAccount.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.True(checkAccountBalance.Reserved == accountBalance.Reserved); LimitOrdersResponse cancelMessage = (LimitOrdersResponse)await this.WaitForRabbitMQ <LimitOrdersResponse>( o => o.orders.Any(m => m.order.externalId == limitOrderID && m.order.status == "Cancelled")); Assert.NotNull(cancelMessage); LimitOrders cancelSubMessage = cancelMessage.orders.Where(m => m.order.externalId == limitOrderID && m.order.status == "Cancelled").FirstOrDefault(); Assert.NotNull(cancelSubMessage); Assert.True(cancelSubMessage.order.clientId == this.TestAccountId1); Assert.True(cancelSubMessage.order.assetPairId == this.TestAssetPair.Id); Assert.True(cancelSubMessage.order.volume == amount * -1); Assert.True(cancelSubMessage.order.price == price); }
public async Task <List <Trade> > ConvertAsync(LimitOrders orders) { var result = new List <Trade>(); if (orders?.Orders == null || orders.Orders.Count == 0) { return(result); } var matchedWithMarketOrder = MatchedWithMarketOrder(orders); foreach (var order in orders.Orders) { if (order.Trades == null || order.Trades.Count == 0) { continue; } foreach (var trade in order.Trades) { if (result.Any(x => x.Id == trade.TradeId)) { continue; } var assetPair = await _assetsServiceWrapper.TryGetAssetPairAsync(trade.Asset, trade.OppositeAsset); var baseAsset = await _assetsServiceWrapper.TryGetAssetAsync(assetPair.BaseAssetId); var volume = assetPair != null ? (assetPair.BaseAssetId == trade.Asset ? trade.Volume : trade.OppositeVolume) : 0; TradeAction action; if (matchedWithMarketOrder) { action = order.Order.Volume > 0 ? TradeAction.Sell : TradeAction.Buy; } else { var oppositeOrder = orders.Orders.FirstOrDefault(x => x.Order.Id == trade.OppositeOrderId); var latestOrder = oppositeOrder.Order.CreatedAt <= order.Order.CreatedAt ? order.Order : oppositeOrder.Order; action = latestOrder.Volume > 0 ? TradeAction.Buy : TradeAction.Sell; } result.Add(new Trade { Id = trade.TradeId, DateTime = trade.Timestamp, Price = trade.Price, AssetPairId = assetPair?.Id, Volume = volume.Normalize(baseAsset), Index = trade.Index, Action = action }); } } return(result); }
private async Task ProcessMessageAsync(LimitOrders orders) { _ordersPublisher.Publish(orders); }
private async Task ProcessMessageAsync(LimitOrders arg) { _messageStatistic.HandleMessage(arg); await Task.CompletedTask; }