diff --git a/index.html b/index.html index 3cbfe4e..700124d 100644 --- a/index.html +++ b/index.html @@ -24,10 +24,23 @@

Simulate Compound Growth With Volatility

Monte Carlo simulations are a way to predict the outcome of a scenario that has a significant degree of uncertainty. By running thousands of simulations, each with random variations based on your inputted volatility, the tool can model a vast range of possible investment paths. However, to keep the graph clear and easy to interpret, only ten representative trajectories are displayed—each one corresponding to a different percentile of the final investment distribution. This approach gives you a snapshot of potential outcomes, from the lower end (what you might end up with in less favorable conditions) to the higher end (the outcome if things go exceptionally well).

- - + +
- + +
+ + +
+ + +
diff --git a/script.js b/script.js index cd4700f..6968237 100644 --- a/script.js +++ b/script.js @@ -4,6 +4,24 @@ function updateSliderValue(sliderId, displayId) { display.textContent = slider.value; } +function toggleMonthlyInput() { + var investmentType = document.getElementById('investmentType').value; + var monthlyInvestmentInput = document.getElementById('monthlyInvestmentInput'); + + // Check if the investment type is 'monthly' and show/hide the monthly investment input accordingly + if (investmentType === 'monthly') { + // Remove the 'hidden' class if it exists + if (monthlyInvestmentInput.classList.contains('hidden')) { + monthlyInvestmentInput.classList.remove('hidden'); + } + } else { + // Add the 'hidden' class if it's not already there + if (!monthlyInvestmentInput.classList.contains('hidden')) { + monthlyInvestmentInput.classList.add('hidden'); + } + } +} + function getColorForPercentile(percentile) { // Red to yellow gradient: interpolate between red (255,0,0) and yellow (255,255,0) var greenValue = Math.round(255 * (percentile / 10)); // Interpolate green channel @@ -22,6 +40,8 @@ function gaussianRandom(mean=0, stdev=1) { function runSimulations() { // Retrieve input values var amountInvested = parseFloat(document.getElementById('amountInvested').value); + var investmentType = document.getElementById('investmentType').value; // Get the investment type + var monthlyInvestment = investmentType === 'monthly' ? parseFloat(document.getElementById('monthlyInvestment').value) : 0; var cagr = parseFloat(document.getElementById('cagr').value) / 100; // Convert percentage to decimal var volatility = parseFloat(document.getElementById('volatility').value) / 100; // Convert percentage to decimal var timeHorizon = parseInt(document.getElementById('timeHorizon').value); @@ -32,6 +52,15 @@ function runSimulations() { var monthlyReturn = Math.log(1 + cagr)/12; // like cagr/12 but adjusted for compounding var monthlyVolatility = volatility/Math.sqrt(12); // lognormal process, volatility grows with sqrt(time) + // Calculate the timeseries of investment contributions + var investmentContributions = [amountInvested]; // Start with the initial amount invested + for (let month = 1; month <= numMonths; month++) { + // Each month, add the monthly investment to the last value + let lastValue = investmentContributions[month - 1]; + let newValue = lastValue + monthlyInvestment; + investmentContributions.push(newValue); + } + // Run all simulations and store final values with corresponding simulation data var simulationResults = []; for (let sim = 0; sim < monteCarloSimulations; sim++) { @@ -40,7 +69,7 @@ function runSimulations() { let lastValue = data[month - 1]; // Simulate the monthly growth with volatility let monthlyGrowth = monthlyReturn + monthlyVolatility*gaussianRandom(mean=0, stdev=1) - let newValue = lastValue * (1 + monthlyGrowth); + let newValue = lastValue * (1 + monthlyGrowth) + monthlyInvestment; data.push(newValue); } simulationResults.push({ finalValue: data[numMonths], data: data }); @@ -71,6 +100,18 @@ function runSimulations() { pointRadius: 0, // Hide points for a cleaner look })); + // Add the blue dashed line dataset for the cumulative investments + datasets.push({ + label: 'Cumulative Investment', + data: investmentContributions, + borderColor: 'blue', + backgroundColor: 'rgba(0, 0, 255, 0.1)', // Light blue fill under the line + borderWidth: 1, + borderDash: [5, 5], // Dashed line: 5px dash, 5px gap + pointRadius: 0, // Hide points for a cleaner look + fill: true // Fill the area under the line + }); + // Create Chart.js chart new Chart(ctx, { type: 'line', @@ -124,8 +165,9 @@ function runSimulations() { }); } -// Initialize slider values on page load +// Initialization document.addEventListener('DOMContentLoaded', function() { updateSliderValue('cagr', 'cagrValue'); updateSliderValue('volatility', 'volatilityValue'); + toggleMonthlyInput(); }); \ No newline at end of file diff --git a/styles.css b/styles.css index d3b1233..fe79805 100644 --- a/styles.css +++ b/styles.css @@ -8,6 +8,10 @@ h1 { color: #0b422e; /* Dark green color for the title */ } +#monthlyInvestmentInput.hidden.hidden { + display: none; +} + /* Base styles for mobile-first approach */ .container { width: 100%; /* Full width */ @@ -45,6 +49,12 @@ h1 { .content button { margin-bottom: 30px; /* Increase vertical separation */ } + +.content select { + width: 45%; /* Custom width for dropdown menu */ + padding: 5px 5px; /* Increase padding to make the dropdown menu lager*/ + font-size: 1.2em; /* Larger font size for the number inside the input */ +} .content input[type="number"] { width: 35%; /* Custom width for number inputs */ font-size: 1.3em; /* Larger font size for the number inside the input */