// Gets the prices for all of the securities in a portfolio from IEX Cloud private async Task <ICollection <PortfolioSecurity> > GetPrices(PortfolioDetailsViewModel viewModel) { string token = GetToken(); var client = _clientFactory.CreateClient(); var timePeriod = viewModel.TimePeriod; foreach (PortfolioSecurity ps in viewModel.Portfolio.PortfolioSecurities) { var request = new HttpRequestMessage(HttpMethod.Get, $"https://cloud.iexapis.com/stable/stock/{ps.Security.Ticker}/chart/{timePeriod}/?chartCloseOnly=true&chartInterval=21&token={token}"); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { // Convert the response into price objects and save as a list to the PS's List<Price> var json = await response.Content.ReadAsStreamAsync(); List <IEXPrice> IEXPrices = await System.Text.Json.JsonSerializer.DeserializeAsync <List <IEXPrice> >(json); foreach (IEXPrice p in IEXPrices) { ps.Prices.Add(new Price { Date = p.Date, AdjClose = p.Close }); } } } return(viewModel.Portfolio.PortfolioSecurities); }
// GET: Portfolios/Details/5 public async Task <IActionResult> Details(int?id, string timePeriod) { var viewModel = new PortfolioDetailsViewModel(); viewModel.TimePeriod = timePeriod; if (id == null) { return(NotFound()); } viewModel.Portfolio = await _context.Portfolios .Include(p => p.User) .Include(p => p.PortfolioSecurities) .ThenInclude(p => p.Security) .Include(p => p.PortfolioSecurities) .ThenInclude(p => p.AssetClass) .FirstOrDefaultAsync(p => p.Id == id); // Asset Allocation data for pie chart Dictionary <string, int> assetAllocation = new Dictionary <string, int>(); foreach (PortfolioSecurity ps in viewModel.Portfolio.PortfolioSecurities) { if (!assetAllocation.ContainsKey(ps.AssetClass.Name)) { assetAllocation[ps.AssetClass.Name] = 0; } assetAllocation[ps.AssetClass.Name] += ps.Weight; } // Convert dictionary to json and assign to viewModel viewModel.AssetAllocationKeys = JsonConvert.SerializeObject(assetAllocation.Keys); viewModel.AssetAllocationValues = JsonConvert.SerializeObject(assetAllocation.Values); // Get the prices for all the securities in the portfolio if (timePeriod != null) { viewModel.Portfolio.PortfolioSecurities = await GetPrices(viewModel); List <DateTime> dates = viewModel.Portfolio.PortfolioSecurities.FirstOrDefault().Prices .Select(p => p.Date).ToList(); // Iterate over all the dates returned for prices and make entries in Dictionaries foreach (DateTime date in dates) { viewModel.PortfolioValues[date] = 100_000; viewModel.MonthlyReturns[date] = 0; viewModel.CumulativeReturns[date] = 0; } // Iterate over all the PortfolioSecurities, calculate the weighted monthly return and cumulative return for each security and add it to the dictionary decimal monthlyReturn = 0; decimal cumulativeReturn = 0; foreach (PortfolioSecurity ps in viewModel.Portfolio.PortfolioSecurities) { for (int i = 0; i < ps.Prices.Count(); i++) { if (i != 0) { monthlyReturn = (ps.Prices[i].AdjClose / ps.Prices[i - 1].AdjClose) - 1; viewModel.MonthlyReturns[ps.Prices[i].Date] += monthlyReturn * ps.Weight / 100; cumulativeReturn = (ps.Prices[i].AdjClose / ps.Prices[0].AdjClose) - 1; viewModel.CumulativeReturns[ps.Prices[i].Date] += cumulativeReturn * ps.Weight / 100; } } } // Iterate over the viewModel's CumulativeReturns dictionary and update the PortfolioValues dictionary with the appropriate value //DateTime firstDate = viewModel.PortfolioValues.Keys.First(); //viewModel.PortfolioValues[firstDate] = 100_000; foreach (KeyValuePair <DateTime, decimal> kvp in viewModel.CumulativeReturns) { viewModel.PortfolioValues[kvp.Key] = 100_000 * (1 + kvp.Value); } decimal begValue = viewModel.PortfolioValues.First().Value; decimal endValue = viewModel.PortfolioValues.Last().Value; viewModel.Return = ((endValue / begValue) - 1) * 100; decimal numYears = dates.Count() / 12; viewModel.CAGR = (decimal)(Math.Pow((double)(endValue / begValue), (double)(1 / numYears))) - 1; viewModel.CAGR = viewModel.CAGR * 100; // Calculate the StdDeviation List <decimal> monthlyReturns = viewModel.MonthlyReturns.Values.ToList(); // Remove the first value since the 1st period has no return monthlyReturns.Remove(monthlyReturns[0]); decimal monthlyStdDev = StdDev(monthlyReturns); // Get estimated annualized StdDev viewModel.StdDeviation = monthlyStdDev * (decimal)Math.Sqrt(12); viewModel.StdDeviation = viewModel.StdDeviation * 100; // Set dates viewModel.StartDate = viewModel.PortfolioValues.First().Key; viewModel.EndDate = viewModel.PortfolioValues.Last().Key; // Convert portfolio values to JSON and set in the viewModel viewModel.ChartData = JsonConvert.SerializeObject(viewModel.PortfolioValues); return(View(viewModel)); } if (viewModel.Portfolio == null) { return(NotFound()); } return(View(viewModel)); }