public static Task <SwapPriceEstimation> EstimateSwapPriceAsync( decimal amount, AmountType amountType, CurrencyConfig fromCurrency, CurrencyConfig toCurrency, IAccount account, IAtomexClient atomexClient, ISymbolsProvider symbolsProvider, CancellationToken cancellationToken = default) { return(Task.Run(() => { if (fromCurrency == null) { return null; } if (toCurrency == null) { return null; } var symbol = symbolsProvider .GetSymbols(account.Network) .SymbolByCurrencies(fromCurrency, toCurrency); if (symbol == null) { return null; } var side = symbol.OrderSideForBuyCurrency(toCurrency); var orderBook = atomexClient.GetOrderBook(symbol); if (orderBook == null) { return null; } var baseCurrency = account.Currencies.GetByName(symbol.Base); var isSoldAmount = amountType == AmountType.Sold; var(estimatedOrderPrice, estimatedPrice) = orderBook.EstimateOrderPrices( side: side, amount: amount, amountDigitsMultiplier: isSoldAmount ? fromCurrency.DigitsMultiplier : toCurrency.DigitsMultiplier, qtyDigitsMultiplier: baseCurrency.DigitsMultiplier, amountType: amountType); var(estimatedMaxFromAmount, estimatedMaxToAmount) = orderBook.EstimateMaxAmount(side, fromCurrency.DigitsMultiplier); var isNoLiquidity = amount != 0 && estimatedOrderPrice == 0; var oppositeAmount = isSoldAmount ? symbol.IsBaseCurrency(toCurrency.Name) ? estimatedPrice != 0 ? AmountHelper.RoundDown(amount / estimatedPrice, toCurrency.DigitsMultiplier) : 0m : AmountHelper.RoundDown(amount * estimatedPrice, toCurrency.DigitsMultiplier) : symbol.IsBaseCurrency(toCurrency.Name) ? AmountHelper.RoundDown(amount * estimatedPrice, fromCurrency.DigitsMultiplier) : estimatedPrice != 0 ? AmountHelper.RoundDown(amount / estimatedPrice, fromCurrency.DigitsMultiplier) : 0m; return new SwapPriceEstimation { FromAmount = isSoldAmount ? amount : oppositeAmount, ToAmount = isSoldAmount ? oppositeAmount : amount, OrderPrice = estimatedOrderPrice, Price = estimatedPrice, MaxFromAmount = estimatedMaxFromAmount, MaxToAmount = estimatedMaxToAmount, IsNoLiquidity = isNoLiquidity }; }, cancellationToken)); }
public static Task <SwapParams> EstimateSwapParamsAsync( IFromSource from, decimal fromAmount, string redeemFromAddress, CurrencyConfig fromCurrency, CurrencyConfig toCurrency, IAccount account, IAtomexClient atomexClient, ISymbolsProvider symbolsProvider, ICurrencyQuotesProvider quotesProvider, CancellationToken cancellationToken = default) { return(Task.Run(async() => { if (fromCurrency == null) { return null; } if (toCurrency == null) { return null; } var redeemFromWalletAddress = redeemFromAddress != null ? await account .GetAddressAsync(toCurrency.Name, redeemFromAddress, cancellationToken) .ConfigureAwait(false) : null; // estimate redeem fee var estimatedRedeemFee = await toCurrency .GetEstimatedRedeemFeeAsync(redeemFromWalletAddress, withRewardForRedeem: false) .ConfigureAwait(false); // estimate reward for redeem var rewardForRedeem = await RewardForRedeemHelper.EstimateAsync( account: account, quotesProvider: quotesProvider, feeCurrencyQuotesProvider: symbol => atomexClient?.GetOrderBook(symbol)?.TopOfBook(), redeemableCurrency: toCurrency, redeemFromAddress: redeemFromWalletAddress, cancellationToken: cancellationToken); // get amount reserved for active swaps var reservedForSwapsAmount = await GetAmountReservedForSwapsAsync( from: from, account: account, currency: fromCurrency) .ConfigureAwait(false); // estimate maker network fee var estimatedMakerNetworkFee = await EstimateMakerNetworkFeeAsync( fromCurrency: fromCurrency, toCurrency: toCurrency, account: account, atomexClient: atomexClient, symbolsProvider: symbolsProvider, cancellationToken: cancellationToken) .ConfigureAwait(false); var fromCurrencyAccount = account .GetCurrencyAccount(fromCurrency.Name) as IEstimatable; // estimate payment fee var estimatedPaymentFee = await fromCurrencyAccount .EstimateSwapPaymentFeeAsync( from: from, amount: fromAmount, cancellationToken: cancellationToken) .ConfigureAwait(false); // estimate max amount and max fee var maxAmountEstimation = await fromCurrencyAccount .EstimateMaxSwapPaymentAmountAsync( from: from, reserve: true, cancellationToken: cancellationToken) .ConfigureAwait(false); if (maxAmountEstimation.Error != null) { return new SwapParams { Amount = 0m, PaymentFee = estimatedPaymentFee.Value, RedeemFee = estimatedRedeemFee, RewardForRedeem = rewardForRedeem, MakerNetworkFee = estimatedMakerNetworkFee, ReservedForSwaps = reservedForSwapsAmount, Error = maxAmountEstimation.Error }; } var maxNetAmount = Math.Max(maxAmountEstimation.Amount - reservedForSwapsAmount - estimatedMakerNetworkFee, 0m); if (maxNetAmount == 0m) // insufficient funds { return new SwapParams { Amount = 0m, PaymentFee = maxAmountEstimation.Fee, RedeemFee = estimatedRedeemFee, RewardForRedeem = rewardForRedeem, MakerNetworkFee = estimatedMakerNetworkFee, ReservedForSwaps = reservedForSwapsAmount, Error = new Error( code: Errors.InsufficientFunds, description: Resources.InsufficientFundsToCoverMakerNetworkFee, details: string.Format(Resources.InsufficientFundsToCoverMakerNetworkFeeDetails, estimatedMakerNetworkFee, // required fromCurrency.Name, // currency code maxAmountEstimation.Amount - reservedForSwapsAmount)) // available }; } if (fromAmount > maxNetAmount) // amount greater than max net amount => use max amount params { return new SwapParams { Amount = Math.Max(maxNetAmount, 0m), PaymentFee = maxAmountEstimation.Fee, RedeemFee = estimatedRedeemFee, RewardForRedeem = rewardForRedeem, MakerNetworkFee = estimatedMakerNetworkFee, ReservedForSwaps = reservedForSwapsAmount, Error = new Error( code: Errors.InsufficientFunds, description: Resources.InsufficientFunds, details: string.Format(Resources.InsufficientFundsToSendAmountDetails, fromAmount, // required fromCurrency.Name, // currency code maxNetAmount)) // available }; } return new SwapParams { Amount = fromAmount, PaymentFee = estimatedPaymentFee.Value, RedeemFee = estimatedRedeemFee, RewardForRedeem = rewardForRedeem, MakerNetworkFee = estimatedMakerNetworkFee, ReservedForSwaps = reservedForSwapsAmount, Error = null }; }, cancellationToken)); }