# Load packages

# Core
library(tidyverse)
library(tidyquant)

# Source function
source("../00_scripts/simulate_accumulation.R")

1 Import stock prices

Revise the code below.

symbols <- c("SPY", "EFA", "IJS", "EEM", "AGG")

prices <- tq_get(x    = symbols,
                 get  = "stock.prices",    
                 from = "2012-12-31",
                 to   = "2022-12-31")

2 Convert prices to returns

asset_returns_tbl <- prices %>%
    
    group_by(symbol) %>%
    
    tq_transmute(select     = adjusted, 
                 mutate_fun = periodReturn, 
                 period     = "monthly",
                 type       = "log") %>%
    
    slice(-1) %>%
    
    ungroup() %>%
    
    set_names(c("asset", "date", "returns"))

3 Assign a weight to each asset

Revise the code for weights.

# symbols
symbols <- asset_returns_tbl %>% distinct(asset) %>% pull()
symbols
## [1] "AGG" "EEM" "EFA" "IJS" "SPY"
# weights
weights <- c(0.25, 0.25, 0.2, 0.2, 0.1)
weights
## [1] 0.25 0.25 0.20 0.20 0.10
w_tbl <- tibble(symbols, weights)
w_tbl
## # A tibble: 5 × 2
##   symbols weights
##   <chr>     <dbl>
## 1 AGG        0.25
## 2 EEM        0.25
## 3 EFA        0.2 
## 4 IJS        0.2 
## 5 SPY        0.1

4 Build a portfolio

portfolio_returns_tbl <- asset_returns_tbl %>%
    
    tq_portfolio(assets_col = asset, 
                 returns_col = returns, 
                 weights = w_tbl, 
                 rebalance_on = "months", 
                 col_rename = "returns")

portfolio_returns_tbl
## # A tibble: 120 × 2
##    date        returns
##    <date>        <dbl>
##  1 2013-01-31  0.0204 
##  2 2013-02-28 -0.00239
##  3 2013-03-28  0.0121 
##  4 2013-04-30  0.0174 
##  5 2013-05-31 -0.0128 
##  6 2013-06-28 -0.0247 
##  7 2013-07-31  0.0321 
##  8 2013-08-30 -0.0224 
##  9 2013-09-30  0.0511 
## 10 2013-10-31  0.0301 
## # … with 110 more rows

5 Simulating growth of a dollar

# Get mean portfolio return
mean_port_return <- mean(portfolio_returns_tbl$returns)
mean_port_return
## [1] 0.003637758
# Get standard deviation of portfolio returns
stddev_port_return <- sd(portfolio_returns_tbl$returns)
stddev_port_return
## [1] 0.03421552

6 Simulation function

No need

7 Running multiple simulations

# Create a vector of 1s as a starting point
sims <- 51
starts <- rep(100, sims) %>%
    set_names(paste("sim", 1:sims))

starts
##  sim 1  sim 2  sim 3  sim 4  sim 5  sim 6  sim 7  sim 8  sim 9 sim 10 sim 11 
##    100    100    100    100    100    100    100    100    100    100    100 
## sim 12 sim 13 sim 14 sim 15 sim 16 sim 17 sim 18 sim 19 sim 20 sim 21 sim 22 
##    100    100    100    100    100    100    100    100    100    100    100 
## sim 23 sim 24 sim 25 sim 26 sim 27 sim 28 sim 29 sim 30 sim 31 sim 32 sim 33 
##    100    100    100    100    100    100    100    100    100    100    100 
## sim 34 sim 35 sim 36 sim 37 sim 38 sim 39 sim 40 sim 41 sim 42 sim 43 sim 44 
##    100    100    100    100    100    100    100    100    100    100    100 
## sim 45 sim 46 sim 47 sim 48 sim 49 sim 50 sim 51 
##    100    100    100    100    100    100    100
# Simulate
# for reproducible research
set.seed(1234)

monte_carlo_sim_51 <- starts %>%
    
    #Simulate
    map_dfc(.x = .,
            .f = ~simulate_accumulation(initial_value = .x, 
                                        N             = 240, 
                                        mean_return   = mean_port_return, 
                                        sd_return     = stddev_port_return)) %>%
    
    # Add column month
    mutate(month = 1:nrow(.)) %>%
    select(month, everything())%>%
    
    # Rename column names
    set_names(c("month", names(starts))) %>%
    
    # Transform to long form
    pivot_longer(cols = -month, names_to = "sim", values_to = "growth")

monte_carlo_sim_51
## # A tibble: 12,291 × 3
##    month sim    growth
##    <int> <chr>   <dbl>
##  1     1 sim 1     100
##  2     1 sim 2     100
##  3     1 sim 3     100
##  4     1 sim 4     100
##  5     1 sim 5     100
##  6     1 sim 6     100
##  7     1 sim 7     100
##  8     1 sim 8     100
##  9     1 sim 9     100
## 10     1 sim 10    100
## # … with 12,281 more rows
# Find quantiles
monte_carlo_sim_51 %>%
    
    group_by(sim) %>%
    summarise(growth = last(growth)) %>%
    ungroup() %>%
    pull(growth) %>%
    
    quantile(probs = c(0, 0.25, 0.5, 0.75, 1)) %>%
    round(2)
##     0%    25%    50%    75%   100% 
##  68.90 168.20 237.59 332.96 539.07

8 Visualizing simulations with ggplot

Line Plot of Simulations with Max, Median, and Min

# Step 1: Summarize data into max, median and min if last value
sim_summary <- monte_carlo_sim_51 %>%
    
    group_by(sim) %>%
    summarise(growth = last(growth)) %>%
    ungroup() %>%
    
    summarise(max    = max(growth),
              median = median(growth),
              min    = min(growth))

sim_summary
## # A tibble: 1 × 3
##     max median   min
##   <dbl>  <dbl> <dbl>
## 1  539.   238.  68.9
# Step 2 Plot
monte_carlo_sim_51 %>%
    
    # Filter for max, median, and min sim
    group_by(sim) %>%
    filter(last(growth) == sim_summary$max |
               last(growth) == sim_summary$median |
               last(growth) == sim_summary$min) %>%
    ungroup() %>%

    # Plot
    ggplot(aes(x = month, y = growth, color = sim)) +
    geom_line() +
    theme(legend.position = "none") +
    theme(plot.title = element_text(hjust = 0.5)) +
    theme(plot.subtitle = element_text(hjust = 0.5)) +
    
    labs(title = "Simulating growth of $100 over 240 months",
         subtitle = "Maximum, Median, and Minimum Simulation")

Based on the Monte Carlo simulation results, how much should you expect from your $100 investment after 20 years? What is the best-case scenario? What is the worst-case scenario? What are limitations of this simulation analysis?

Based on the results of the Monte Carlo’s 51 simulations, there would be a reasonable expectation that 100 dollars could grow to around 238 dollars in twenty years. In the best case scenario, the simulations had maximum growth on the original 100 dollar investment to reach $539. Alternatively, the worst-case projections were that the original investment could deteriorate to 68.9 dollars. I expanded the data’s time frame to ten years, so that both bear and bull market conditions would have an influence on the simulation results. As such, the forecasted growth of 138 dollars appears to be reasonable without acknowledging the simulations limitations. However, if we were to remove the assumed normal distribution of returns, the results would likely not be as profitable. Furthermore, while Monte Carlo simulations can be useful for estimating the likelihood of a gain or loss, these simulations use averages to predict what is most likely based on these averages. Averages can be insightful for quickly interpreting data, but if the data varies drastically between extremes, than this may lead to inaccuracies that will misrepresent real world circumstances and the investors expectations may be thrown off.