Retirement Spending Strategy Model (Conceptual Simulator)

Initial Retirement Setup

$
$

Portfolio & Market Assumptions

%
Blended return for your overall portfolio (stocks, bonds, etc.).
%
For informational context; projections are deterministic based on E(R).
%

Retirement Spending Strategy

$

Retirement Portfolio Projections

Summary of projections will appear here.

Portfolio Value Over Time (Nominal & Real)

Portfolio value chart appears here.

Annual Withdrawals Over Time (Nominal & Real)

Withdrawal chart appears here.

Year-by-Year Projection Details:

Projection table will appear here.

Value chart.

'; withdrawalChartContainer.innerHTML = '

Withdrawal chart.

'; tableContainer.innerHTML = '

Projection table.

'; return; } const r = rspsm_results; const s = r.summary; const i = r.inputsSnapshot; let longevityMessage = ""; if (s.portfolioLasts) { longevityMessage = `Portfolio lasts for the full ${i.retirementDurationYears}-year retirement period.`; if (i.desiredLegacy > 0) { longevityMessage += s.meetsLegacy ? ` It also meets the desired legacy of ${rspsm_formatCurrency(i.desiredLegacy)}.` : ` However, it does not meet the desired legacy of ${rspsm_formatCurrency(i.desiredLegacy)}.`; } } else { longevityMessage = `Portfolio is projected to be depleted in Year ${s.portfolioDepletedInYear}.`; } summaryDiv.innerHTML = `

Projection Summary (Over ${i.retirementDurationYears} Years)

Portfolio Longevity: ${longevityMessage}

