public async Task <CommandHandlingResult> Handle(ProcessLimitOrderCommand command, IEventPublisher eventPublisher) { var clientId = command.LimitOrder.Order.ClientId; if (!_trusted.ContainsKey(clientId)) { _trusted[clientId] = (await _clientAccountClient.IsTrustedAsync(clientId)).Value; } var isTrustedClient = _trusted[clientId]; var limitOrderExecutedEvent = new LimitOrderExecutedEvent { IsTrustedClient = isTrustedClient, LimitOrder = command.LimitOrder }; var tradesWerePerformed = command.LimitOrder.Trades != null && command.LimitOrder.Trades.Any(); if (!isTrustedClient) { // need previous order state for not trusted clients var prevOrderState = await _limitOrdersRepository.GetOrderAsync(command.LimitOrder.Order.ClientId, command.LimitOrder.Order.Id); var isImmediateTrade = tradesWerePerformed && command.LimitOrder.Trades.First().Timestamp == command.LimitOrder.Order.Registered; limitOrderExecutedEvent.HasPrevOrderState = prevOrderState != null && !isImmediateTrade; limitOrderExecutedEvent.PrevRemainingVolume = prevOrderState?.RemainingVolume; limitOrderExecutedEvent.Aggregated = AggregateSwaps(limitOrderExecutedEvent.LimitOrder.Trades); } await _limitOrdersRepository.CreateOrUpdateAsync(command.LimitOrder.Order); var status = (OrderStatus)Enum.Parse(typeof(OrderStatus), command.LimitOrder.Order.Status); // workaround: ME sends wrong status if (status == OrderStatus.Processing && !tradesWerePerformed) { status = OrderStatus.InOrderBook; } else if (status == OrderStatus.PartiallyMatched && !tradesWerePerformed) { status = OrderStatus.Placed; } if (status == OrderStatus.Processing || status == OrderStatus.PartiallyMatched || // new version of Processing status == OrderStatus.Matched || status == OrderStatus.Cancelled) { limitOrderExecutedEvent.Trades = await CreateTrades(command.LimitOrder); } eventPublisher.PublishEvent(limitOrderExecutedEvent); return(CommandHandlingResult.Ok()); }
public async Task <CommandHandlingResult> Handle(ProcessLimitOrderCommand command, IEventPublisher eventPublisher) { var sw = new Stopwatch(); sw.Start(); var stepWatch = new Stopwatch(); stepWatch.Start(); try { var clientId = command.LimitOrder.Order.ClientId; if (!_trusted.ContainsKey(clientId)) { _trusted[clientId] = (await _clientAccountClient.IsTrustedAsync(clientId)).Value; } _log.Info("LimitOrderProcessing", new { TxHandler = new { Step = "01. Check client account is trusted", Time = stepWatch.ElapsedMilliseconds } }); stepWatch.Restart(); var isTrustedClient = _trusted[clientId]; var limitOrderExecutedEvent = new LimitOrderExecutedEvent { IsTrustedClient = isTrustedClient, LimitOrder = command.LimitOrder }; var tradesWerePerformed = command.LimitOrder.Trades != null && command.LimitOrder.Trades.Any(); if (!isTrustedClient) { // need previous order state for not trusted clients var prevOrderState = await _limitOrdersRepository.GetOrderAsync(command.LimitOrder.Order.ClientId, command.LimitOrder.Order.Id); var isImmediateTrade = tradesWerePerformed && command.LimitOrder.Trades.First().Timestamp == command.LimitOrder.Order.Registered; limitOrderExecutedEvent.HasPrevOrderState = prevOrderState != null && !isImmediateTrade; limitOrderExecutedEvent.PrevRemainingVolume = prevOrderState?.RemainingVolume; limitOrderExecutedEvent.Aggregated = AggregateSwaps(limitOrderExecutedEvent.LimitOrder.Trades); _log.Info("LimitOrderProcessing", new { TxHandler = new { Step = "02. Get Previous order state for not trusted client", Time = stepWatch.ElapsedMilliseconds } }); stepWatch.Restart(); } if (!isTrustedClient) { await _limitOrdersRepository.CreateOrUpdateAsync(command.LimitOrder.Order); _log.Info("LimitOrderProcessing", new { TxHandler = new { Step = "03. Upsert limit order for not trusted client", Time = stepWatch.ElapsedMilliseconds } }); stepWatch.Restart(); } var status = (OrderStatus)Enum.Parse(typeof(OrderStatus), command.LimitOrder.Order.Status); // workaround: ME sends wrong status if (status == OrderStatus.Processing && !tradesWerePerformed) { status = OrderStatus.InOrderBook; } else if (status == OrderStatus.PartiallyMatched && !tradesWerePerformed) { status = OrderStatus.Placed; } if (status == OrderStatus.Processing || status == OrderStatus.PartiallyMatched || // new version of Processing status == OrderStatus.Matched || status == OrderStatus.Cancelled) { limitOrderExecutedEvent.Trades = await CreateTrades(command.LimitOrder); } _log.Info("LimitOrderProcessing", new { TxHandler = new { Step = "04. Create trades", Time = stepWatch.ElapsedMilliseconds } }); stepWatch.Restart(); eventPublisher.PublishEvent(limitOrderExecutedEvent); return(CommandHandlingResult.Ok()); } finally { sw.Stop(); _log.Info("Command execution time", context: new { TxHandler = new { Handler = nameof(LimitOrderCommandHandler), Command = nameof(ProcessLimitOrderCommand), Time = sw.ElapsedMilliseconds } }); } }
public async Task <bool> ProcessMessage(LimitQueueItem tradeItem) { var trusted = new Dictionary <string, bool>(); foreach (var limitOrderWithTrades in tradeItem.Orders) { try { var meOrder = limitOrderWithTrades.Order; if (!trusted.ContainsKey(meOrder.ClientId)) { trusted[meOrder.ClientId] = await _clientAccountsRepository.IsTrusted(meOrder.ClientId); } var isTrusted = trusted[meOrder.ClientId]; var aggregated = AggregateSwaps(limitOrderWithTrades.Trades); ILimitOrder prevOrderState = null; // need previous order state for not trusted clients if (!isTrusted) { prevOrderState = await _limitOrdersRepository.GetOrderAsync(meOrder.Id); } await _limitOrdersRepository.CreateOrUpdateAsync(meOrder); var status = (OrderStatus)Enum.Parse(typeof(OrderStatus), meOrder.Status); IClientTrade[] trades = null; if (status == OrderStatus.Processing || status == OrderStatus.Matched) { trades = await SaveTrades(limitOrderWithTrades); } // all code below is for untrusted users if (isTrusted) { continue; } await SaveTransactionAndContext(trades, aggregated, limitOrderWithTrades); switch (status) { case OrderStatus.InOrderBook: case OrderStatus.Cancelled: await CreateEvent(limitOrderWithTrades, status); break; case OrderStatus.Processing: case OrderStatus.Matched: if (prevOrderState == null) { await CreateEvent(limitOrderWithTrades, OrderStatus.InOrderBook); } await SendMoney(trades, aggregated, meOrder, status); break; case OrderStatus.Dust: case OrderStatus.NoLiquidity: case OrderStatus.NotEnoughFunds: case OrderStatus.ReservedVolumeGreaterThanBalance: case OrderStatus.UnknownAsset: case OrderStatus.LeadToNegativeSpread: await _log.WriteInfoAsync(nameof(LimitTradeQueue), nameof(ProcessMessage), limitOrderWithTrades.ToJson(), "order rejected"); break; default: throw new ArgumentOutOfRangeException(nameof(OrderStatus)); } if (status == OrderStatus.Cancelled || status == OrderStatus.Matched) { await ReturnRemainingVolume(limitOrderWithTrades); } await UpdateCache(meOrder); await SendPush(aggregated, meOrder, prevOrderState, status); } catch (Exception e) { await _log.WriteErrorAsync(nameof(LimitTradeQueue), nameof(ProcessMessage), limitOrderWithTrades.Order.ToJson(), e); } } return(true); }