Skip to content

Commit

Permalink
New feature: monthly investments option
Browse files Browse the repository at this point in the history
  • Loading branch information
Angel Gonzalez committed Jan 14, 2024
1 parent 8d07bf8 commit 78b6495
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 5 deletions.
19 changes: 16 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,23 @@ <h1>Simulate Compound Growth With Volatility</h1>
<p>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).</p>

<div class="input-wrapper">
<label for="amountInvested">Amount invested: </label>
<input type="number" id="amountInvested" name="amountInvested" value="1000" min="0" step="100">
<label for="investmentType">Investment Type:</label>
<select name="investmentType" id="investmentType" onchange="toggleMonthlyInput()">
<option value="one-time">One-time investment</option>
<option value="monthly">Monthly investments</option>
</select>
</div>


<div class="input-wrapper">
<label for="amountInvested">Initial amount invested:</label>
<input type="number" id="amountInvested" name="amountInvested" value="1000" step="100">
</div>

<div class="input-wrapper hidden" id="monthlyInvestmentInput"> <!-- initially hidden -->
<label for="monthlyInvestment">Monthly investment:</label>
<input type="number" id="monthlyInvestment" name="monthlyInvestment" value="100" step="10">
</div>

<div class="input-wrapper">
<label for="cagr">CAGR (%): <span id="cagrValue">9</span>%</label>
<input type="range" id="cagr" name="cagr" min="0" max="30" value="9" step="1" oninput="updateSliderValue('cagr', 'cagrValue')">
Expand Down
46 changes: 44 additions & 2 deletions script.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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++) {
Expand All @@ -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 });
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -124,8 +165,9 @@ function runSimulations() {
});
}

// Initialize slider values on page load
// Initialization
document.addEventListener('DOMContentLoaded', function() {
updateSliderValue('cagr', 'cagrValue');
updateSliderValue('volatility', 'volatilityValue');
toggleMonthlyInput();
});
10 changes: 10 additions & 0 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 78b6495

Please sign in to comment.