private async Task <Error> SendSwapAsync() { try { var account = App.Account; var requiredAmount = Amount + EstimatedPaymentFee; var fromWallets = (await account .GetUnspentAddressesAsync( currency: FromCurrency, amount: requiredAmount, fee: 0, feePrice: 0, isFeePerTransaction: false, addressUsagePolicy: AddressUsagePolicy.UseMinimalBalanceFirst)) .ToList(); if (requiredAmount > 0 && !fromWallets.Any()) { return(new Error(Errors.SwapError, Resources.CvInsufficientFunds)); } var symbol = App.Account.Symbols.SymbolByCurrencies(FromCurrency, ToCurrency); var side = symbol.OrderSideForBuyCurrency(ToCurrency); var terminal = App.Terminal; var orderBook = terminal.GetOrderBook(symbol); var price = orderBook.EstimatedDealPrice(side, Amount); if (price == 0) { return(new Error(Errors.NoLiquidity, Resources.CvNoLiquidity)); } var qty = Math.Round(AmountHelper.AmountToQty(side, Amount, price), symbol.Base.Digits); var order = new Order { Symbol = symbol, TimeStamp = DateTime.UtcNow, Price = price, Qty = qty, Side = side, Type = OrderType.FillOrKill, FromWallets = fromWallets.ToList(), }; await order.CreateProofOfPossessionAsync(account); terminal.OrderSendAsync(order); } catch (Exception e) { Log.Error(e, "Conversion error"); return(new Error(Errors.SwapError, Resources.CvConversionError)); } return(null); }
private async Task <Error> SendSwapAsync() { try { var account = App.Account; var requiredAmount = Amount + Fee; var fromWallets = (await account .GetUnspentAddressesAsync( currency: FromCurrency, requiredAmount: requiredAmount)) .ToList(); var refundWallet = await account .GetRefundAddressAsync(FromCurrency, fromWallets); var toWallet = await account .GetRedeemAddressAsync(ToCurrency); var symbol = Symbols.SymbolByCurrencies(FromCurrency, ToCurrency); var side = symbol.OrderSideForBuyCurrency(ToCurrency); var terminal = App.Terminal; var orderBook = terminal.GetOrderBook(symbol); var price = orderBook.EstimatedDealPrice(side, Amount); if (price == 0) { return(new Error(Errors.NoLiquidity, Resources.CvNoLiquidity)); } var qty = Math.Round(AmountHelper.AmountToQty(side, Amount, price), symbol.QtyDigits); var order = new Order { Symbol = symbol, TimeStamp = DateTime.UtcNow, Price = price, Qty = qty, Fee = Fee, Side = side, Type = OrderType.FillOrKill, FromWallets = fromWallets.ToList(), ToWallet = toWallet, RefundWallet = refundWallet }; await order.CreateProofOfPossessionAsync(account); terminal.OrderSendAsync(order); } catch (Exception e) { Log.Error(e, "Conversion error"); return(new Error(Errors.SwapError, Resources.CvConversionError)); } return(null); }
public (decimal, decimal) EstimateOrderPrices( Side side, decimal amount, decimal amountDigitsMultiplier, decimal qtyDigitsMultiplier) { var requiredAmount = amount; lock (SyncRoot) { var book = side == Side.Buy ? Sells : Buys; if (amount == 0) { return(book.Any() ? (book.First().Key, book.First().Key) : (0m, 0m)); } var totalUsedQuoteAmount = 0m; var totalUsedQty = 0m; foreach (var entryPair in book) { var qty = entryPair.Value.Qty(); var price = entryPair.Key; var availableAmount = AmountHelper.QtyToAmount(side, qty, price, amountDigitsMultiplier); var usedAmount = Math.Min(requiredAmount, availableAmount); var usedQty = AmountHelper.AmountToQty(side, usedAmount, price, qtyDigitsMultiplier); totalUsedQuoteAmount += usedQty * price; totalUsedQty += usedQty; requiredAmount -= usedAmount; if (requiredAmount <= 0) { return(price, totalUsedQuoteAmount / totalUsedQty); } } } return(0m, 0m); }
private async Task <Error> ConvertAsync() { try { var account = App.Account; var currencyAccount = account .GetCurrencyAccount <ILegacyCurrencyAccount>(FromCurrency.Name); var fromWallets = (await currencyAccount .GetUnspentAddressesAsync( toAddress: null, amount: Amount, fee: 0, feePrice: await FromCurrency.GetDefaultFeePriceAsync(), feeUsagePolicy: FeeUsagePolicy.EstimatedFee, addressUsagePolicy: AddressUsagePolicy.UseMinimalBalanceFirst, transactionType: BlockchainTransactionType.SwapPayment)) .ToList(); foreach (var fromWallet in fromWallets) { if (fromWallet.Currency != FromCurrency.Name) { fromWallet.Currency = FromCurrency.Name; } } // check balances var errors = await BalanceChecker.CheckBalancesAsync(App.Account, fromWallets); if (errors.Any()) { return(new Error(Errors.SwapError, GetErrorsDescription(errors))); } if (Amount == 0) { return(new Error(Errors.SwapError, Resources.CvZeroAmount)); } if (Amount > 0 && !fromWallets.Any()) { return(new Error(Errors.SwapError, Resources.CvInsufficientFunds)); } var symbol = App.SymbolsProvider .GetSymbols(App.Account.Network) .SymbolByCurrencies(FromCurrency, ToCurrency); var baseCurrency = App.Account.Currencies.GetByName(symbol.Base); var side = symbol.OrderSideForBuyCurrency(ToCurrency); var terminal = App.Terminal; var price = EstimatedPrice; var orderPrice = EstimatedOrderPrice; if (price == 0) { return(new Error(Errors.NoLiquidity, Resources.CvNoLiquidity)); } var qty = AmountHelper.AmountToQty(side, Amount, price, baseCurrency.DigitsMultiplier); if (qty < symbol.MinimumQty) { var minimumAmount = AmountHelper.QtyToAmount(side, symbol.MinimumQty, price, FromCurrency.DigitsMultiplier); var message = string.Format(CultureInfo.InvariantCulture, Resources.CvMinimumAllowedQtyWarning, minimumAmount, FromCurrency.Name); return(new Error(Errors.SwapError, message)); } var order = new Order { Symbol = symbol.Name, TimeStamp = DateTime.UtcNow, Price = orderPrice, Qty = qty, Side = side, Type = OrderType.FillOrKill, FromWallets = fromWallets.ToList(), MakerNetworkFee = EstimatedMakerNetworkFee }; await order.CreateProofOfPossessionAsync(account); terminal.OrderSendAsync(order); // wait for swap confirmation var timeStamp = DateTime.UtcNow; while (DateTime.UtcNow < timeStamp + SwapTimeout) { await Task.Delay(SwapCheckInterval); var currentOrder = terminal.Account.GetOrderById(order.ClientOrderId); if (currentOrder == null) { continue; } if (currentOrder.Status == OrderStatus.Pending) { continue; } if (currentOrder.Status == OrderStatus.PartiallyFilled || currentOrder.Status == OrderStatus.Filled) { var swap = (await terminal.Account .GetSwapsAsync()) .FirstOrDefault(s => s.OrderId == currentOrder.Id); if (swap == null) { continue; } return(null); } if (currentOrder.Status == OrderStatus.Canceled) { return(new Error(Errors.PriceHasChanged, Resources.SvPriceHasChanged)); } if (currentOrder.Status == OrderStatus.Rejected) { return(new Error(Errors.OrderRejected, Resources.SvOrderRejected)); } } return(new Error(Errors.TimeoutReached, Resources.SvTimeoutReached)); } catch (Exception e) { Log.Error(e, "Conversion error"); return(new Error(Errors.SwapError, Resources.CvConversionError)); } }