· jaryd krishnan · tutorials · 11 min read

Building a Monte Carlo Simulator with React

Dive into the probabilistic world of Monte Carlo simulations and learn how to implement them in a React application.

Step-by-step guide to build a Monte Carlo simulator using React.

Introduction

Monte Carlo simulations are a class of computational algorithms that rely on repeated random sampling to estimate a numerical result. While the name might suggest a connection to gambling, Monte Carlo methods find their application in a variety of fields like physics, finance, and project management.

In this blog post, we will be building a Monte Carlo simulator using React, a popular JavaScript library for building user interfaces. The simulator will be based on a simple scenario - predicting future values of an investment given variable rates of return and inflation. Why? Well, we built one in our last blog post!

Setting Up the React Application

Firstly, you’ll need to set up a new React application. For this, we’ll use Next.js, a widely used tool for creating new React applications with a good default configuration. If you don’t already have Node.js and npm installed, you will need to do that first. Once you have Node.js and npm, you can install Next.js and create a new project by running the following commands:

npx create-next-app@latest monte-carlo-simulator

This command creates a new directory called monte-carlo-simulator, sets up a new React application in it, and installs the necessary dependencies.

Once the installation process is complete, navigate to the project directory:

cd monte-carlo-simulator

You can start the development server and open the application in your browser by running:

npm run dev

Now that you have your React app set up, you’re ready to start building the Monte Carlo simulator. You can press “Control + C” to stop the running application.

Next, we’ll be installing a couple of additional dependencies. First is victory, a set of modular charting components for React and React Native. Victory makes it easy to get started with a set of simple, flexible, and composable charts. We’ll also install tailwindcss, a utility-first CSS framework for rapidly building custom user interfaces.

You can add these to your project by running:

npm install -D tailwindcss postcss autoprefixer
npm install victory

Now, we’ll want ot set up tailwind:

npx tailwindcss init -p

And we can edit the newly created tailwind.config.js file to have the following:

module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",

    // Or if using `src` directory:
    "./src/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

We should also import our tailwind directives into our globals.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

We’re now ready to start building our Monte Carlo simulator!

Creating the Simulator Component

With our new React application set up, it’s time to start creating the Monte Carlo simulator component. Firstly, create a new file MonteCarlo.jsx in the src directory. This is where we’ll put all the logic for our simulation.

The simulator will use a number of state variables to control and track the simulation:

  • numSimulations: the number of simulations to run
  • numYears: the number of years for each simulation
  • initialInvestment: the starting value for the simulated investment
  • goal: the target value we hope the investment will exceed
  • numSuccesses: the number of simulations where the investment exceeded the goal
  • simulationRuns: the history of investment values for each simulation

Here’s how to set up these state variables at the beginning of our MonteCarlo component:

import { useState } from 'react';

const MonteCarlo = () => {
  const [numSimulations, setNumSimulations] = useState(1000);
  const [numYears, setNumYears] = useState(20);
  const [initialInvestment, setInitialInvestment] = useState(10000);
  const [goal, setGoal] = useState(20000);
  const [numSuccesses, setNumSuccesses] = useState(0);
  const [simulationRuns, setSimulationRuns] = useState([]);

  // runSimulation function will go here

  return (
    // JSX will be added here
  );
};

export default MonteCarlo;

In the next sections, we’ll add the logic to run the simulations and render the results.

Building the Simulation Logic

Now that we’ve established our state variables and basic structure of our Monte Carlo component, let’s implement the logic for running the simulations. We’ll define a runSimulation function that performs the Monte Carlo simulation.

Here’s a step-by-step walkthrough of the runSimulation function:

// This goes in place of the comment "runSimulation function will go here"
const runSimulation = () => {
  let successes = 0;
  let newSimulationRuns = [];
  for (let i = 0; i < numSimulations; i++) {
    let value = initialInvestment;
    let yearlyValues = [initialInvestment];
    for (let j = 0; j < numYears; j++) {
      // Random rate of return between -5% and +15%
      const rateOfReturn = -0.05 + Math.random() * 0.2;
      // Random inflation rate between 0% and 4%
      const inflationRate = Math.random() * 0.04;
      value = value * (1 + rateOfReturn) / (1 + inflationRate);
      yearlyValues.push(value);
    }
    if (value > goal) {
      successes += 1;
    }
    newSimulationRuns.push({
      initialInvestment,
      goal,
      numYears,
      finalValue: value,
      yearlyValues
    });
  }
  setNumSuccesses(successes);
  setSimulationRuns(newSimulationRuns);
};

Each simulation starts with the initial investment and then for each year, we simulate a random rate of return and a random inflation rate. The new value of the investment is calculated accordingly, and then added to the yearlyValues array. This is repeated for the desired number of years. At the end of the simulation, if the final value of the investment exceeds the goal, we increment the successes counter. Finally, we store the details of this simulation run in the newSimulationRuns array.

