private async Task CompleteAssetSettlementAsync(AssetSettlement assetSettlement)
        {
            try
            {
                AssetHedgeSettings assetHedgeSettings =
                    await _assetHedgeSettingsService.GetByAssetIdAsync(assetSettlement.AssetId);

                if (!assetSettlement.IsDirect && !assetSettlement.IsExternal)
                {
                    // In this case position will be closed automatically by hedge limit order.
                }
                else
                {
                    await _positionService.CloseAsync(assetSettlement.AssetId, assetHedgeSettings.Exchange,
                                                      assetSettlement.ActualAmount, assetSettlement.ActualPrice);
                }

                assetSettlement.Status = AssetSettlementStatus.Completed;
            }
            catch (Exception exception)
            {
                _log.ErrorWithDetails(exception, "An error occurred while completing asset settlement",
                                      assetSettlement);

                assetSettlement.Error = SettlementError.Unknown;
            }

            await _settlementRepository.UpdateAsync(assetSettlement);
        }
        public async Task ExecuteAssetAsync(string settlementId, string assetId, decimal actualAmount,
                                            decimal actualPrice, string userId)
        {
            Settlement settlement = await GetByIdAsync(settlementId);

            AssetSettlement assetSettlement = settlement.GetAsset(assetId);

            if (assetSettlement == null)
            {
                throw new InvalidOperationException("Asset not found");
            }

            if (!assetSettlement.IsDirect || !assetSettlement.IsExternal)
            {
                throw new InvalidOperationException("Only direct external assets can be manually executed");
            }

            var allowedStatuses = new[] { SettlementStatus.Reserved, SettlementStatus.Transferred };

            if (!allowedStatuses.Contains(settlement.Status) || assetSettlement.Status != AssetSettlementStatus.New)
            {
                throw new InvalidOperationException("Can not execute asset.");
            }

            assetSettlement.ActualAmount = actualAmount;
            assetSettlement.ActualPrice  = actualPrice;
            assetSettlement.Status       = AssetSettlementStatus.Transferred;

            await _settlementRepository.UpdateAsync(assetSettlement);

            _log.InfoWithDetails("Asset updated", new { assetSettlement, userId });
        }
        public async Task RetryAssetAsync(string settlementId, string assetId, string userId)
        {
            Settlement settlement = await GetByIdAsync(settlementId);

            AssetSettlement assetSettlement = settlement.GetAsset(assetId);

            if (assetSettlement == null)
            {
                throw new InvalidOperationException("Asset not found");
            }

            if (settlement.Status != SettlementStatus.Approved &&
                settlement.Status != SettlementStatus.Reserved ||
                assetSettlement.Status != AssetSettlementStatus.New &&
                assetSettlement.Status != AssetSettlementStatus.Reserved)
            {
                throw new InvalidOperationException("Can not retry asset.");
            }

            assetSettlement.Error = SettlementError.None;

            await _settlementRepository.UpdateAsync(assetSettlement);

            _log.InfoWithDetails("Asset settlement retry", new { assetSettlement, userId });
        }
        public async Task UpdateAssetAsync(string settlementId, string assetId, decimal amount, bool isDirect,
                                           bool isExternal, string userId)
        {
            Settlement settlement = await GetByIdAsync(settlementId);

            if (settlement.Status != SettlementStatus.New)
            {
                throw new InvalidOperationException("Only new settlement can be updated");
            }

            AssetSettlement assetSettlement = settlement.GetAsset(assetId);

            if (assetSettlement == null)
            {
                throw new InvalidOperationException("Asset not found");
            }

            assetSettlement.Update(amount, isDirect, isExternal);

            await ValidateBalanceAsync(settlement);

            await _settlementRepository.ReplaceAsync(settlement);

            _log.InfoWithDetails("Asset updated", new { assetSettlement, userId });
        }
 public Task UpdateAsync(AssetSettlement assetSettlement)
 {
     return(_storage.MergeAsync(
                GetPartitionKey(assetSettlement.SettlementId),
                GetRowKey(assetSettlement.AssetId),
                entity =>
     {
         Mapper.Map(assetSettlement, entity);
         return entity;
     }));
 }
        private async Task TransferReservedFundsAsync(AssetSettlement assetSettlement, string clientId,
                                                      string walletId)
        {
            string assetId = assetSettlement.IsDirect
                ? assetSettlement.AssetId
                : "USD";

            decimal amount = assetSettlement.IsDirect
                ? assetSettlement.Amount
                : assetSettlement.Amount * assetSettlement.Price;

            try
            {
                string transactionId = await _settlementTransferService.TransferReservedFundsAsync(walletId,
                                                                                                   assetId, amount, clientId, assetSettlement.SettlementId);

                assetSettlement.TransactionId = transactionId;
                assetSettlement.Status        = AssetSettlementStatus.Transferred;

                _log.InfoWithDetails("Reserved funds transferred to client wallet",
                                     new { assetSettlement.SettlementId, assetId, amount, clientId, walletId, transactionId });
            }
            catch (NotEnoughFundsException)
            {
                assetSettlement.Error = SettlementError.NotEnoughFunds;

                _log.WarningWithDetails("Not enough reserved funds to transfer to client wallet",
                                        new { assetSettlement.SettlementId, assetId, amount, clientId, walletId });
            }
            catch (Exception exception)
            {
                assetSettlement.Error = SettlementError.Unknown;

                _log.ErrorWithDetails(exception, "An error occurred while transferring reserved funds to client wallet",
                                      new { assetSettlement.SettlementId, assetId, amount, clientId, walletId });
            }

            await _settlementRepository.UpdateAsync(assetSettlement);
        }
        private async Task ReserveFundsAsync(AssetSettlement assetSettlement, string clientId)
        {
            await _settlementRepository.UpdateAsync(assetSettlement);

            string assetId = assetSettlement.IsDirect
                ? assetSettlement.AssetId
                : "USD";

            decimal amount = assetSettlement.IsDirect
                ? assetSettlement.Amount
                : assetSettlement.Amount * assetSettlement.Price;

            try
            {
                await _settlementTransferService.ReserveFundsAsync(assetId, amount, clientId,
                                                                   assetSettlement.SettlementId);

                assetSettlement.Status = AssetSettlementStatus.Reserved;

                _log.InfoWithDetails("Funds reserved from main wallet",
                                     new { assetSettlement.SettlementId, assetId, amount, clientId });
            }
            catch (NotEnoughFundsException)
            {
                assetSettlement.Error = SettlementError.NotEnoughFunds;

                _log.WarningWithDetails("Not enough funds to reserve from main wallet",
                                        new { assetSettlement.SettlementId, assetId, amount, clientId });
            }
            catch (Exception exception)
            {
                assetSettlement.Error = SettlementError.Unknown;

                _log.ErrorWithDetails(exception, "An error occurred while reserving funds from main wallet",
                                      new { assetSettlement.SettlementId, assetId, amount, clientId });
            }

            await _settlementRepository.UpdateAsync(assetSettlement);
        }
 public Task UpdateAsync(AssetSettlement assetSettlement)
 {
     return(_assetSettlementAzureRepository.UpdateAsync(assetSettlement));
 }