public override async Task <CancelOrderResponse> CancelOrder(CancelOrderRequest request, ServerCallContext context) { MeResponseModel response = await _matchingEngineClient.CancelLimitOrderAsync(request.OrderId); if (response == null) { return(new CancelOrderResponse { Error = new ErrorResponseBody { Code = ErrorCode.Runtime, Message = ErrorMessages.MeNotAvailable } }); } if (response.Status == MeStatusCodes.Ok || response.Status == MeStatusCodes.NotFound) { return new CancelOrderResponse { Body = new CancelOrderResponse.Types.Body { Payload = true } } } ; return(new CancelOrderResponse { Error = new ErrorResponseBody { Code = ErrorCode.Unknown, Message = $"ME status: {response.Status}, message: {response.Message}" } }); }
public async Task <IActionResult> CancelAllOrders([FromQuery] string assetPairId = null, [FromQuery] OrderAction?side = null) { var assetPairResult = await _validationService.ValidateAssetPairAsync(assetPairId); if (assetPairResult != null) { throw HftApiException.Create(assetPairResult.Code, assetPairResult.Message).AddField(assetPairResult.FieldName); } bool?isBuy; switch (side) { case OrderAction.Buy: isBuy = true; break; case OrderAction.Sell: isBuy = false; break; default: isBuy = null; break; } var model = new LimitOrderMassCancelModel { Id = Guid.NewGuid().ToString(), AssetPairId = assetPairId, ClientId = User.GetWalletId(), IsBuy = isBuy }; MeResponseModel response = await _matchingEngineClient.MassCancelLimitOrdersAsync(model); if (response == null) { throw HftApiException.Create(HftApiErrorCode.MeRuntime, "ME not available"); } (HftApiErrorCode code, string message) = response.Status.ToHftApiError(); if (code == HftApiErrorCode.Success) { return(Ok(ResponseModel <string> .Ok(null))); } throw HftApiException.Create(code, message); }
public async Task <IActionResult> CancelOrder(string orderId) { MeResponseModel response = await _matchingEngineClient.CancelLimitOrderAsync(orderId); if (response == null) { throw HftApiException.Create(HftApiErrorCode.MeRuntime, "ME not available"); } (HftApiErrorCode code, string message) = response.Status.ToHftApiError(); if (code == HftApiErrorCode.Success) { return(Ok(ResponseModel <string> .Ok(null))); } throw HftApiException.Create(code, message); }
public async Task <IActionResult> PlaceLimitOrder(PlaceLimitOrderRequest request) { var walletId = User.GetWalletId(); var result = await _validationService.ValidateLimitOrderAsync(walletId, request.AssetPairId, request.Side, request.Price, request.Volume); if (result != null) { throw HftApiException.Create(result.Code, result.Message).AddField(result.FieldName); } var order = new LimitOrderModel { Id = Guid.NewGuid().ToString(), AssetPairId = request.AssetPairId, ClientId = walletId, Price = (double)request.Price, CancelPreviousOrders = false, Volume = (double)Math.Abs(request.Volume), OrderAction = request.Side }; MeResponseModel response = await _matchingEngineClient.PlaceLimitOrderAsync(order); if (response == null) { throw HftApiException.Create(HftApiErrorCode.MeRuntime, "ME not available"); } (HftApiErrorCode code, string message) = response.Status.ToHftApiError(); if (code == HftApiErrorCode.Success) { return(Ok(ResponseModel <LimitOrderResponse> .Ok(new LimitOrderResponse { OrderId = response.TransactionId }))); } throw HftApiException.Create(code, message); }
public override async Task <CancelOrderResponse> CancelOrder(CancelOrderRequest request, ServerCallContext context) { MeResponseModel response = await _matchingEngineClient.CancelLimitOrderAsync(request.OrderId); if (response == null) { return(new CancelOrderResponse { Error = new Error { Code = (int)HftApiErrorCode.MeRuntime, Message = "ME not available" } }); } (HftApiErrorCode code, string message) = response.Status.ToHftApiError(); if (code == HftApiErrorCode.Success) { return new CancelOrderResponse { Payload = true } } ; return(new CancelOrderResponse { Error = new Error { Code = (int)code, Message = message } }); }
public async Task <CommandHandlingResult> Handle(EnrollEthCashinToMatchingEngineCommand command, IEventPublisher eventPublisher) { try { var cashinId = command.CashinOperationId.ToString("N"); var clientId = command.ClientId; var hash = command.TransactionHash; var amount = command.Amount; var asset = await _assetsServiceWithCache.TryGetAssetAsync(command.AssetId); var createPendingActions = command.CreatePendingActions; var paymentTransaction = PaymentTransaction.Create(hash, CashInPaymentSystem.Ethereum, clientId.ToString(), (double)amount, asset.DisplayId ?? asset.Id, status: PaymentStatus.Processing); var exists = await _paymentTransactionsRepository.CheckExistsAsync(paymentTransaction); if (exists) { _log.Warning(command.TransactionHash ?? "Empty", $"Transaction already handled {hash}", context: command); return(CommandHandlingResult.Ok()); } if (createPendingActions && asset.IsTrusted) { await _ethererumPendingActionsRepository.CreateAsync(clientId.ToString(), Guid.NewGuid().ToString()); } ChaosKitty.Meow(); MeResponseModel result = null; await ExecuteWithTimeoutHelper.ExecuteWithTimeoutAsync(async() => { result = await _matchingEngineClient.CashInOutAsync(cashinId, clientId.ToString(), asset.Id, (double)amount); }, 5 * 60 * 1000); // 5 min in ms if (result == null || (result.Status != MeStatusCodes.Ok && result.Status != MeStatusCodes.Duplicate)) { _log.Warning(command.TransactionHash ?? "Empty", "ME error", context: result); return(CommandHandlingResult.Fail(TimeSpan.FromMinutes(1))); } eventPublisher.PublishEvent(new EthCashinEnrolledToMatchingEngineEvent() { TransactionHash = hash, }); ChaosKitty.Meow(); await _paymentTransactionsRepository.TryCreateAsync(paymentTransaction); return(CommandHandlingResult.Ok()); } catch (Exception e) { _log.Error(nameof(EnrollEthCashinToMatchingEngineCommand), e, context: command); throw; } }
private static async Task <MeResponseModel> SanitizeExpandedAccount( IAccount account, IReadOnlyDictionary <string, bool> expansionOptions, CancellationToken cancellationToken) { var sanitizedModel = new MeResponseModel { Href = account.Href, Email = account.Email, Username = account.Username, GivenName = account.GivenName, MiddleName = account.MiddleName, Surname = account.Surname, FullName = account.FullName, CreatedAt = account.CreatedAt, ModifiedAt = account.ModifiedAt, Status = account.Status.ToString(), PasswordModifiedAt = account.PasswordModifiedAt, EmailVerificationToken = account.EmailVerificationToken?.GetValue() }; if (!expansionOptions.Any(e => e.Value)) { return(sanitizedModel); } // TODO might be able to simply return the interface objects directly after explicit interfaces are removed if (expansionOptions.Any(e => e.Key.Equals("applications", StringComparison.OrdinalIgnoreCase) && e.Value)) { // TODO replace with ToArrayAsync var applications = await account.GetApplications().ToListAsync(cancellationToken); sanitizedModel.Applications = new { size = applications.Count, items = applications.Select(a => new { href = a.Href, name = a.Name, description = a.Description, status = a.Status.ToString(), createdAt = a.CreatedAt, modifiedAt = a.ModifiedAt, }) }; } if (expansionOptions.Any(e => e.Key.Equals("apiKeys", StringComparison.OrdinalIgnoreCase) && e.Value)) { // TODO replace with ToArrayAsync var apiKeys = await account.GetApiKeys().ToListAsync(cancellationToken); sanitizedModel.ApiKeys = new { size = apiKeys.Count, items = apiKeys.Select(k => new { href = k.Href, name = k.Name, description = k.Description, id = k.Id, status = k.Status.ToString(), }) }; } if (expansionOptions.Any(e => e.Key.Equals("customData", StringComparison.OrdinalIgnoreCase) && e.Value)) { sanitizedModel.CustomData = (await account.GetCustomDataAsync(cancellationToken)) .ToDictionary(cd => cd.Key, cd => cd.Value); } if (expansionOptions.Any(e => e.Key.Equals("directory", StringComparison.OrdinalIgnoreCase) && e.Value)) { var directory = await account.GetDirectoryAsync(cancellationToken); sanitizedModel.Directory = new { href = directory.Href, name = directory.Name, description = directory.Description, status = directory.Status.ToString(), createdAt = directory.CreatedAt, modifiedAt = directory.ModifiedAt, }; } if (expansionOptions.Any(e => e.Key.Equals("groupMemberships", StringComparison.OrdinalIgnoreCase) && e.Value)) { // TODO replace with ToArrayAsync var groupMemberships = await account.GetGroupMemberships().ToListAsync(cancellationToken); sanitizedModel.GroupMemberships = new { size = groupMemberships.Count, items = groupMemberships.Select(gm => new { href = gm.Href }) }; } if (expansionOptions.Any(e => e.Key.Equals("groups", StringComparison.OrdinalIgnoreCase) && e.Value)) { // TODO replace with ToArrayAsync var groups = await account.GetGroups().ToListAsync(cancellationToken); sanitizedModel.Groups = new { // TODO add href if available on IAsyncQueryable size = groups.Count, items = groups.Select(g => new { href = g.Href, name = g.Name, description = g.Description, status = g.Status.ToString(), createdAt = g.CreatedAt, modifiedAt = g.ModifiedAt, }) }; } if (expansionOptions.Any(e => e.Key.Equals("providerData", StringComparison.OrdinalIgnoreCase) && e.Value)) { var providerData = await account.GetProviderDataAsync(cancellationToken); sanitizedModel.ProviderData = new { href = providerData.Href, createdAt = providerData.CreatedAt, modifiedAt = providerData.ModifiedAt, providerId = providerData.ProviderId }; } if (expansionOptions.Any(e => e.Key.Equals("tenant", StringComparison.OrdinalIgnoreCase) && e.Value)) { var tenant = await account.GetTenantAsync(cancellationToken); sanitizedModel.Tenant = new { href = tenant.Href, name = tenant.Name, key = tenant.Key, createdAt = tenant.CreatedAt, modifiedAt = tenant.ModifiedAt }; } return(sanitizedModel); }
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 CashInOut() { 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); double realBallance = accountBalance.Balance - accountBalance.Reserved; // Low Balance cashout test double badCashOutAmmount = (realBallance + 0.1) * -1; MeResponseModel meBadCashOutResponse = await this.Consumer.Client.CashInOutAsync( Guid.NewGuid().ToString(), testAccount.Id, accountBalance.Asset, badCashOutAmmount); Assert.True(meBadCashOutResponse.Status == MeStatusCodes.LowBalance); // Good cashout test string goodCashOutID = Guid.NewGuid().ToString(); double goodCashOutAmmount = Math.Round((realBallance / 10) * -1, this.AssetPrecission); MeResponseModel meGoodCashOutResponse = await this.Consumer.Client.CashInOutAsync( goodCashOutID, testAccount.Id, accountBalance.Asset, goodCashOutAmmount); Assert.True(meGoodCashOutResponse.Status == MeStatusCodes.Ok); CashOperation message = (CashOperation)await this.WaitForRabbitMQ <CashOperation>(o => o.id == goodCashOutID); Assert.NotNull(message); Assert.True(message.clientId == testAccount.Id); Assert.True(message.asset == accountBalance.Asset); if (Double.TryParse(message.volume, NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedVolume)) { Assert.True(parsedVolume == goodCashOutAmmount); } AccountEntity testAccountCheck = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); BalanceDTO balanceCheck = testAccountCheck.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.True(Math.Round(balanceCheck.Balance, this.AssetPrecission) == Math.Round(accountBalance.Balance + goodCashOutAmmount, this.AssetPrecission)); // TODO get asset accuracy //Cash In test string cashInID = Guid.NewGuid().ToString(); double cashInAmmount = goodCashOutAmmount * -1; //cash in the same ammount we cashed out MeResponseModel meCashInResponse = await this.Consumer.Client.CashInOutAsync( cashInID, testAccount.Id, this.TestAsset1, cashInAmmount); Assert.True(meCashInResponse.Status == MeStatusCodes.Ok); message = (CashOperation)await this.WaitForRabbitMQ <CashOperation>(o => o.id == cashInID); Assert.NotNull(message); Assert.True(message.clientId == testAccount.Id); Assert.True(message.asset == accountBalance.Asset); if (Double.TryParse(message.volume, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedVolume)) { Assert.True(parsedVolume == cashInAmmount); } //balances after cashout -> cashin with the same ammount should not differ AccountEntity testAccountAfter = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); Assert.NotNull(testAccountAfter); BalanceDTO accountBalanceAfter = testAccountAfter.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.NotNull(accountBalanceAfter); Assert.True(accountBalance.Balance == accountBalanceAfter.Balance); }
public async Task CashSwap() { AccountEntity testAccount1 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); Assert.NotNull(testAccount1); BalanceDTO accountBalance1Asset1 = testAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.NotNull(accountBalance1Asset1); BalanceDTO accountBalance1Asset2 = testAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); Assert.NotNull(accountBalance1Asset2); AccountEntity testAccount2 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId2); Assert.NotNull(testAccount2); BalanceDTO accountBalance2Asset1 = testAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.NotNull(accountBalance2Asset1); BalanceDTO accountBalance2Asset2 = testAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); Assert.NotNull(accountBalance2Asset2); //Attempt invalid swap double badSwapAmount1 = accountBalance1Asset1.Balance + 0.7; double badSwapAmount2 = accountBalance2Asset2.Balance - 0.2; // this one isn't bad, but the transaction should still fail string badSwapId = Guid.NewGuid().ToString(); MeResponseModel badSwapResponse = await this.Consumer.Client.SwapAsync(badSwapId, testAccount1.Id, this.TestAsset1, badSwapAmount1, testAccount2.Id, this.TestAsset2, badSwapAmount2); Assert.True(badSwapResponse.Status == MeStatusCodes.LowBalance); //Attempt a valid swap double swapAmount1 = Math.Round(accountBalance1Asset1.Balance / 10, this.AssetPrecission); double swapAmount2 = Math.Round(accountBalance2Asset2.Balance / 10, this.AssetPrecission); string swapId = Guid.NewGuid().ToString(); MeResponseModel swapReseponse = await this.Consumer.Client.SwapAsync(swapId, testAccount1.Id, this.TestAsset1, swapAmount1, testAccount2.Id, this.TestAsset2, swapAmount2); Assert.True(swapReseponse.Status == MeStatusCodes.Ok); CashSwapOperation message = (CashSwapOperation)await this.WaitForRabbitMQ <CashSwapOperation>(o => o.id == swapId); Assert.True(message.asset1 == this.TestAsset1); Assert.True(message.asset2 == this.TestAsset2); Assert.True(message.clientId1 == testAccount1.Id); Assert.True(message.clientId2 == testAccount2.Id); if (Double.TryParse(message.volume1, NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedVolume)) { Assert.True(parsedVolume == swapAmount1); } if (Double.TryParse(message.volume2, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedVolume)) { Assert.True(parsedVolume == swapAmount2); } CashSwapEntity checkCashSwapOperation = (CashSwapEntity)await this.CashSwapRepository.TryGetAsync(c => c.ExternalId == swapId); Assert.True(checkCashSwapOperation.AssetId1 == this.TestAsset1); Assert.True(checkCashSwapOperation.AssetId2 == this.TestAsset2); Assert.True(checkCashSwapOperation.ClientId1 == testAccount1.Id); Assert.True(checkCashSwapOperation.ClientId2 == testAccount2.Id); Assert.True(checkCashSwapOperation.Amount1 == swapAmount1); Assert.True(checkCashSwapOperation.Amount2 == swapAmount2); AccountEntity checkTestAccount1 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); BalanceDTO checkAccountBalance1Asset1 = checkTestAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); BalanceDTO checkAccountBalance1Asset2 = checkTestAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); AccountEntity checkTestAccount2 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId2); BalanceDTO checkAccountBalance2Asset1 = checkTestAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); BalanceDTO checkAccountBalance2Asset2 = checkTestAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); Assert.True(Math.Round(checkAccountBalance1Asset1.Balance, this.AssetPrecission) == Math.Round(accountBalance1Asset1.Balance - swapAmount1, this.AssetPrecission)); Assert.True(Math.Round(checkAccountBalance2Asset1.Balance, this.AssetPrecission) == Math.Round(accountBalance2Asset1.Balance + swapAmount1, this.AssetPrecission)); Assert.True(Math.Round(checkAccountBalance1Asset2.Balance, this.AssetPrecission) == Math.Round(accountBalance1Asset2.Balance + swapAmount2, this.AssetPrecission)); Assert.True(Math.Round(checkAccountBalance2Asset2.Balance, this.AssetPrecission) == Math.Round(accountBalance2Asset2.Balance - swapAmount2, this.AssetPrecission)); // Attempt swap back string swapBackId = Guid.NewGuid().ToString(); MeResponseModel swapBackReseponse = await this.Consumer.Client.SwapAsync(swapBackId, testAccount2.Id, this.TestAsset1, swapAmount1, testAccount1.Id, this.TestAsset2, swapAmount2); Assert.True(swapBackReseponse.Status == MeStatusCodes.Ok); message = (CashSwapOperation)await this.WaitForRabbitMQ <CashSwapOperation>(o => o.id == swapBackId); Assert.True(message.asset1 == this.TestAsset1); Assert.True(message.asset2 == this.TestAsset2); Assert.True(message.clientId1 == testAccount2.Id); Assert.True(message.clientId2 == testAccount1.Id); if (Double.TryParse(message.volume1, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedVolume)) { Assert.True(parsedVolume == swapAmount1); } if (Double.TryParse(message.volume2, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedVolume)) { Assert.True(parsedVolume == swapAmount2); } checkCashSwapOperation = (CashSwapEntity)await this.CashSwapRepository.TryGetAsync(c => c.ExternalId == swapBackId); Assert.True(checkCashSwapOperation.AssetId1 == this.TestAsset1); Assert.True(checkCashSwapOperation.AssetId2 == this.TestAsset2); Assert.True(checkCashSwapOperation.ClientId1 == testAccount2.Id); Assert.True(checkCashSwapOperation.ClientId2 == testAccount1.Id); Assert.True(checkCashSwapOperation.Amount1 == swapAmount1); Assert.True(checkCashSwapOperation.Amount2 == swapAmount2); checkTestAccount1 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); checkAccountBalance1Asset1 = checkTestAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); checkAccountBalance1Asset2 = checkTestAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); checkTestAccount2 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId2); checkAccountBalance2Asset1 = checkTestAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); checkAccountBalance2Asset2 = checkTestAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset2).FirstOrDefault(); // balances should be back to their initial state Assert.True(accountBalance1Asset1.Balance == checkAccountBalance1Asset1.Balance); Assert.True(accountBalance1Asset2.Balance == checkAccountBalance1Asset2.Balance); Assert.True(accountBalance2Asset1.Balance == checkAccountBalance2Asset1.Balance); Assert.True(accountBalance2Asset2.Balance == checkAccountBalance2Asset2.Balance); }
public async Task CashTransfer() { AccountEntity testAccount1 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); Assert.NotNull(testAccount1); BalanceDTO accountBalance1 = testAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.NotNull(accountBalance1); AccountEntity testAccount2 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId2); Assert.NotNull(testAccount2); BalanceDTO accountBalance2 = testAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.NotNull(accountBalance2); //Attempt invalid transfer double badTransferAmount = accountBalance1.Balance + 0.7; string badTransferId = Guid.NewGuid().ToString(); MeResponseModel badTransferResponse = await this.Consumer.Client.TransferAsync(badTransferId, testAccount1.Id, testAccount2.Id, this.TestAsset1, badTransferAmount); Assert.NotNull(badTransferResponse); Assert.True(badTransferResponse.Status == MeStatusCodes.LowBalance); //Transfer from Test acc 1 to Test acc 2 double transferAmount = Math.Round(accountBalance1.Balance / 10, this.AssetPrecission); string transferId = Guid.NewGuid().ToString(); MeResponseModel transferResponse = await this.Consumer.Client.TransferAsync(transferId, testAccount1.Id, testAccount2.Id, this.TestAsset1, transferAmount); Assert.NotNull(transferResponse); Assert.True(transferResponse.Status == MeStatusCodes.Ok); CashTransferOperation message = (CashTransferOperation)await this.WaitForRabbitMQ <CashTransferOperation>(o => o.id == transferId); Assert.NotNull(message); Assert.True(message.asset == this.TestAsset1); Assert.True(message.fromClientId == testAccount1.Id); Assert.True(message.toClientId == testAccount2.Id); if (Double.TryParse(message.volume, NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedMsgAmount)) { Assert.True(parsedMsgAmount == transferAmount); } CashSwapEntity checkCashSwapOperation = (CashSwapEntity)await this.CashSwapRepository.TryGetAsync(c => c.ExternalId == transferId); Assert.True(checkCashSwapOperation.Amount == transferAmount); Assert.True(checkCashSwapOperation.AssetId == this.TestAsset1); Assert.True(checkCashSwapOperation.FromClientId == testAccount1.Id); Assert.True(checkCashSwapOperation.ToClientId == testAccount2.Id); AccountEntity checkTestAccount1 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); BalanceDTO checkAccountBalance1 = checkTestAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); AccountEntity checkTestAccount2 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId2); BalanceDTO checkAccountBalance2 = checkTestAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); Assert.True(Math.Round(checkAccountBalance1.Balance, this.AssetPrecission) == Math.Round(accountBalance1.Balance - transferAmount, this.AssetPrecission)); // TODO get asset accuracy Assert.True(Math.Round(checkAccountBalance2.Balance, this.AssetPrecission) == Math.Round(accountBalance2.Balance + transferAmount, this.AssetPrecission)); // TODO get asset accuracy //Transfer same amount from Test acc 2 to Test acc 1 string transferBackId = Guid.NewGuid().ToString(); MeResponseModel transferBackResponse = await this.Consumer.Client.TransferAsync(transferBackId, testAccount2.Id, testAccount1.Id, this.TestAsset1, transferAmount); Assert.NotNull(transferBackResponse); Assert.True(transferBackResponse.Status == MeStatusCodes.Ok); message = (CashTransferOperation)await this.WaitForRabbitMQ <CashTransferOperation>(o => o.id == transferBackId); Assert.NotNull(message); Assert.True(message.asset == this.TestAsset1); Assert.True(message.fromClientId == testAccount2.Id); Assert.True(message.toClientId == testAccount1.Id); if (Double.TryParse(message.volume, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedMsgAmount)) { Assert.True(parsedMsgAmount == transferAmount); } CashSwapEntity checkCashSwapBackOperation = (CashSwapEntity)await this.CashSwapRepository.TryGetAsync(c => c.ExternalId == transferBackId); Assert.True(checkCashSwapBackOperation.Amount == transferAmount); Assert.True(checkCashSwapBackOperation.AssetId == this.TestAsset1); Assert.True(checkCashSwapBackOperation.FromClientId == testAccount2.Id); Assert.True(checkCashSwapBackOperation.ToClientId == testAccount1.Id); checkTestAccount1 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId1); checkAccountBalance1 = checkTestAccount1.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); checkTestAccount2 = (AccountEntity)await this.AccountRepository.TryGetAsync(this.TestAccountId2); checkAccountBalance2 = checkTestAccount2.BalancesParsed.Where(b => b.Asset == this.TestAsset1).FirstOrDefault(); //balances should be back to their initial state after 2 transfers back and forward Assert.True(accountBalance1.Balance == checkAccountBalance1.Balance); Assert.True(accountBalance2.Balance == checkAccountBalance2.Balance); }
private static void ValidateResponse(MeResponseModel response, MeStatusCodes expected) { Assert.NotNull(response); Assert.Equal(response.Status, expected); }
public override async Task <LimitOrderResponse> PlaceLimitOrder(LimitOrderRequest request, ServerCallContext context) { var walletId = context.GetHttpContext().User.GetWalletId(); var result = await _validationService.ValidateLimitOrderAsync(request.AssetPairId, Convert.ToDecimal(request.Price), Convert.ToDecimal(request.Volume)); if (result != null) { return(new LimitOrderResponse { Error = new Error { Code = (int)result.Code, Message = result.Message } }); } var order = new LimitOrderModel { Id = Guid.NewGuid().ToString(), AssetPairId = request.AssetPairId, ClientId = walletId, Price = Convert.ToDouble(request.Price), CancelPreviousOrders = false, Volume = Math.Abs(Convert.ToDouble(request.Volume)), OrderAction = request.Side == Side.Buy ? OrderAction.Buy : OrderAction.Sell }; MeResponseModel response = await _matchingEngineClient.PlaceLimitOrderAsync(order); if (response == null) { return(new LimitOrderResponse { Error = new Error { Code = (int)HftApiErrorCode.MeRuntime, Message = "ME not available" } }); } (HftApiErrorCode code, string message) = response.Status.ToHftApiError(); if (code == HftApiErrorCode.Success) { return new LimitOrderResponse { Payload = new LimitOrderResponse.Types.LimitOrderPayload { OrderId = response.TransactionId } } } ; return(new LimitOrderResponse { Error = new Error { Code = (int)code, Message = message } }); }
public override async Task <CancelOrderResponse> CancelAllOrders(CancelOrdersRequest request, ServerCallContext context) { var result = await _validationService.ValidateAssetPairAsync(request.AssetPairId); if (result != null) { return(new CancelOrderResponse { Error = new Error { Code = (int)result.Code, Message = result.Message } }); } bool?isBuy; switch (request.Side) { case Side.Buy: isBuy = true; break; case Side.Sell: isBuy = false; break; default: isBuy = null; break; } var model = new LimitOrderMassCancelModel { Id = Guid.NewGuid().ToString(), AssetPairId = request.AssetPairId, ClientId = context.GetHttpContext().User.GetWalletId(), IsBuy = isBuy }; MeResponseModel response = await _matchingEngineClient.MassCancelLimitOrdersAsync(model); if (response == null) { return(new CancelOrderResponse { Error = new Error { Code = (int)HftApiErrorCode.MeRuntime, Message = "ME not available" } }); } (HftApiErrorCode code, string message) = response.Status.ToHftApiError(); if (code == HftApiErrorCode.Success) { return new CancelOrderResponse { Payload = true } } ; return(new CancelOrderResponse { Error = new Error { Code = (int)code, Message = message } }); }
public override async Task <CancelOrderResponse> CancelAllOrders(CancelOrdersRequest request, ServerCallContext context) { MeResponseModel response; if (!string.IsNullOrEmpty(request.AssetPairId)) { var result = await _validationService.ValidateAssetPairAsync(request.AssetPairId); if (result != null) { var res = new CancelOrderResponse { Error = new ErrorResponseBody { Code = ErrorCode.InvalidField, Message = result.Message } }; res.Error.Fields.Add(result.FieldName, result.Message); } bool?isBuy; if (request.OptionalSideCase == CancelOrdersRequest.OptionalSideOneofCase.None) { isBuy = null; } else { switch (request.Side) { case Side.Buy: isBuy = true; break; case Side.Sell: isBuy = false; break; default: isBuy = null; break; } } var model = new LimitOrderMassCancelModel { Id = Guid.NewGuid().ToString(), AssetPairId = request.AssetPairId, ClientId = context.GetClientId(), IsBuy = isBuy }; response = await _matchingEngineClient.MassCancelLimitOrdersAsync(model); } else { var orders = await GetOrders(new LimitOrdersRequest(), context); if (orders.Body?.Orders.Any() ?? false) { var orderIds = orders.Body.Orders.Select(x => x.Id).ToList(); response = await _matchingEngineClient.CancelLimitOrdersAsync(orderIds); } else { response = new MeResponseModel { Status = MeStatusCodes.Ok }; } } if (response == null) { return(new CancelOrderResponse { Error = new ErrorResponseBody { Code = ErrorCode.Runtime, Message = ErrorMessages.MeNotAvailable } }); } if (response.Status == MeStatusCodes.Ok) { return new CancelOrderResponse { Body = new CancelOrderResponse.Types.Body { Payload = true } } } ; return(new CancelOrderResponse { Error = new ErrorResponseBody { Code = ErrorCode.Runtime, Message = response.Message ?? response.Status.ToString() } }); }