Once all simulations are complete, we update the numSuccesses and simulationRuns state variables with the final results.

In the next section, we’ll build the user interface to input the simulation parameters and display the results.

Building the User Interface

With the simulation logic in place, it’s time to build the user interface that will allow users to interact with our Monte Carlo Simulator. We’re going to create a form for the user to input the initial investment, the goal, the number of years and the number of simulations to run. We’ll also provide a button to start the simulation. Lastly, we’ll display the result of the simulation.

Here’s the basic layout of our interface:

// This goes in the "return" area in our component above, where we say "JSX will be added here"
<div className="flex flex-col items-center justify-center h-screen">
  <h2 className="text-2xl font-bold mb-4">Monte Carlo Simulation - Investment Portfolio</h2>
  <form className="mb-4">
    <label className="block mb-2">
      Initial Investment:
      <input type="number" value={initialInvestment} onChange={e => setInitialInvestment(parseFloat(e.target.value))} />
    </label>
    <label className="block mb-2">
      Goal:
      <input type="number" value={goal} onChange={e => setGoal(parseFloat(e.target.value))} />
    </label>
    <label className="block mb-2">
      Number of Years:
      <input type="number" value={numYears} onChange={e => setNumYears(parseInt(e.target.value, 10))} />
    </label>
    <label className="block mb-2">
      Number of Simulations:
      <input type="number" value={numSimulations} onChange={e => setNumSimulations(parseInt(e.target.value))} />
    </label>
    <button type="button" onClick={runSimulation}>Run Simulation</button>
  </form>
  {numSuccesses > 0 && (
    <p>Successful simulations: {numSuccesses}</p>
  )}
  {/* Victory code will go here */}
</div>

This form has four input fields, each associated with a state variable. When the value of an input field changes, we update the corresponding state variable. The button has an onClick event handler which triggers the runSimulation function when clicked. We also display the number of successful simulations after the simulation is run.

In the next section, we’ll add charting to visualize the results of our simulations.

Charting the Results with Victory

To visualize the results of our Monte Carlo simulations, we’re going to use a powerful data visualization library called Victory. Victory provides a set of modular charting components for React and React Native. It’s flexible, easy to use, and it allows us to create rich, interactive data visualizations.

We’re going to use VictoryChart, VictoryLine, and VictoryTheme from Victory. VictoryChart is a wrapper component that renders a chart area with some default chart elements, VictoryLine is a component for drawing line charts, and VictoryTheme contains a set of predefined themes that we can apply to our charts.

Below is an example of how you can create a chart with Victory:

// This will go at the top of your component
import { VictoryChart, VictoryLine, VictoryTheme } from 'victory';

// Inside your render function, where we commented "Victory code will go here"
<VictoryChart theme={VictoryTheme.material}>
  {simulationRuns.map((run, index) => (
    <VictoryLine data={run.yearlyValues.map((value, year) => ({ x: year, y: value }))} />
  ))}
</VictoryChart>

This code will render a line chart with a line for each simulation run. The x coordinate of a point represents the year, and the y coordinate represents the value of the investment at that year.

In the next section, we’ll discuss how to add more advanced features to our simulator.

Adding a Dash of Color

To improve the visual impact of our chart, let’s color-code the results. We want the color of each line to reflect the success of that simulation run: green if the final value is above the goal, and red otherwise.

Victory makes it easy to customize the appearance of chart elements. The style prop of the VictoryLine component is an object that can contain various styling properties. For instance, data is the styling property for the line itself.

Here’s how you can set the color of each line based on the finalValue property of the simulation run:

<VictoryChart theme={VictoryTheme.material}>
  {simulationRuns.map((run, index) => (
    <VictoryLine
      style={{ data: { stroke: run.finalValue >= run.goal ? 'green' : 'red' } }}
      data={run.yearlyValues.map((value, year) => ({ x: year, y: value }))}
    />
  ))}
</VictoryChart>

In the style prop, we’re using a JavaScript ternary operator to conditionally set the color of the line. If finalValue is greater than or equal to goal, we use ‘green’; otherwise, we use ‘red’.

Styling is an essential part of data visualization, as it helps users interpret the data more effectively. Using colors wisely can greatly enhance the readability and appeal of your charts.

Making the Chart Interactive with Tooltips

Visualizations are most effective when they enable users to explore data in detail. A great way to achieve this is by adding tooltips that display specific data points when the user hovers over a line in the chart.

Victory provides a built-in VictoryVoronoiContainer component that makes it simple to add interactive tooltips to your chart. We can set the containerComponent prop of the VictoryChart to a VictoryVoronoiContainer:

<VictoryChart
  theme={VictoryTheme.material}
  containerComponent={<VictoryVoronoiContainer />}
>

