private async Task Handle(ExecuteSpecialLiquidationOrderCommand command, IEventPublisher publisher) { var executionInfo = await _operationExecutionInfoRepository.GetAsync <SpecialLiquidationOperationData>( operationName : SpecialLiquidationSaga.OperationName, id : command.OperationId); if (executionInfo?.Data == null) { return; } if (executionInfo.Data.SwitchState(SpecialLiquidationOperationState.PriceReceived, SpecialLiquidationOperationState.ExternalOrderExecuted)) { if (command.Volume == 0) { publisher.PublishEvent(new SpecialLiquidationOrderExecutedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), MarketMakerId = "ZeroNetVolume", ExecutionTime = _dateService.Now(), OrderId = _identityGenerator.GenerateGuid(), }); } else { var order = new OrderModel( tradeType: command.Volume > 0 ? TradeType.Buy : TradeType.Sell, orderType: OrderType.Market.ToType <Lykke.Service.ExchangeConnector.Client.Models.OrderType>(), timeInForce: TimeInForce.FillOrKill, volume: (double)Math.Abs(command.Volume), dateTime: _dateService.Now(), exchangeName: executionInfo.Data.ExternalProviderId, instrument: command.Instrument, price: (double?)command.Price, orderId: _identityGenerator.GenerateAlphanumericId()); try { var executionResult = await _exchangeConnectorService.CreateOrderAsync(order); publisher.PublishEvent(new SpecialLiquidationOrderExecutedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), MarketMakerId = executionInfo.Data.ExternalProviderId, ExecutionTime = executionResult.Time, OrderId = executionResult.ExchangeOrderId, }); } catch (Exception exception) { publisher.PublishEvent(new SpecialLiquidationOrderExecutionFailedEvent { OperationId = command.OperationId, CreationTime = _dateService.Now(), Reason = exception.Message }); await _log.WriteWarningAsync(nameof(SpecialLiquidationCommandsHandler), nameof(ExecuteSpecialLiquidationOrderCommand), $"Failed to execute the order: {order.ToJson()}", exception); } } await _operationExecutionInfoRepository.Save(executionInfo); } }
//TODO: remove orderProcessed function and make all validations before match public void MatchMarketOrderForOpen(Order order, Func <MatchedOrderCollection, bool> orderProcessed) { var prices = _externalOrderBooksList.GetPricesForOpen(order); if (prices == null) { orderProcessed(new MatchedOrderCollection()); return; } prices = order.GetOrderType() == OrderDirection.Buy ? prices.OrderBy(tuple => tuple.price).ToList() : prices.OrderByDescending(tuple => tuple.price).ToList(); var assetPair = _assetPairsCache.GetAssetPairByIdOrDefault(order.Instrument); var externalAssetPair = assetPair?.BasePairId ?? order.Instrument; foreach (var sourcePrice in prices) { var externalOrderModel = new OrderModel(); try { externalOrderModel = new OrderModel( order.GetOrderType().ToType <TradeType>(), OrderType.Market, TimeInForce.FillOrKill, (double)Math.Abs(order.Volume), _dateService.Now(), sourcePrice.source, externalAssetPair); var executionResult = _exchangeConnectorService.CreateOrderAsync(externalOrderModel).GetAwaiter() .GetResult(); var executedPrice = Math.Abs(executionResult.Price) > 0 ? (decimal)executionResult.Price : sourcePrice.price.Value; var matchedOrders = new MatchedOrderCollection { new MatchedOrder { ClientId = order.ClientId, MarketMakerId = sourcePrice.source, MatchedDate = _dateService.Now(), OrderId = executionResult.ClientOrderId, Price = CalculatePriceWithMarkups(assetPair, order.GetOrderType(), executedPrice), Volume = (decimal)executionResult.Volume } }; order.OpenExternalProviderId = sourcePrice.source; order.OpenExternalOrderId = executionResult.ClientOrderId; _rabbitMqNotifyService.ExternalOrder(executionResult).GetAwaiter().GetResult(); if (orderProcessed(matchedOrders)) { return; } else { var cancelOrderModel = new OrderModel( order.GetCloseType().ToType <TradeType>(), OrderType.Market, TimeInForce.FillOrKill, (double)Math.Abs(order.Volume), _dateService.Now(), sourcePrice.source, externalAssetPair); var cancelOrderResult = _exchangeConnectorService.CreateOrderAsync(cancelOrderModel).GetAwaiter().GetResult(); _rabbitMqNotifyService.ExternalOrder(cancelOrderResult).GetAwaiter().GetResult(); } return; } catch (Exception e) { _log.WriteErrorAsync(nameof(StpMatchingEngine), nameof(MatchMarketOrderForOpen), $"Internal order: {order.ToJson()}, External order model: {externalOrderModel.ToJson()}", e); } } if (string.IsNullOrEmpty(order.OpenExternalProviderId) || string.IsNullOrEmpty(order.OpenExternalOrderId)) { order.Status = OrderStatus.Rejected; order.RejectReason = OrderRejectReason.NoLiquidity; order.RejectReasonText = "Error executing external order"; } }
public async Task <MatchedOrderCollection> MatchOrderAsync(Order order, bool shouldOpenNewPosition, OrderModality modality = OrderModality.Regular) { List <(string source, decimal?price)> prices = null; if (!string.IsNullOrEmpty(_marginTradingSettings.DefaultExternalExchangeId)) { var quote = _quoteCacheService.GetQuote(order.AssetPairId); if (quote.GetVolumeForOrderDirection(order.Direction) >= Math.Abs(order.Volume)) { prices = new List <(string source, decimal?price)> { (_marginTradingSettings .DefaultExternalExchangeId, quote.GetPriceForOrderDirection(order.Direction)) }; } } if (prices == null) { prices = _externalOrderbookService.GetOrderedPricesForExecution(order.AssetPairId, order.Volume, shouldOpenNewPosition); if (prices == null || !prices.Any()) { return(new MatchedOrderCollection()); } } var assetPair = _assetPairsCache.GetAssetPairByIdOrDefault(order.AssetPairId); var externalAssetPair = assetPair?.BasePairId ?? order.AssetPairId; foreach (var(source, price) in prices .Where(x => string.IsNullOrEmpty(order.ExternalProviderId) || x.source == order.ExternalProviderId)) { var externalOrderModel = new OrderModel(); var orderType = order.OrderType == Core.Orders.OrderType.Limit || order.OrderType == Core.Orders.OrderType.TakeProfit ? OrderType.Limit : OrderType.Market; var targetPrice = order.OrderType == Core.Orders.OrderType.Market ? (double?)price : (double?)order.Price; try { externalOrderModel = new OrderModel( tradeType: order.Direction.ToType <TradeType>(), orderType: orderType, timeInForce: TimeInForce.FillOrKill, volume: (double)Math.Abs(order.Volume), dateTime: _dateService.Now(), exchangeName: source, instrument: externalAssetPair, price: targetPrice, orderId: order.Id, modality: modality.ToType <TradeRequestModality>()); var cts = new CancellationTokenSource(); cts.CancelAfter(_marginTradingSettings.GavelTimeout); var executionResult = await _exchangeConnectorService.CreateOrderAsync(externalOrderModel, cts.Token); if (!executionResult.Success) { throw new Exception( $"External order was not executed. Status: {executionResult.ExecutionStatus}. Failure: {executionResult.FailureType}"); } var executedPrice = Math.Abs(executionResult.Price) > 0 ? (decimal)executionResult.Price : price.Value; var matchedOrders = new MatchedOrderCollection { new MatchedOrder { MarketMakerId = source, MatchedDate = _dateService.Now(), OrderId = executionResult.ExchangeOrderId, Price = CalculatePriceWithMarkups(assetPair, order.Direction, executedPrice), Volume = (decimal)executionResult.Volume, IsExternal = true } }; await _rabbitMqNotifyService.ExternalOrder(executionResult); _operationsLogService.AddLog("external order executed", order.AccountId, externalOrderModel.ToJson(), executionResult.ToJson()); return(matchedOrders); } catch (Exception e) { var connector = _marginTradingSettings.ExchangeConnector == ExchangeConnectorType.FakeExchangeConnector ? "Fake" : _exchangeConnectorService.BaseUri.OriginalString; _log.WriteError( $"{nameof(StpMatchingEngine)}:{nameof(MatchOrderAsync)}:{connector}", $"Internal order: {order.ToJson()}, External order model: {externalOrderModel.ToJson()}", e); } } return(new MatchedOrderCollection()); }