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());
        }
Beispiel #2
0
        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);
        }