Inflation-Protected Real Estate Investment Planner

Inflation-Protected Real Estate Investment Planner

This will be used to adjust future values to today's dollars and can inflate expenses/rent if linked.
Your required rate of return for calculating Net Present Value of cash flows.

Property Acquisition

Rental Income

Can be same as general inflation or a specific estimate.

Annual Operating Expenses

Assumed to inflate annually with general inflation.
E.g., utilities not paid by tenant, HOA, etc. Assumed to inflate annually.

Inflation-Protected Real Estate Investment Analysis

Enter all details and click "Analyze Investment" to see projections.

Please enter valid Purchase Price and Initial Monthly Rent.

"; yearByYearTableContainerEl.innerHTML = ""; notesContainerEl.style.display = 'none'; downloadPdfBtn.style.display = 'none'; inputSummaryForPdfEl.innerHTML = ""; analysisTitleEl.textContent = "Inflation-Protected Real Estate Investment Analysis"; return; } analysisTitleEl.textContent = `Investment Analysis over ${analysisPeriod} Years`; inputSummaryForPdfEl.innerHTML = `

Key Input Assumptions:

  • Analysis Period: ${analysisPeriod} Years
  • Inflation Rate: ${(inflationRate*100).toFixed(1)}%
  • Property Price: ${formatCurrency(purchasePrice)}
  • Down Payment: ${(downPaymentPct*100).toFixed(0)}%
  • Mortgage Rate: ${mortgageRatePct.toFixed(2)}% for ${loanTerm} Years
  • Initial Monthly Rent: ${formatCurrency(initialMonthlyRent)}
  • Property Appreciation: ${(appreciationRate*100).toFixed(1)}% p.a.
