Example #1
0
        public async Task <GetPortfolioResponse> Handle(GetPortfolioRequest request, CancellationToken cancellationToken)
        {
            var member = await _context.Members
                         .Where(x => x.MemberUsername == request.MemberUsername)
                         .Include(a => a.PreferredFiatCurrency)
                         .Include(x => x.PreferredCoinCurrency)
                         .Include(x => x.PreferredTimeFrame)
                         .Include(a => a.Owners)
                         .AsNoTracking()
                         .SingleOrDefaultAsync(cancellationToken);

            if (member == null)
            {
                throw new NotFoundException(nameof(Member), request.MemberUsername);
            }

            var result = new GetPortfolioResponse
            {
                MemberPreferredFiatCurrencyID             = member.PreferredFiatCurrencyID,
                MemberPreferredFiatCurrencySymbol         = member.PreferredFiatCurrency.Symbol,
                MemberPreferredFiatCurrencyUnitPriceInUSD = member.PreferredFiatCurrency.UnitPriceInUSD,
                MemberPreferredCoinCurrencyID             = member.PreferredCoinCurrencyID,
                MemberPreferredCoinCurrencySymbol         = member.PreferredCoinCurrency.Symbol,
                MemberPreferredCoinCurrencyUnitPriceInUSD = member.PreferredCoinCurrency.UnitPriceInUSD,
                MemberPreferredTimeFrameName = member.PreferredTimeFrame.Name
            };

            #region Load Crypto Currencies owned by Member
            foreach (var owner in member.Owners)
            {
                var nonFiatWallets = _context.Wallets
                                     .Where(x =>
                                            x.OwnerID == owner.OwnerID &&
                                            x.BlockchainID != BlockchainID.Fiat)
                                     .AsNoTracking()
                                     .ToList();

                foreach (var wallet in nonFiatWallets)
                {
                    var pockets = await _context.Pockets
                                  .Where(x =>
                                         x.WalletID == wallet.WalletID &&
                                         x.Currency.UnitPriceInUSD > 0)
                                  .Include(a => a.Transactions)
                                  .AsNoTracking()
                                  .ToListAsync(cancellationToken);

                    foreach (var pocket in pockets)
                    {
                        CurrencyDTO currencyDTO = result.Currencies
                                                  .Where(x => x.CurrencyID == pocket.CurrencyID)
                                                  .SingleOrDefault();

                        if (currencyDTO == null)
                        {
                            var currency = await _context.Currencies
                                           .Where(x => x.CurrencyID == pocket.CurrencyID)
                                           .AsNoTracking()
                                           .SingleOrDefaultAsync(cancellationToken);

                            currencyDTO = new CurrencyDTO
                            {
                                CurrencyID     = currency.CurrencyID,
                                Symbol         = currency.Symbol,
                                Name           = currency.Name,
                                UnitPriceInUSD = currency.UnitPriceInUSD,
                                CurrencyType   = currency.CurrencyType
                            };

                            result.Currencies.Add(currencyDTO);
                        }

                        var selectedTransactions = pocket.Transactions
                                                   .Where(x =>
                                                          x.TransactionType == TransactionType.StartingBalance ||
                                                          x.TransactionType == TransactionType.BuyIN ||
                                                          x.TransactionType == TransactionType.BuyOUT ||
                                                          x.TransactionType == TransactionType.SellIN ||
                                                          x.TransactionType == TransactionType.SellOUT ||
                                                          x.TransactionType == TransactionType.ExternalTransferIN ||
                                                          x.TransactionType == TransactionType.ExternalTransferOUT ||
                                                          x.TransactionType == TransactionType.SyncBalanceImport ||
                                                          x.TransactionType == TransactionType.SyncTransactionIN ||
                                                          x.TransactionType == TransactionType.SyncTransactionOUT)
                                                   .OrderBy(o => o.TransactionDateTime)
                                                   .ToList();

                        foreach (var transaction in selectedTransactions)
                        {
                            TransactionDTO transactionDTO = new TransactionDTO
                            {
                                TransactionID       = transaction.TransactionID,
                                Amount              = transaction.Amount,
                                TransactionType     = transaction.TransactionType,
                                TransactionDateTime = transaction.TransactionDateTime,
                                UnitPriceInUSD      = transaction.UnitPriceInUSD
                            };

                            currencyDTO.Transactions.Add(transactionDTO);
                        }

                        #region TotalPurchaseValueInPreferredFiatCurrency
                        if (result.MemberPreferredFiatCurrencyUnitPriceInUSD > 0)
                        {
                            currencyDTO.TotalPurchaseValueInPreferredFiatCurrency = currencyDTO.TotalPurchaseValueInUSD / result.MemberPreferredFiatCurrencyUnitPriceInUSD;
                        }
                        else
                        {
                            currencyDTO.TotalPurchaseValueInPreferredFiatCurrency = 0m;
                        }
                        #endregion

                        #region CurrentValueInPreferredCoinCurrency
                        if (currencyDTO.CurrencyID == result.MemberPreferredCoinCurrencyID)
                        {
                            currencyDTO.CurrentValueInPreferredCoinCurrency = currencyDTO.TotalAmount;
                        }
                        else
                        {
                            if (result.MemberPreferredCoinCurrencyUnitPriceInUSD > 0)
                            {
                                currencyDTO.CurrentValueInPreferredCoinCurrency = currencyDTO.TotalAmount * (currencyDTO.UnitPriceInUSD / result.MemberPreferredCoinCurrencyUnitPriceInUSD);
                            }
                            else
                            {
                                currencyDTO.CurrentValueInPreferredCoinCurrency = 0m;
                            }
                        }
                        #endregion
                    }
                }
            }
            #endregion

            var validCurrencies = result.Currencies.Where(x => x.TotalAmount > 0).ToList();

            foreach (var currencyDTO in validCurrencies)
            {
                // debug why token with amount > 1 USD doesn't occur?
                _logger.LogDebug($"validCurrencies - Coin Symbol: {currencyDTO.Symbol}");
                _logger.LogDebug($"validCurrencies - Coin TotalAmount: {currencyDTO.TotalAmount}");

                #region Just For Debug. *Note: Maybe Problems Occur from "Too Many Request from Coin Market Cap"
                CryptoLatestQuotesResult latestQuotesResult = null;

                try
                {
                    latestQuotesResult = await _cryptoService.GetLatestQuoteAsync(currencyDTO.Symbol, result.MemberPreferredFiatCurrencySymbol);
                }
                catch (Exception ex)
                {
                    result.IsSuccessful = false;
                    result.ErrorMessage = ex.Message;
                }
                #endregion

                if (latestQuotesResult != null)
                {
                    currencyDTO.Price            = latestQuotesResult.Price;
                    currencyDTO.Volume24h        = latestQuotesResult.Volume24h;
                    currencyDTO.PercentChange1h  = latestQuotesResult.PercentChange1h;
                    currencyDTO.PercentChange24h = latestQuotesResult.PercentChange24h;
                    currencyDTO.PercentChange7d  = latestQuotesResult.PercentChange7d;

                    // Sekalian update Currency kalau MemberPreferredFiatCurrencySymbol adalah USD
                    if (result.MemberPreferredFiatCurrencySymbol == CurrencySymbol.USD)
                    {
                        var currency = await _context.Currencies
                                       .Where(x => x.CurrencyID == currencyDTO.CurrencyID)
                                       .SingleAsync(cancellationToken);

                        currency.Rank             = latestQuotesResult.Rank;
                        currency.UnitPriceInUSD   = latestQuotesResult.Price;
                        currency.Volume24h        = latestQuotesResult.Volume24h;
                        currency.PercentChange1h  = latestQuotesResult.PercentChange1h;
                        currency.PercentChange24h = latestQuotesResult.PercentChange24h;
                        currency.PercentChange7d  = latestQuotesResult.PercentChange7d;
                        currency.LastUpdated      = latestQuotesResult.LastUpdated;

                        await _context.SaveChangesAsync(cancellationToken);

                        currencyDTO.UnitPriceInUSD = latestQuotesResult.Price;
                    }
                }
                else
                {
                    //currencyDTO.Price = currencyDTO.UnitPriceInUSD / currencyDTO.Portfolio.MemberPreferredFiatCurrencyUnitPriceInUSD;
                    currencyDTO.Price = currencyDTO.UnitPriceInUSD / result.MemberPreferredFiatCurrencyUnitPriceInUSD;
                }

                #region Hitung Percentage Lifetime
                decimal difference = 0m;

                #region Kalau pakai Average Unit Price in Preferred Fiat Currency
                if (currencyDTO.AverageBuyPriceInPreferredFiatCurrency > 0)
                {
                    difference = currencyDTO.Price - currencyDTO.AverageBuyPriceInPreferredFiatCurrency;
                    currencyDTO.PercentChangeLifetime = Convert.ToSingle(difference / currencyDTO.AverageBuyPriceInPreferredFiatCurrency);
                }
                else
                {
                    currencyDTO.PercentChangeLifetime = 0f;
                }
                #endregion

                #region SelectedPercentChange
                currencyDTO.SelectedPercentChange = result.MemberPreferredTimeFrameName switch
                {
                    TimeFrameName.OneHour => currencyDTO.PercentChange1h,
                    TimeFrameName.OneDay => currencyDTO.PercentChange24h,
                    TimeFrameName.OneWeek => currencyDTO.PercentChange7d,
                    _ => currencyDTO.PercentChangeLifetime
                };
                #endregion

                #region Kalau pakai First Transaction
                //var firstTransaction = currency.IncomingTransactions.FirstOrDefault();

                //if (firstTransaction != null)
                //{
                //    decimal firstTransactionUnitPriceInPreferredFiatCurrency = 1m;

                //    if (result.MemberPreferredFiatCurrencySymbol == CurrencySymbol.USD)
                //    {
                //        firstTransactionUnitPriceInPreferredFiatCurrency = firstTransaction.UnitPriceInUSD;
                //    }
                //    else
                //    {
                //        var firstTransactionUnitPriceInPreferredFiatCurrencyPriceConversionResult = _cryptoService.Send(new PriceConversionParameter
                //        {
                //            FromCurrencySymbol = CurrencySymbol.USD,
                //            ToCurrencySymbol = result.MemberPreferredFiatCurrencySymbol,
                //            Amount = firstTransaction.UnitPriceInUSD
                //        });

                //        firstTransactionUnitPriceInPreferredFiatCurrency = firstTransactionUnitPriceInPreferredFiatCurrencyPriceConversionResult.IsSuccessful ? firstTransactionUnitPriceInPreferredFiatCurrencyPriceConversionResult.Amount : 1m;
                //    }

                //    difference = currency.CurrentUnitPriceInPreferredFiatCurrency - firstTransactionUnitPriceInPreferredFiatCurrency;
                //    currencyDTO.PercentChangeLifetime = difference / firstTransactionUnitPriceInPreferredFiatCurrency;
                //}
                //else
                //{
                //    currencyDTO.PercentChangeLifetime = 0m;
                //}
                #endregion

                #endregion
            }

            return(result);
        }
    }
        public async Task <CryptoLatestQuotesResult> GetLatestQuoteAsync(string fromCurrencySymbol, string toCurrencySymbol)
        {
            var result = new CryptoLatestQuotesResult();

            string uri = $"{CryptoServiceProviders.CoinMarketCap.CryptoCurrencyQuotesLatestEndpointURL}?symbol={fromCurrencySymbol}&convert={toCurrencySymbol}";

            _logger.LogDebug($"{nameof(GetLatestQuoteAsync)}");
            _logger.LogDebug(uri);

            IDictionary <string, string> additionalHttpHeaders = new Dictionary <string, string>
            {
                { "X-CMC_PRO_API_KEY", APIKey }
            };

            try
            {
                var cryptoCurrencyQuotesLatestResultJSON = await ExternalWebAPIRequestor.GetAsync <CryptoCurrencyQuotesLatestResultJSON>(uri, additionalHttpHeaders);

                if (cryptoCurrencyQuotesLatestResultJSON.status.error_code == 0)
                {
                    foreach (var data in cryptoCurrencyQuotesLatestResultJSON.data)
                    {
                        if (data.Key == fromCurrencySymbol)
                        {
                            foreach (var quote in data.Value.quote)
                            {
                                if (quote.Key == toCurrencySymbol)
                                {
                                    result = new CryptoLatestQuotesResult
                                    {
                                        Rank             = data.Value.cmc_rank ?? 0,
                                        Price            = quote.Value.price,
                                        Volume24h        = quote.Value.volume_24h ?? 0m,
                                        PercentChange1h  = quote.Value.percent_change_1h ?? 0f,
                                        PercentChange24h = quote.Value.percent_change_24h ?? 0f,
                                        PercentChange7d  = quote.Value.percent_change_7d ?? 0f,
                                        LastUpdated      = quote.Value.last_updated,
                                    };
                                }
                            }
                        }
                    }
                }
                else
                {
                    result = null;
                }
            }
            catch (WebException ex) when((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.BadRequest)
            {
                //result = null;
                throw;
            }
            catch (WebException ex) when((ex.Response as HttpWebResponse)?.StatusCode != HttpStatusCode.OK)
            {
                //result = null;
                throw;
            }

            return(result);
        }