public async Task <IActionResult> PlaceBulkOrder([FromBody] PlaceBulkLimitOrderRequest request) { var walletId = User.GetWalletId(); var items = request.Orders?.ToArray() ?? Array.Empty <BulkOrderItemModel>(); var orders = new List <MultiOrderItemModel>(); foreach (var item in items) { var order = new MultiOrderItemModel { Id = Guid.NewGuid().ToString(), Price = (double)item.Price, Volume = (double)item.Volume, OrderAction = item.OrderAction, OldId = item.OldId }; orders.Add(order); } var multiOrder = new MultiLimitOrderModel { Id = Guid.NewGuid().ToString(), ClientId = walletId, AssetPairId = request.AssetPairId, CancelPreviousOrders = request.CancelPreviousOrders, Orders = orders.ToArray() }; if (request.CancelMode.HasValue) { multiOrder.CancelMode = request.CancelMode.Value; } MultiLimitOrderResponse response = await _matchingEngineClient.PlaceMultiLimitOrderAsync(multiOrder); if (response == null) { throw HftApiException.Create(HftApiErrorCode.MeRuntime, "ME not available"); } var bulkResponse = new BulkLimitOrderResponse { AssetPairId = request.AssetPairId, Error = response.Status.ToHftApiError().code, Statuses = response.Statuses?.Select(x => new BulkOrderItemStatusModel { Id = x.Id, Price = (decimal)x.Price, Volume = (decimal)x.Volume, Error = x.Status.ToHftApiError().code }).ToArray() }; return(Ok(ResponseModel <BulkLimitOrderResponse> .Ok(bulkResponse))); }
private async Task <MultiOrderItemModel> ToMultiOrderItemModel(string clientId, AssetPair assetPair, BulkOrderItemModel item) { var requestId = GetNextRequestId(); var fees = _calculateOrderFees ? await _feeCalculator.GetLimitOrderFees(clientId, assetPair, item.OrderAction) : Array.Empty <LimitOrderFeeModel>(); var model = new MultiOrderItemModel { Id = requestId.ToString(), Price = (double)item.Price, Volume = (double)item.Volume, OrderAction = item.OrderAction.ToMeOrderAction(), Fee = fees.FirstOrDefault() }; if (!string.IsNullOrWhiteSpace(item.OldId)) { model.OldId = item.OldId; } return(model); }
public async Task ApplyAsync(string assetPairId, IReadOnlyCollection <LimitOrder> limitOrders) { string walletId = _settingsService.GetWalletId(); if (string.IsNullOrEmpty(walletId)) { throw new ExchangeException("The wallet not set"); } var multiOrderItems = new List <MultiOrderItemModel>(); foreach (LimitOrder limitOrder in limitOrders) { var multiOrderItem = new MultiOrderItemModel { Id = limitOrder.Id, OrderAction = ToOrderAction(limitOrder.Type), Price = (double)limitOrder.Price, Volume = (double)Math.Abs(limitOrder.Volume) }; multiOrderItems.Add(multiOrderItem); } var multiLimitOrder = new MultiLimitOrderModel { Id = Guid.NewGuid().ToString(), ClientId = walletId, AssetPairId = GetAssetPair(assetPairId), CancelPreviousOrders = true, Orders = multiOrderItems, CancelMode = CancelMode.BothSides }; _log.InfoWithDetails("Matching engine place multi limit order request", multiLimitOrder); MultiLimitOrderResponse response; try { response = await _matchingEngineClient.PlaceMultiLimitOrderAsync(multiLimitOrder); } catch (Exception exception) { _log.ErrorWithDetails(exception, "An error occurred during creating limit orders", multiLimitOrder); throw new ExchangeException("Cannot create limit orders an unexpected error occurred", exception); } if (response == null) { throw new ExchangeException("Matching engine returned an empty response"); } foreach (LimitOrderStatusModel orderStatus in response.Statuses) { LimitOrder limitOrder = limitOrders.SingleOrDefault(e => e.Id == orderStatus.Id); if (limitOrder != null) { limitOrder.Error = ToLimitOrderError(orderStatus.Status); limitOrder.ErrorMessage = limitOrder.Error != LimitOrderError.Unknown ? orderStatus.StatusReason : !string.IsNullOrEmpty(orderStatus.StatusReason) ? orderStatus.StatusReason : "Unknown error"; } else { _log.WarningWithDetails("Matching engine returned status for unknown limit order", new { LimitOrderId = orderStatus.Id }); } } string[] ignoredLimitOrders = response.Statuses .Select(x => x.Id) .Except(multiLimitOrder.Orders.Select(x => x.Id)) .ToArray(); if (ignoredLimitOrders.Any()) { _log.WarningWithDetails("Matching engine response not contains status of limit order", new { AssetPairId = assetPairId, LimitOrders = ignoredLimitOrders }); } _log.InfoWithDetails("Matching engine place multi limit order response", response); }
public async Task ApplyAsync(string assetPairId, IReadOnlyCollection <LimitOrder> limitOrders) { if (string.IsNullOrEmpty(_walletId)) { throw new Exception("WalletId is not set"); } var map = new Dictionary <string, LimitOrder>(); var multiOrderItems = new List <MultiOrderItemModel>(); foreach (LimitOrder limitOrder in limitOrders) { var multiOrderItem = new MultiOrderItemModel { Id = Guid.NewGuid().ToString("D"), OrderAction = limitOrder.TradeType.ToOrderAction(), Price = (double)limitOrder.Price, Volume = (double)limitOrder.Volume, }; map[multiOrderItem.Id] = limitOrder; multiOrderItems.Add(multiOrderItem); limitOrder.ExternalId = multiOrderItem.Id; } var multiLimitOrder = new MultiLimitOrderModel { Id = Guid.NewGuid().ToString(), ClientId = _walletId, AssetPairId = assetPairId, CancelPreviousOrders = true, Orders = multiOrderItems, CancelMode = CancelMode.BothSides }; _log.Info("ME place multi limit order request", new { request = $"data: {multiLimitOrder.ToJson()}" }); MultiLimitOrderResponse response; try { response = await _matchingEngineClient.PlaceMultiLimitOrderAsync(multiLimitOrder, new CancellationTokenSource(Consts.MatchingEngineTimeout).Token); } catch (Exception exception) { _log.Error(exception, "An error occurred during creating limit orders", new { request = $"data: {multiLimitOrder.ToJson()}" }); throw; } if (response == null) { throw new Exception("ME response is null"); } if (response.Statuses.All(x => x.Status == MeStatusCodes.Ok)) { _log.Info("ME place multi limit order response", new { response = $"data: {response.ToJson()}" }); } else { _log.Warning("ME place multi limit order response. Some orders have unsuccessful codes.", context: new { response = $"data: {response.ToJson()}" }); } foreach (var orderStatus in response.Statuses) { if (map.TryGetValue(orderStatus.Id, out var limitOrder)) { limitOrder.Error = orderStatus.Status.ToOrderError(); limitOrder.ErrorMessage = limitOrder.Error != LimitOrderError.Unknown ? orderStatus.StatusReason : !string.IsNullOrEmpty(orderStatus.StatusReason) ? orderStatus.StatusReason : "Unknown error"; } } }
public async Task ApplyAsync(string assetPairId, IReadOnlyCollection <LimitOrder> limitOrders) { string walletId = await _settingsService.GetWalletIdAsync(); if (string.IsNullOrEmpty(walletId)) { throw new Exception("WalletId is not set"); } var map = new Dictionary <string, string>(); var multiOrderItems = new List <MultiOrderItemModel>(); foreach (LimitOrder limitOrder in limitOrders) { var multiOrderItem = new MultiOrderItemModel { Id = Guid.NewGuid().ToString("D"), OrderAction = limitOrder.Type.ToOrderAction(), Price = (double)limitOrder.Price, Volume = (double)Math.Abs(limitOrder.Volume) }; multiOrderItems.Add(multiOrderItem); map[multiOrderItem.Id] = limitOrder.Id; } var multiLimitOrder = new MultiLimitOrderModel { Id = Guid.NewGuid().ToString(), ClientId = walletId, AssetPairId = assetPairId, CancelPreviousOrders = true, Orders = multiOrderItems, CancelMode = CancelMode.BothSides }; _log.InfoWithDetails("ME place multi limit order request", multiLimitOrder); MultiLimitOrderResponse response; try { response = await _matchingEngineClient.PlaceMultiLimitOrderAsync(multiLimitOrder); } catch (Exception exception) { _log.ErrorWithDetails(exception, "An error occurred during creating limit orders", multiLimitOrder); throw; } if (response == null) { throw new Exception("ME response is null"); } foreach (LimitOrderStatusModel orderStatus in response.Statuses) { if (map.TryGetValue(orderStatus.Id, out var limitOrderId)) { var limitOrder = limitOrders.Single(e => e.Id == limitOrderId); limitOrder.Error = orderStatus.Status.ToOrderError(); limitOrder.ErrorMessage = limitOrder.Error != LimitOrderError.Unknown ? orderStatus.StatusReason : !string.IsNullOrEmpty(orderStatus.StatusReason) ? orderStatus.StatusReason : "Unknown error"; } else { _log.Warning("ME returned status for order which is not in the request", context: $"order: {orderStatus.Id}"); } } string[] ignoredOrdersByMe = response.Statuses .Select(x => x.Id) .Except(multiLimitOrder.Orders.Select(x => x.Id)) .ToArray(); if (ignoredOrdersByMe.Any()) { _log.WarningWithDetails("ME didn't return status for orders", $"pair: {assetPairId}, orders: {string.Join(", ", ignoredOrdersByMe)}"); } _log.InfoWithDetails("ME place multi limit order response", response); }
public override async Task <BulkLimitOrderResponse> PlaceBulkLimitOrder(BulkLimitOrderRequest request, ServerCallContext context) { var walletId = context.GetHttpContext().User.GetWalletId(); var items = request.Orders?.ToArray() ?? Array.Empty <BulkOrder>(); var orders = new List <MultiOrderItemModel>(); foreach (var item in items) { var order = new MultiOrderItemModel { Id = Guid.NewGuid().ToString(), Price = Convert.ToDouble(item.Price), Volume = Convert.ToDouble(item.Volume), OrderAction = _mapper.Map <OrderAction>(item.Side), OldId = string.IsNullOrEmpty(item.OldId) ? null : item.OldId }; orders.Add(order); } var multiOrder = new MultiLimitOrderModel { Id = Guid.NewGuid().ToString(), ClientId = walletId, AssetPairId = request.AssetPairId, CancelPreviousOrders = request.CancelPreviousOrders, Orders = orders.ToArray() }; if (request.OptionalCancelModeCase != BulkLimitOrderRequest.OptionalCancelModeOneofCase.None) { multiOrder.CancelMode = _mapper.Map <Lykke.MatchingEngine.Connector.Models.Api.CancelMode>(request.CancelMode); } MultiLimitOrderResponse response = await _matchingEngineClient.PlaceMultiLimitOrderAsync(multiOrder); if (response == null) { return(new BulkLimitOrderResponse { Error = new Error { Code = _mapper.Map <ErrorCode>(HftApiErrorCode.MeRuntime), Message = "ME not available" } }); } var bulkResponse = new BulkLimitOrderResponse { Payload = new BulkLimitOrderResponse.Types.BulkLimitOrderPayload { AssetPairId = request.AssetPairId } }; bulkResponse.Payload.Statuses.AddRange(response.Statuses?.Select(x => new BulkOrderItemStatus { Id = x.Id, Price = x.Price.ToString(CultureInfo.InvariantCulture), Volume = x.Volume.ToString(CultureInfo.InvariantCulture), Error = _mapper.Map <ErrorCode>(x.Status.ToHftApiError().code) })); return(bulkResponse); }