`; const downPaymentAmount = purchasePrice * downPaymentPct; const loanAmount = purchasePrice - downPaymentAmount; const purchaseClosingCosts = (closingCostsPurchaseType === 'percent' ? purchasePrice * (closingCostsPurchaseInput / 100) : closingCostsPurchaseInput); const initialCashOutlay = downPaymentAmount + purchaseClosingCosts; const mortgage = calculateMortgageDetails(loanAmount, mortgageRatePct, loanTerm); // Use correct loanTerm variable let yearByYearHTML = ``; let currentPropertyValue = purchasePrice; let currentAnnualRent = initialMonthlyRent * 12; let currentAnnualInsurance = annualInsurance; let currentOtherAnnualOpEx = otherAnnualOpEx; let currentFixedPropertyTax = (propertyTaxType === 'fixed' ? propertyTaxInput : 0); let currentFixedMaintenance = (maintenanceType === 'fixed' ? maintenanceInput : 0); let currentFixedManagementFee = (managementFeeType === 'fixed' ? managementFeeInput : 0); let cumulativeNominalCashFlow = 0; let cumulativeRealCashFlow = 0; for (let year = 1; year <= analysisPeriod; year++) { const inflationFactorForYear = Math.pow(1 + inflationRate, year); const inflationFactorForStartOfYear = Math.pow(1 + inflationRate, year - 1); const propertyValueReal = currentPropertyValue / inflationFactorForStartOfYear; const grossPotentialRent = currentAnnualRent; const vacancyLoss = grossPotentialRent * vacancyRate; const effectiveGrossIncome = grossPotentialRent - vacancyLoss; const propertyTaxYear = (propertyTaxType === 'percent_value' ? currentPropertyValue * (propertyTaxInput / 100) : currentFixedPropertyTax); const maintenanceYear = (maintenanceType === 'percent_value' ? currentPropertyValue * (maintenanceInput / 100) : currentFixedMaintenance); const managementFeeYear = (managementFeeType === 'percent_rent' ? grossPotentialRent * (managementFeeInput / 100) : currentFixedManagementFee); const totalOperatingExpenses = propertyTaxYear + currentAnnualInsurance + maintenanceYear + managementFeeYear + currentOtherAnnualOpEx; const noi = effectiveGrossIncome - totalOperatingExpenses; const debtServiceYear = (year <= loanTerm && mortgage.annualPayments[year-1]) ? mortgage.annualPayments[year-1].totalPayment : 0; const cashFlowBeforeTaxNominal = noi - debtServiceYear; const cashFlowBeforeTaxReal = cashFlowBeforeTaxNominal / inflationFactorForYear; cumulativeNominalCashFlow += cashFlowBeforeTaxNominal; cumulativeRealCashFlow += cashFlowBeforeTaxReal; let loanBalanceEndOfYear; if (year <= loanTerm && mortgage.annualPayments[year-1]) { loanBalanceEndOfYear = mortgage.annualPayments[year-1].endingBalance; } else if (year > loanTerm) { loanBalanceEndOfYear = 0; } else { loanBalanceEndOfYear = loanAmount; // This case needs refinement if loan term is very short or 0 principal if(mortgage.annualPayments.length > 0 && year-1 < mortgage.annualPayments.length) { loanBalanceEndOfYear = mortgage.annualPayments[year-1] ? mortgage.annualPayments[year-1].endingBalance : loanAmount; } else if (mortgage.annualPayments.length === 0 && loanAmount > 0) { // No payments made yet loanBalanceEndOfYear = loanAmount; } else { // Loan paid off or never existed significantly loanBalanceEndOfYear = 0; } } const equityNominal = currentPropertyValue - loanBalanceEndOfYear; const equityReal = equityNominal / inflationFactorForYear; yearByYearHTML += ``; currentPropertyValue *= (1 + appreciationRate); currentAnnualRent *= (1 + rentIncreaseRate); currentAnnualInsurance *= (1 + inflationRate); currentOtherAnnualOpEx *= (1 + inflationRate); if (propertyTaxType === 'fixed') currentFixedPropertyTax *= (1 + inflationRate); if (maintenanceType === 'fixed') currentFixedMaintenance *= (1 + inflationRate); if (managementFeeType === 'fixed') currentFixedManagementFee *= (1 + inflationRate); } yearByYearHTML += `
Year Property Value (Nominal $)Property Value (Real $) Gross Potential Rent (Nominal $)Effective Gross Income (Nominal $) Operating Expenses (Nominal $)NOI (Nominal $) Debt Service (Nominal $)Cash Flow BT (Nominal $) Cash Flow BT (Real $) Loan Balance (Nominal $)Equity (Nominal $)Equity (Real $)
${year} ${formatCurrency(currentPropertyValue)}${formatCurrency(propertyValueReal)} ${formatCurrency(grossPotentialRent)}${formatCurrency(effectiveGrossIncome)} ${formatCurrency(totalOperatingExpenses)}${formatCurrency(noi)} ${formatCurrency(debtServiceYear)} ${formatCurrency(cashFlowBeforeTaxNominal)} ${formatCurrency(cashFlowBeforeTaxReal)} ${formatCurrency(loanBalanceEndOfYear)} ${formatCurrency(equityNominal)}${formatCurrency(equityReal)}
`; yearByYearTableContainerEl.innerHTML = yearByYearHTML; const finalPropertyValueNominal = purchasePrice * Math.pow(1 + appreciationRate, analysisPeriod); const sellingCostsAmount = finalPropertyValueNominal * sellingCostsPct; let finalLoanBalanceAtEndOfAnalysis; if (analysisPeriod >= loanTerm) { finalLoanBalanceAtEndOfAnalysis = 0; } else { // Get the ending balance from the last calculated year in the amortization schedule finalLoanBalanceAtEndOfAnalysis = (mortgage.annualPayments[analysisPeriod - 1]) ? mortgage.annualPayments[analysisPeriod - 1].endingBalance : loanAmount; } const netProceedsFromSaleNominal = finalPropertyValueNominal - sellingCostsAmount - finalLoanBalanceAtEndOfAnalysis; const totalNominalReturnOnOutlay = (netProceedsFromSaleNominal + cumulativeNominalCashFlow) - initialCashOutlay; const totalRealReturnOnOutlay = (netProceedsFromSaleNominal / Math.pow(1 + inflationRate, analysisPeriod) + cumulativeRealCashFlow) - initialCashOutlay; const simpleNominalRoi = initialCashOutlay > 0 ? (totalNominalReturnOnOutlay / initialCashOutlay) * 100 : 0; const simpleRealRoi = initialCashOutlay > 0 ? (totalRealReturnOnOutlay / initialCashOutlay) * 100 : 0; summaryMetricsEl.innerHTML = `