The VictoryVoronoiContainer adds a voronoi overlay to the chart which is used to determine which data point the user’s cursor is closest to.

Next, we need to add a VictoryTooltip to our VictoryLine. The VictoryTooltip component is rendered whenever the user hovers over a data point in a line:

<VictoryLine
  style={{ data: { stroke: run.finalValue >= run.goal ? 'green' : 'red' } }}
  data={run.yearlyValues.map((value, year) => ({ x: year, y: value }))}
  labels={({ datum }) => `Year: ${datum.x}, Value: ${datum.y.toFixed(2)}`}
  labelComponent={<VictoryTooltip />}
/>

In the labels prop, we’re defining what text the tooltip should show. We’re accessing the x and y properties of the hovered data point (referred to as datum) to display the year and the value of the investment. The labelComponent prop tells Victory to use VictoryTooltip to render the labels.

And that’s it! With tooltips, your users can now hover over any line in the chart to see the exact investment value for each year.

Conclusion

Congratulations! You’ve successfully built a Monte Carlo simulator using React. You can see the final result below:

Monte Carlo Simulation - Investment Portfolio

The investment goal was achieved in 0.00% of the simulations.

$10,000.00$12,000.00$14,000.00$16,000.00$18,000.00$20,000.00Year5101520

The final code for this component looks like this:

import { useState } from 'react';
import { VictoryChart, VictoryLine, VictoryTheme, VictoryTooltip, VictoryVoronoiContainer } from 'victory';

import { useState } from 'react';

const MonteCarlo = () => {
  const [numSimulations, setNumSimulations] = useState(1000);
  const [numYears, setNumYears] = useState(20);
  const [initialInvestment, setInitialInvestment] = useState(10000);
  const [goal, setGoal] = useState(20000);
  const [numSuccesses, setNumSuccesses] = useState(0);
  const [simulationRuns, setSimulationRuns] = useState([]);

  // runSimulation function will go here
  const runSimulation = () => {
    let successes = 0;
    let newSimulationRuns = [];
    for (let i = 0; i < numSimulations; i++) {
      let value = initialInvestment;
      let yearlyValues = [initialInvestment];
      for (let j = 0; j < numYears; j++) {
        // Random rate of return between -5% and +15%
        const rateOfReturn = -0.05 + Math.random() * 0.2;
        // Random inflation rate between 0% and 4%
        const inflationRate = Math.random() * 0.04;
        value = value * (1 + rateOfReturn) / (1 + inflationRate);
        yearlyValues.push(value);
      }
      if (value > goal) {
        successes += 1;
      }
      newSimulationRuns.push({
        initialInvestment,
        goal,
        numYears,
        finalValue: value,
        yearlyValues
      });
    }
    setNumSuccesses(successes);
    setSimulationRuns(newSimulationRuns);
  };

  // JSX will be added here
  return (
    <div className="flex flex-col items-center justify-center h-screen">
      <h2 className="text-2xl font-bold mb-4">Monte Carlo Simulation - Investment Portfolio</h2>
      <form className="mb-4">
        <label className="block mb-2">
          Initial Investment:
          <input type="number" value={initialInvestment} onChange={e => setInitialInvestment(parseFloat(e.target.value))} />
        </label>
        <label className="block mb-2">
          Goal:
          <input type="number" value={goal} onChange={e => setGoal(parseFloat(e.target.value))} />
        </label>
        <label className="block mb-2">
          Number of Years:
          <input type="number" value={numYears} onChange={e => setNumYears(parseInt(e.target.value, 10))} />
        </label>
        <label className="block mb-2">
          Number of Simulations:
          <input type="number" value={numSimulations} onChange={e => setNumSimulations(parseInt(e.target.value))} />
        </label>
        <button type="button" onClick={runSimulation}>Run Simulation</button>
      </form>
      {numSuccesses > 0 && (
        <p>Successful simulations: {numSuccesses}</p>
      )}
      {/* Victory code will go here */}
      <VictoryChart
        theme={VictoryTheme.material}
        containerComponent={<VictoryVoronoiContainer />}
      >
        <VictoryLine
          style={{ data: { stroke: run.finalValue >= run.goal ? 'green' : 'red' } }}
          data={run.yearlyValues.map((value, year) => ({ x: year, y: value }))}
          labels={({ datum }) => `$${datum.y.toFixed(2)}`}
          labelComponent={<VictoryTooltip />}
        />
      </VictoryChart>
    </div>
  );
};

export default MonteCarlo;

By the end of this tutorial, you should have a working Monte Carlo simulation running in your browser. This tutorial just scratches the surface of what Monte Carlo simulations can do. They’re a powerful tool in any data scientist’s arsenal, and being able to build one with React opens up a world of possibilities for interactive, web-based data analysis.

Remember, the most important part of learning is experimenting, so play around with the code and try adding new features or tweaking the existing ones to better suit your needs.

Back to Blog