// Method used to calculate the financial goal. public static string CalculateGoal(double withdrawlRate, double taxRate, decimal currentInvestments, decimal retirementSpeding, decimal initialSavings, double returns, double savings, bool pegToInflation, double inflation) { double time = 0; decimal goal = currentInvestments; decimal fix = (decimal)((1 + taxRate) / withdrawlRate); decimal adjustedRetirementSpeding = retirementSpeding; if (!pegToInflation) { goal = retirementSpeding * fix; return(goal.ToString("C0")); } while (goal / fix < adjustedRetirementSpeding) { goal = currentInvestments; if (savings > 0) { goal += FinanceCalculations.FutureVariableAnnuityValue(initialSavings, time, returns, savings, 365, 0, recurringInvestingFrequency); } else { goal += FinanceCalculations.FutureFixedAnnuityValue(initialSavings, time, returns, 365, 0, recurringInvestingFrequency); } adjustedRetirementSpeding = FinanceCalculations.FutureValue(retirementSpeding, time, inflation); time++; } return(goal.ToString("C0")); }
// Method used to find the future value of the repeated payments. private decimal CalculateAnnuity(double length, double rate) { int frequency = int.Parse(txtCompoundingFrequency.Text); decimal payment = decimal.Parse(txtAnnuityPayment.Text, NumberStyles.Currency); int immediately = chkPaymentAt.Checked ? 1 : 0; int paymentFrequency = int.Parse(txtPaymentFrequency.Text); double growth = double.Parse(txtPaymentGrowth.Text) / 100; if (growth == 0) { return(FinanceCalculations.FutureFixedAnnuityValue(payment, length, rate, frequency, immediately, paymentFrequency)); } else { return(FinanceCalculations.FutureVariableAnnuityValue(payment, length, rate, growth, frequency, immediately, paymentFrequency)); } }
// An interative function that finds the rate to meet the goal within a given time. public static double GetRateOfGrowth(decimal goal, decimal startingBal, decimal initialSavings, double growth, double time) { decimal bal = startingBal; double rate = 0; while (bal < goal) { bal = FinanceCalculations.FutureValue(startingBal, time, rate, 365); if (growth > 0) { bal += FinanceCalculations.FutureVariableAnnuityValue(initialSavings, time, rate, growth, 365, 0, recurringInvestingFrequency); } else { bal += FinanceCalculations.FutureFixedAnnuityValue(initialSavings, time, rate, 365, 0, recurringInvestingFrequency); } rate += RATE_STEP; } return(rate); }
// An interative function that finds the time to meet the goal. public static double GetTimeToGoal(decimal goal, decimal principal, decimal payment, double growth, double savingsGrowth) { double time = 0; decimal bal = 0; while (bal < goal) { bal = 0; bal += FinanceCalculations.FutureValue(principal, time, growth); if (savingsGrowth > 0) { bal += FinanceCalculations.FutureVariableAnnuityValue(payment, time, growth, savingsGrowth, 365, 0, recurringInvestingFrequency); } else { bal += FinanceCalculations.FutureFixedAnnuityValue(payment, time, growth, 365, 0, recurringInvestingFrequency); } time += TIME_STEP; } return(time); }
private void GenerateReturns() { // Set parameters bool incomeGrowthExcessOfInflation = chkInflationIncomeGrowthPeg.Checked; int projectionType = cboProjection.SelectedIndex; int paymentFrequency = Investing.recurringInvestingFrequency; double bondAllocation = double.Parse(txtBondFraction.Text) / 100; double stockAllocation = double.Parse(txtStockFraction.Text) / 100; double inflationMean = double.Parse(txtInflation.Text) / 100; double incomeGrowthRate = double.Parse(txtIncomeGrowth.Text) / 100 + (incomeGrowthExcessOfInflation ? inflationMean : 0); double savingsGrowthRateFraction = double.Parse(txtSavingFractionGrowth.Text) / 100; double bReturns = double.Parse(txtBondReturns.Text) / 100; double sReturns = double.Parse(txtStockReturns.Text) / 100; double savingsGrowthRate = savingsGrowthRateFraction * incomeGrowthRate; double time = 0; decimal currentInvestments = decimal.Parse(txtPrincipal.Text, NumberStyles.Currency); decimal initialSavings = decimal.Parse(txtIncome.Text, NumberStyles.Currency) - decimal.Parse(txtSpending.Text, NumberStyles.Currency); decimal goal = decimal.Parse(txtSavingsGoal.Text, NumberStyles.Currency); carcInvestment.Series.Clear(); // 0 = fixed returns, 1 = monte-carlo; TODO: 2 = historical cycles if (projectionType == 0) { // Fixed Returns double averageReturn = Investing.PortfolioWeightedAverageReturn(bondAllocation, stockAllocation, bReturns, sReturns); double timeToRetire = Investing.GetTimeToGoal(goal, currentInvestments, initialSavings, averageReturn, savingsGrowthRate); decimal[] total = new decimal[(int)timeToRetire + 5]; decimal[] returns = new decimal[(int)timeToRetire + 5]; decimal[] principal = new decimal[(int)timeToRetire + 5]; // Generate return arrays for (int i = 0; i < (int)timeToRetire + 5; i++) { principal[i] += currentInvestments; total[i] += FinanceCalculations.FutureValue(currentInvestments, i, averageReturn, 365); if (savingsGrowthRate > 0) { total[i] += FinanceCalculations.FutureVariableAnnuityValue(initialSavings, i, averageReturn, savingsGrowthRate, 365, 0, paymentFrequency); principal[i] += FinanceCalculations.FutureVariableAnnuityValue(initialSavings, i, 0, savingsGrowthRate, 365, 0, paymentFrequency); } else { total[i] += FinanceCalculations.FutureFixedAnnuityValue(initialSavings, i, averageReturn, 365, 0, paymentFrequency); principal[i] += initialSavings * i; } returns[i] = total[i] - principal[i]; } GenerateChart(total, "Total"); GenerateChart(returns, "Return"); GenerateChart(principal, "Principal", goal, timeToRetire); time = timeToRetire; } else if (projectionType == 1) { // Monte-Carlo int simulations = 50000; //double inflationVolatility = 0.0119; double stockVolatility = 0.5; double bondVolatility = 0.03; double[] percentileList = { 0.9, 0.75, 0.5, 0.25, 0.1 }; double[] lengths = new double[simulations]; double[] percentileLengths = new double[percentileList.Length]; double[] averageRatesOfGrowth = new double[percentileList.Length]; decimal[][] percentileReturns = new decimal[percentileList.Length][]; //Run the simulations to find lengths for (int i = 0; i < simulations; i++) { decimal x = currentInvestments; Random r = new Random(DateTime.Now.Ticks.GetHashCode()); lengths[i] = 0; while (x < goal) { // Randomly generated return of the portfolio double pReturn = Investing.CalculateRandomPortfolioReturn(bReturns, bondVolatility, bondAllocation, sReturns, stockVolatility, stockAllocation, r); // Caluculate portfolio value at the end of the year x *= 1 + (decimal)pReturn; // Add savings to portfolio if (savingsGrowthRate > 0) { x += FinanceCalculations.FutureValue(initialSavings, lengths[i], savingsGrowthRate, 1); } else { x += initialSavings * i; } lengths[i]++; } } for (int i = 0; i < percentileList.Length; i++) { // Get length percentiles percentileLengths[i] = FinanceCalculations.Percentile(lengths, percentileList[i]); // Find equivelant growth rate averageRatesOfGrowth[i] = Investing.GetRateOfGrowth(goal, currentInvestments, initialSavings, savingsGrowthRate, percentileLengths[i]); // Initialize the array with the 90th percentile in length + 2 percentileReturns[i] = new decimal[(int)percentileLengths[0] + 2]; //Calculate return array for (int j = 0; j < (int)percentileLengths[0] + 2; j++) { percentileReturns[i][j] += FinanceCalculations.FutureValue(currentInvestments, j, averageRatesOfGrowth[i], 365); if (savingsGrowthRate > 0) { percentileReturns[i][j] += FinanceCalculations.FutureVariableAnnuityValue(initialSavings, j, averageRatesOfGrowth[i], savingsGrowthRate, 365, 0, paymentFrequency); } else { percentileReturns[i][j] += FinanceCalculations.FutureFixedAnnuityValue(initialSavings, j, averageRatesOfGrowth[i], 365, 0, paymentFrequency); } } } for (int i = 0; i < percentileLengths.Length; i++) { int k = percentileList.Length - i - 1; GenerateChart(percentileReturns[k], (100 * percentileList[i]).ToString("0") + "th Percentile Returns", goal, percentileLengths[2]); } time = percentileLengths[2]; } PrintResults(time); }