Key Performance Indicators (End of Period ${analysisPeriod} Years):

Initial Cash Outlay (Down Payment + Closing Costs): ${formatCurrency(initialCashOutlay)}

Estimated Property Value (Nominal): ${formatCurrency(finalPropertyValueNominal)}

Estimated Property Value (Real, in today's dollars): ${formatCurrency(finalPropertyValueNominal / Math.pow(1 + inflationRate, analysisPeriod))}

Total Net Cash Flow Before Tax (Nominal): ${formatCurrency(cumulativeNominalCashFlow)}

Total Net Cash Flow Before Tax (Real, sum of yearly real CFs): ${formatCurrency(cumulativeRealCashFlow)}

Net Proceeds from Sale (Nominal, after selling costs & loan payoff): ${formatCurrency(netProceedsFromSaleNominal)}


Total Return on Initial Outlay (Nominal, Pre-tax): ${formatCurrency(totalNominalReturnOnOutlay)} (${simpleNominalRoi.toFixed(1)}%)

Total Return on Initial Outlay (Real, Pre-tax, in today's dollars): ${formatCurrency(totalRealReturnOnOutlay)} (${simpleRealRoi.toFixed(1)}%)

`; notesContainerEl.style.display = 'block'; downloadPdfBtn.style.display = 'block'; } function formatCurrency(value) { return `$${(value || 0).toLocaleString(undefined, {minimumFractionDigits:0, maximumFractionDigits:0})}`; } downloadPdfBtn?.addEventListener('click', function () { const { jsPDF } = window.jspdf; const pdfOutputArea = document.getElementById('iprePdfOutputArea'); if (!pdfOutputArea || summaryMetricsEl.innerHTML.includes("Enter all details")) { alert('Please analyze the investment first before downloading PDF.'); return; } document.getElementById('iprepNotesContainer').style.display = 'block'; document.getElementById('iprepInputSummaryForPdf').style.display = 'block'; html2canvas(pdfOutputArea, { scale: 1.1, useCORS: true, backgroundColor: '#ffffff', windowWidth: pdfOutputArea.scrollWidth, windowHeight: pdfOutputArea.scrollHeight }) .then(canvas => { const imgData = canvas.toDataURL('image/jpeg', 0.85); const pdf = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a3' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const margin = 25; let contentWidth = pdfWidth - 2 * margin; let contentHeight = pdfHeight - 2 * margin; const canvasWidth = canvas.width; const canvasHeight = canvas.height; let imgWidth = canvasWidth; let imgHeight = canvasHeight; let ratio = imgWidth / imgHeight; if (imgWidth > contentWidth) { imgWidth = contentWidth; imgHeight = imgWidth / ratio; } if (imgHeight > contentHeight) { imgHeight = contentHeight; imgWidth = imgHeight * ratio; } const x = (pdfWidth - imgWidth) / 2; const y = (pdfHeight - imgHeight) / 2; pdf.addImage(imgData, 'JPEG', x, y, imgWidth, imgHeight, undefined, 'MEDIUM'); pdf.save('Inflation_Protected_RE_Analysis.pdf'); }) .catch(err => { console.error("Error generating PDF:", err); alert("Error generating PDF. See console for details."); }); }); function escapeHtml(unsafe) { if (typeof unsafe !== 'string') return unsafe === undefined || unsafe === null ? '' : String(unsafe); return unsafe.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } switchTab('generalTab'); });
Scroll to Top