Final Nominal Portfolio Value: ${rspsm_formatCurrency(s.finalNominalValue)}
Final Real Portfolio Value (Today's Dollars): ${rspsm_formatCurrency(s.finalRealValue)}
Total Nominal Withdrawals: ${rspsm_formatCurrency(s.totalNominalWithdrawals)}
Total Real Withdrawals (Today's Dollars): ${rspsm_formatCurrency(s.totalRealWithdrawals)}
`; // Charts const nominalValues = [i.initialPortfolioValue].concat(r.projectionsTable.map(p => p.endValueNominal)); const realValues = [i.initialPortfolioValue].concat(r.projectionsTable.map(p => p.endValueReal)); // Real value of initial is itself rspsm_renderLineChart(nominalValues, realValues, 'Portfolio Value ($)', valueChartContainer); const nominalWithdrawals = [0].concat(r.projectionsTable.map(p => p.withdrawalNominal)); // Start with 0 withdrawal at year 0 const realWithdrawals = [0].concat(r.projectionsTable.map(p => p.withdrawalReal)); rspsm_renderLineChart(nominalWithdrawals, realWithdrawals, 'Annual Withdrawal ($)', withdrawalChartContainer, '#FBBF24', '#F59E0B'); // Table let tableHTML = ``; r.projectionsTable.forEach(p => { tableHTML += ``; }); tableHTML += `
YearStart Value (Nom.)Withdrawal (Nom.)Growth (Nom.)End Value (Nom.)End Value (Real)Withdrawal (Real)
${p.year} ${rspsm_formatCurrency(p.startValueNominal)} ${rspsm_formatCurrency(p.withdrawalNominal)} ${rspsm_formatCurrency(p.growthNominal)} ${rspsm_formatCurrency(p.endValueNominal)} ${rspsm_formatCurrency(p.endValueReal)} ${rspsm_formatCurrency(p.withdrawalReal)}
`; tableContainer.innerHTML = tableHTML; const pdfBtn = document.getElementById('rspsm_downloadPdfButton'); if(pdfBtn) pdfBtn.style.display = 'block'; } function rspsm_renderLineChart(data1, data2, yAxisLabel, container, color1 = '#A78BFA', color2 = '#6EE7B7') { container.innerHTML = ''; const svgWidth = Math.min(800, (rspsm_getEl('rspsmContainer_main').offsetWidth || 400) / 2 - 30); const svgHeight = 250; const m = {top: 20, right: 20, bottom: 40, left: 75}; const w = svgWidth - m.left - m.right; const h = svgHeight - m.top - m.bottom; const allYValues = data1.concat(data2); const yMin = Math.min(0, ...allYValues.filter(v=>!isNaN(v) && v !== null)); const yMax = Math.max(...allYValues.filter(v=>!isNaN(v) && v !== null)); const xMax = data1.length -1; const xScale = x => (x / (xMax === 0 ? 1 : xMax)) * w; const yScale = y => h - ((y - yMin) / (yMax - yMin === 0 ? 1 : yMax - yMin)) * h; let path1Data = "M" + data1.map((val, i) => `${xScale(i).toFixed(2)},${yScale(val).toFixed(2)}`).join(" L"); let path2Data = data2 ? "M" + data2.map((val, i) => `${xScale(i).toFixed(2)},${yScale(val).toFixed(2)}`).join(" L") : ""; container.innerHTML = ` Years (0 - ${xMax}) ${yAxisLabel} ${rspsm_formatCurrency(yMax,0)} ${rspsm_formatCurrency(yMin,0)} ${data2 ? `` : ''} ${yAxisLabel.includes('Value') ? 'Nominal' : 'Nominal'} ${data2 ? ` ${yAxisLabel.includes('Value') ? 'Real' : 'Real'}` : ''} `; } // --- PDF Generation --- function rspsm_downloadPDF() { if (!rspsm_results) { alert("Please run the projection first."); return; } if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Core PDF library (jsPDF) is not loaded.'); console.error('jsPDF library not found.'); return; } const { jsPDF: JSPDF } = window.jspdf; const doc = new JSPDF(); if (typeof doc.autoTable !== 'function') { alert('PDF Table plugin (jsPDF-AutoTable) not loaded. Tables may be missing.'); console.error('doc.autoTable is not a function.'); } let y = 15; const m = 15; const r = rspsm_results; const i = r.inputsSnapshot; function addLine(text, size, style = 'normal', indent = 0, spacing = 2.5) { const cw = doc.internal.pageSize.getWidth() - (2 * m); if (y > 275) { doc.addPage(); y = m; } doc.setFontSize(size); doc.setFont(undefined, style); const lines = doc.splitTextToSize(text, cw - indent); doc.text(lines, m + indent, y); y += (lines.length * (size * 0.35)) + spacing; } addLine(`Retirement Spending Strategy Plan: ${i.strategyName || 'N/A'}`, 16, 'bold', 0, 5); addLine(`Report Date: ${new Date().toLocaleDateString()}`, 9, 'italic', 0, 5); addLine("Initial Setup & Assumptions:", 12, 'bold', 0, 3); let inputData = [ ["Initial Portfolio Value:", rspsm_formatCurrency(i.initialPortfolioValue)], ["Retirement Duration:", `${i.retirementDurationYears} years`], ["Desired Legacy:", rspsm_formatCurrency(i.desiredLegacy)], ["Portfolio Nominal E(R):", rspsm_formatPercent(i.expectedNominalReturn*100)], ["Assumed Inflation (CPI):", rspsm_formatPercent(i.inflationRate*100)], ["Spending Strategy:", i.spendingStrategyType.replace(/([A-Z])/g, ' $1').trim()], ]; if(i.spendingStrategyType === 'fixedAmount') inputData.push(["Initial Annual Withdrawal:", rspsm_formatCurrency(i.initialAnnualWithdrawalAmount)]); if(i.spendingStrategyType.includes('Percent')) inputData.push(["Withdrawal Rate:", rspsm_formatPercent(i.withdrawalPercentage*100)]); if(i.spendingStrategyType !== 'percentCurrent') inputData.push(["Adjust Withdrawals for Inflation:", i.adjustWithdrawalsForInflation ? 'Yes' : 'No']); if (typeof doc.autoTable === 'function') { doc.autoTable({ startY: y, body: inputData, theme: 'plain', styles: {fontSize: 9, cellPadding: 1.2}, columnStyles: {0:{fontStyle:'bold'}}}); y = doc.lastAutoTable.finalY + 6; } else { inputData.forEach(row => addLine(`${row[0]} ${row[1]}`, 9)); y+=5; } addLine("Projection Summary:", 12, 'bold', 0, 3); const s = r.summary; let summaryData = [ ["Portfolio Longevity:", s.portfolioLasts ? `Lasts for ${i.retirementDurationYears} years` : `Depleted in Year ${s.portfolioDepletedInYear}`], ["Final Nominal Portfolio Value:", rspsm_formatCurrency(s.finalNominalValue)], ["Final Real Portfolio Value (Today's $):", rspsm_formatCurrency(s.finalRealValue)], ["Meets Desired Legacy of "+rspsm_formatCurrency(i.desiredLegacy)+":", s.meetsLegacy ? "Yes" : "No"], ["Total Nominal Withdrawals:", rspsm_formatCurrency(s.totalNominalWithdrawals)], ["Total Real Withdrawals (Today's $):", rspsm_formatCurrency(s.totalRealWithdrawals)], ]; if (typeof doc.autoTable === 'function') { doc.autoTable({ startY: y, body: summaryData, theme: 'plain', styles: {fontSize: 9, cellPadding: 1.2}, columnStyles: {0:{fontStyle:'bold'}}}); y = doc.lastAutoTable.finalY + 6; } else { summaryData.forEach(row => addLine(`${row[0]} ${row[1]}`, 9)); y+=5; } if (y > 190 && r.projectionsTable.length > 10) {doc.addPage(); y=m;} addLine(`Year-by-Year Projections:`, 11, 'bold', 0, 3); let head = [['Year', 'Start Val (N)', 'Withdraw (N)', 'Growth (N)', 'End Val (N)', 'End Val (Real)', 'Withdraw (Real)']]; let body = r.projectionsTable.map(p => [ p.year, p.startValueNominal.toFixed(0), p.withdrawalNominal.toFixed(0), p.growthNominal.toFixed(0), p.endValueNominal.toFixed(0), p.endValueReal.toFixed(0), p.withdrawalReal.toFixed(0) ]); if (typeof doc.autoTable === 'function') { doc.autoTable({ startY: y, head: head, body: body, theme: 'grid', headStyles: { fillColor: [76, 175, 80], textColor: 255, fontSize: 8 }, // Green header styles: { fontSize: 7.5, cellPadding: 1, halign: 'right' }, columnStyles: { 0: { halign: 'center', fontStyle: 'bold'} } }); y = doc.lastAutoTable.finalY + 7; } else { addLine("Projection table cannot be generated (plugin issue).", 8, 'italic'); y+=5;} if (y > 275) { doc.addPage(); y = m; } addLine("Note: This is a conceptual simulator based on user-defined assumptions (deterministic returns, constant inflation). It does not account for taxes or specific investment risks. Not financial advice.", 7, 'italic'); doc.save(`${(i.strategyName || 'RetirementPlan').replace(/\s+/g, '_')}.pdf`); }
Scroll to Top