/** // // flexibleRetirementPlanner Source Code Module. // // Copyright 2006-2008 by Jim Richmond // You may not use, distribute, or modify this code without the consent of the copyright holder. // // for more information, visit: http://www.flexibleRetirementPlanner.com // // // The attached is a partial listing of source code provided for example only. This code is // not intended to be useable in its current form. Although this example was reflective of the // actual code at the time it was copied, the actual source code used by the planner may differ. // */ // Main simulation method. public void run() { int iteration, year; double totalBalance, previousBalance, balanceAtRetirementStart; double previousPercentOfExpensesFunded, percentOfExpensesFunded; double inflation, portfolioReturn; int successCount = 0; int backToWorkCount = 0; double yearlyTaxableBalance[][] = new double[yearsInPlan + 1][simIterations + 1]; double yearlyTaxDeferredBalance[][] = new double[yearsInPlan + 1][simIterations + 1]; double yearlyTaxFreeBalance[][] = new double[yearsInPlan + 1][simIterations + 1]; double yearlyPPMaintained[][] = new double[yearsInPlan + 1][simIterations + 1]; double yearlyWithdrawalAmount[][] = new double[yearsInPlan + 1][simIterations + 1]; MersenneTwisterFast rng = new MersenneTwisterFast(); balanceAtRetirementStart = 0; // Gets filled in later for (iteration = 1; iteration <= simIterations; iteration++) { int yearPlanFailed = 0; int backToWorkEndYear = 0; // Setup to keep track of portfolio balances taxableBalance = StartingTaxableBalance; taxDeferredBalance = StartingTaxDeferredBalance; taxFreeBalance = startingTaxFreeBalance; totalBalance = taxableBalance + taxDeferredBalance + taxFreeBalance; previousBalance = totalBalance; // Keep track of the percent of requested expenses that get funded // based on results of applying withdrawal policy previousPercentOfExpensesFunded = 1; percentOfExpensesFunded = 1; for (year = 1; year <= yearsInPlan; year++) { YearlyInputs inData = yearlyInputs[year]; double netWithdrawalAmount; if (year == retirementStartYear) { // Keep track of what portfolio is worth at start of retirement for // use in spending policy computations. balanceAtRetirementStart = totalBalance; } // Determine withdrawal amount needed for this year and deduct from portfolio // if retiree is back to work (because of low portfolio), no withdrawal occurs if (year < backToWorkEndYear) { netWithdrawalAmount = 0; } else { netWithdrawalAmount = getNetWithdrawalAmount(percentOfExpensesFunded, inData); } double grossWithdrawalAmount = ProcessAnnualWithdrawal( netWithdrawalAmount, year); // Get normally distributed random values for inflation and return if (inData.InflationStdev != 0) { inflation = (rng.nextGaussian() * inData.InflationStdev) + inData.InflationAvg; } else { inflation = inData.InflationAvg; } portfolioReturn = (rng.nextGaussian() * inData.ReturnStdev) + inData.ReturnAvg; // Adjust portfolio balances for new investments, investment returns, // taxes on gains, and inflation ProcessPortfolioGrowth(year, portfolioReturn, inflation); totalBalance = taxableBalance + taxFreeBalance + taxDeferredBalance; // Compute percent of needed spending that will get funded next year // based on selected spending policy and how things are going so far if ( (year >= retirementStartYear) && (totalBalance > 0)) { if ( (backToWorkEndYear == 0) && (btwTriggerPercent != 0)) { backToWorkEndYear = checkIfBtwNeeded(year, totalBalance, balanceAtRetirementStart); } if (spendingPolicy == CONSERVATIVE_SPENDING_POLICY) { percentOfExpensesFunded = SimCalc. ComputeFundingUsingConservativePolicy( totalBalance, previousBalance, balanceAtRetirementStart, previousPercentOfExpensesFunded, inflation * spendingPolicySensitivity, spendingPercentFloor, spendingPercentCeiling); } else if (spendingPolicy == FLEXIBLE_SPENDING_POLICY) { percentOfExpensesFunded = SimCalc.ComputeFundingUsingFlexiblePolicy( totalBalance, previousBalance, balanceAtRetirementStart, previousPercentOfExpensesFunded, inflation * spendingPolicySensitivity, spendingPercentFloor, spendingPercentCeiling); } // else - for Stable PP, there's no change in withdrawal percent, // which means it stays at the same level as before } if ( (totalBalance <= 0) && (yearPlanFailed == 0)) { yearPlanFailed = year; } previousPercentOfExpensesFunded = percentOfExpensesFunded; previousBalance = totalBalance; yearlyOutputs[year].AvgReturn += portfolioReturn; yearlyOutputs[year].AvgInflation += inflation; yearlyOutputs[year].AvgIraPenaltyPaid += iraPenaltyPaid; // Store some data for use in median calculations later yearlyTaxableBalance[year][iteration] = taxableBalance; yearlyTaxDeferredBalance[year][iteration] = taxDeferredBalance; yearlyTaxFreeBalance[year][iteration] = taxFreeBalance; if (totalBalance > 0) { yearlyPPMaintained[year][iteration] = percentOfExpensesFunded; yearlyWithdrawalAmount[year][iteration] = grossWithdrawalAmount; } else { yearlyPPMaintained[year][iteration] = 0; yearlyWithdrawalAmount[year][iteration] = 0; } } // for year if ((totalBalance > 0) && (yearPlanFailed == 0) ){ successCount = successCount + 1; } else { yearlyOutputs[yearPlanFailed].PercentOfFailures += 1; } if (backToWorkEndYear > 0) { backToWorkCount += 1; } } // for iteration lowestAvgPPMaintained = 1000.0; highestAvgPPMaintained = 0.0; for (year = 1; year <= yearsInPlan; year++) { YearlyOutputs outData = yearlyOutputs[year]; outData.AvgTaxableBalance = getMedian(yearlyTaxableBalance[year], simIterations); outData.AvgTaxDeferredBalance = getMedian(yearlyTaxDeferredBalance[year], simIterations); outData.AvgTaxFreeBalance = getMedian(yearlyTaxFreeBalance[year], simIterations); outData.AvgWithdrawal = getMedian(yearlyWithdrawalAmount[year], simIterations); outData.AvgPercentPPMaintained = getMedian(yearlyPPMaintained[year], simIterations); if (year >= retirementStartYear) { if (outData.AvgPercentPPMaintained < lowestAvgPPMaintained) { lowestAvgPPMaintained = outData.AvgPercentPPMaintained; } if (outData.AvgPercentPPMaintained > highestAvgPPMaintained) { highestAvgPPMaintained = outData.AvgPercentPPMaintained; } } outData.PercentOfFailures /= (simIterations - successCount); outData.AvgReturn /= simIterations; outData.AvgInflation /= simIterations; outData.AvgIraPenaltyPaid /= simIterations; } probabilityOfBackToWork = (double) backToWorkCount / (double) simIterations; probabilityOfSuccess = (double) successCount / (double) simIterations; if (probabilityOfSuccess > 0.999) { probabilityOfSuccess = 0.999; // Don't let probability equal 100% // we're just not that sure of ourselves!!! } outputValid = true; } public void ProcessPortfolioGrowth(int year, double portfolioReturn, double inflation) { // Note: The order these adjustments are made can dramatically affect outcomes. // 1) A portfolio gain is computed on the original balance // 2) Investment taxes are charged on the yearly portfolio gain if required // 3) An inflation adjustment is computed by reducing the original balance by // the inflation rate. // 4) Any new investment funds are added in. double afterTaxGain, inflationAdjustment; double taxRate = yearlyInputs[year].InvestmentTaxRate; if (taxableBalance >= 0) { if (portfolioReturn > 0) { afterTaxGain = (taxableBalance * portfolioReturn) * (1 - taxRate); } else { // Even though it seems strange to ease losses with a tax break, // doing so evens out the effects of taxing the entire gain each // year rather than only taxing the realized gains. afterTaxGain = (taxableBalance * portfolioReturn) * (1-taxRate); } inflationAdjustment = taxableBalance * inflation; taxableBalance = (taxableBalance + yearlyInputs[year].NewTaxableInvestment + afterTaxGain - inflationAdjustment); } if (taxableBalance < 0) { taxableBalance = 0; } if (taxFreeBalance >= 0) { afterTaxGain = (taxFreeBalance * portfolioReturn); inflationAdjustment = taxFreeBalance * inflation; taxFreeBalance = (taxFreeBalance + yearlyInputs[year].NewTaxFreeInvestment + afterTaxGain - inflationAdjustment); } if (taxFreeBalance < 0) { taxFreeBalance = 0; } if (taxDeferredBalance >= 0) { afterTaxGain = (taxDeferredBalance * portfolioReturn); inflationAdjustment = taxDeferredBalance * inflation; taxDeferredBalance = (taxDeferredBalance + yearlyInputs[year].NewTaxDeferredInvestment + afterTaxGain - inflationAdjustment); } if (taxDeferredBalance < 0) { taxDeferredBalance = 0; } } double getNetWithdrawalAmount(double percentOfExpensesFunded, YearlyInputs inData) { return (percentOfExpensesFunded * inData.SpendingRequested) - inData.ExtraTaxFreeIncome - (inData.ExtraTaxableIncome * (1 - inData.IncomeTaxRate)); } double ProcessAnnualWithdrawal(double netWithdrawalNeeded, int year) { YearlyInputs inData = yearlyInputs[year]; double grossWithdrawalAmount = netWithdrawalNeeded; if (netWithdrawalNeeded <= 0) { // More income came in than the withdrawal needed, add extra to portfolio taxableBalance += -netWithdrawalNeeded; } else { taxableBalance -= netWithdrawalNeeded; if (taxableBalance < 0) { // Need to get money from tax free funds double shortfall = 0 - taxableBalance; taxableBalance = 0; if (year >= iraWithdrawalStartYear) { taxFreeBalance -= shortfall; } else { // Need to make early IRA withdrawal, have to pay taxes and penalty double taxesDue = (shortfall * inData.CompoundedTaxAndIraPenaltyRate); taxFreeBalance -= (shortfall + taxesDue); grossWithdrawalAmount += taxesDue; iraPenaltyPaid = 1; } if (taxFreeBalance < 0) { shortfall = 0 - taxFreeBalance; taxFreeBalance = 0; // Need to pay income taxes on withdrawals from tax deferred funds if (year >= iraWithdrawalStartYear) { double taxesDue = shortfall * inData.CompoundedIncomeTaxRate; taxDeferredBalance -= (shortfall + taxesDue); grossWithdrawalAmount += taxesDue; } else { // Need to make early IRA withdrawal, have to pay penalty // but since penalty and taxes were already computed and subtracted above, they // are skipped in this case double taxesDue = 0; taxDeferredBalance -= (shortfall + taxesDue); // grossWithdrawalAmount += taxesDue; iraPenaltyPaid = 1; } if (taxDeferredBalance < 0) { taxDeferredBalance = 0; // Bummer, ran out of money.. grossWithdrawalAmount = 0; } } } } return grossWithdrawalAmount; } // See if retiree needs to go back to work as a result of portfolio balance // falling below preset threshold. int checkIfBtwNeeded(int year, double totalBalance, double BalanceAtRetirementStart) { int backToWorkEndYear = 0; if ( (year < btwMaxYear) && ( (totalBalance / BalanceAtRetirementStart) < btwTriggerPercent)) { backToWorkEndYear = year + btwDuration; if (backToWorkEndYear > btwMaxYear) backToWorkEndYear = 0; } return backToWorkEndYear; }