diff --git a/links/templates/links/mini_apps/fire_planning.html b/links/templates/links/mini_apps/fire_planning.html
index 6382265..1533545 100644
--- a/links/templates/links/mini_apps/fire_planning.html
+++ b/links/templates/links/mini_apps/fire_planning.html
@@ -322,6 +322,11 @@
Inflation Rate (%)
+
+
+ Income Growth (%)
+
+
@@ -408,7 +413,7 @@
Quick Adjustments - See Impact Instantly
-
+
+
+
+
+
+
+
+ Income Growth
+
+ 0.0%
+
+
+ -1%
+ +1%
+
+
@@ -486,21 +506,6 @@
-
-
-
-
- FIRE Progress Over Time
-
-
-
- This shows your progress toward FIRE as a percentage. The calculation is:
- (Your Net Worth ÷ Inflation-Adjusted FIRE Target) × 100% .
- When you hit 100%, you've reached FIRE!
-
-
-
-
@@ -573,7 +578,6 @@
let netWorthChart = null;
let monteCarloChart = null;
- let progressChart = null;
// Custom plugin to draw vertical line at retirement age
const verticalLinePlugin = {
@@ -649,13 +653,14 @@
function generateProjections(inputs) {
const projections = [];
const yearsToProject = inputs.targetAge - inputs.currentAge;
- const annualSavings = inputs.annualIncome * inputs.savingsRate;
+ let currentIncome = inputs.annualIncome;
let netWorth = inputs.currentNetWorth;
for (let year = 0; year <= yearsToProject; year++) {
const age = inputs.currentAge + year;
const currentYear = new Date().getFullYear() + year;
const fireTarget = inputs.fireTarget * Math.pow(1 + inputs.inflationRate, year);
+ const annualSavings = currentIncome * inputs.savingsRate;
const progress = (netWorth / fireTarget) * 100;
projections.push({
@@ -667,8 +672,9 @@
annualSavings: annualSavings
});
- // Project next year's net worth
+ // Project next year's net worth and income
netWorth = (netWorth + annualSavings) * (1 + inputs.returnRate);
+ currentIncome = currentIncome * (1 + inputs.incomeGrowthRate);
}
return projections;
@@ -772,7 +778,7 @@
newValue = Math.max(min, Math.min(max, newValue));
// Round to appropriate precision
- if (fieldId === 'returnRate' || fieldId === 'inflationRate') {
+ if (fieldId === 'returnRate' || fieldId === 'inflationRate' || fieldId === 'incomeGrowthRate') {
newValue = Math.round(newValue * 10) / 10; // 1 decimal place
} else {
newValue = Math.round(newValue);
@@ -793,11 +799,13 @@
const returnRate = parseFloat(document.getElementById('returnRate').value) || 0;
const annualIncome = parseFloat(document.getElementById('annualIncome').value) || 0;
const targetAge = parseInt(document.getElementById('targetAge').value) || 0;
+ const incomeGrowthRate = parseFloat(document.getElementById('incomeGrowthRate').value) || 0;
document.getElementById('quickExpensesValue').textContent = formatCurrency(annualExpenses);
document.getElementById('quickReturnValue').textContent = returnRate.toFixed(1) + '%';
document.getElementById('quickIncomeValue').textContent = formatCurrency(annualIncome);
document.getElementById('quickTargetAgeValue').textContent = targetAge;
+ document.getElementById('quickIncomeGrowthValue').textContent = incomeGrowthRate.toFixed(1) + '%';
}
// Get input values
@@ -816,6 +824,7 @@
savingsRate: savingsRate,
returnRate: parseFloat(document.getElementById('returnRate').value) / 100 || 0.08,
inflationRate: parseFloat(document.getElementById('inflationRate').value) / 100 || 0.025,
+ incomeGrowthRate: parseFloat(document.getElementById('incomeGrowthRate').value) / 100 || 0,
// Calculate FIRE target using 4% rule
fireTarget: expenses * 25
};
@@ -828,17 +837,19 @@
for (let sim = 0; sim < numSimulations; sim++) {
let netWorth = inputs.currentNetWorth;
+ let currentIncome = inputs.annualIncome;
let yearToFire = null;
for (let year = 0; year < maxYears; year++) {
const age = inputs.currentAge + year;
- const annualSavings = inputs.annualIncome * inputs.savingsRate;
+ const annualSavings = currentIncome * inputs.savingsRate;
// Add randomness to returns (normal distribution)
const volatility = 0.15; // 15% standard deviation
const randomReturn = inputs.returnRate + (Math.random() - 0.5) * 2 * volatility;
netWorth = (netWorth + annualSavings) * (1 + randomReturn);
+ currentIncome = currentIncome * (1 + inputs.incomeGrowthRate);
// Adjust FIRE target for inflation
const inflationAdjustedTarget = inputs.fireTarget * Math.pow(1 + inputs.inflationRate, year);
@@ -898,7 +909,6 @@
// Update all charts
updateNetWorthChart(projections, inputs);
updateMonteCarloChart(inputs, p10, p25, p50, p75, p90);
- updateProgressChart(projections);
}
// Show results
@@ -1122,82 +1132,6 @@
});
}
- // Update progress chart
- function updateProgressChart(projections) {
- if (progressChart) {
- progressChart.destroy();
- }
-
- const ages = projections.map(p => p.age);
- const progress = projections.map(p => Math.min(p.progress, 150)); // Cap at 150% for chart
-
- const ctx = document.getElementById('progressChart').getContext('2d');
- progressChart = new Chart(ctx, {
- type: 'line',
- data: {
- labels: ages,
- datasets: [{
- label: 'Progress to FIRE',
- data: progress,
- borderColor: '#3498db',
- backgroundColor: 'rgba(52, 152, 219, 0.2)',
- borderWidth: 3,
- fill: true,
- tension: 0.4,
- pointRadius: 0
- }, {
- label: '100% (FIRE Achieved)',
- data: Array(ages.length).fill(100),
- borderColor: '#27ae60',
- backgroundColor: 'transparent',
- borderWidth: 2,
- borderDash: [5, 5],
- fill: false,
- pointRadius: 0
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: true,
- plugins: {
- legend: {
- display: true,
- position: 'top'
- },
- tooltip: {
- mode: 'index',
- intersect: false,
- callbacks: {
- label: function(context) {
- if (context.datasetIndex === 0) {
- return 'Progress: ' + context.parsed.y.toFixed(1) + '%';
- }
- return context.dataset.label;
- }
- }
- }
- },
- scales: {
- y: {
- beginAtZero: true,
- max: 150,
- ticks: {
- callback: function(value) {
- return value + '%';
- }
- }
- },
- x: {
- title: {
- display: true,
- text: 'Age'
- }
- }
- }
- }
- });
- }
-
// Reset form
function resetForm() {
document.getElementById('currentAge').value = 35;
@@ -1209,7 +1143,9 @@
document.getElementById('savingsRate').value = 40;
document.getElementById('returnRate').value = 8;
document.getElementById('inflationRate').value = 2.5;
+ document.getElementById('incomeGrowthRate').value = 0;
+ updateQuickAdjustmentDisplays();
document.getElementById('resultsSection').classList.remove('show');
}
@@ -1250,7 +1186,8 @@
'annualIncome',
'savingsRate',
'returnRate',
- 'inflationRate'
+ 'inflationRate',
+ 'incomeGrowthRate'
];
// Add input event listeners for auto-calculation