library(tidyverse)
library(tidyquant)
simulate_accumulation <- function(initial_value, N, mean_return, sd_return) {
growth <- initial_value * cumprod(1 + rnorm(N, mean_return, sd_return))
return(growth)
}
symbols <- c("AAPL", "TGT", "AMZN", "WMT", "PEP")
prices <- tq_get(
x = symbols,
get = "stock.prices",
from = "2012-12-31",
to = "2017-12-31"
)
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"))
asset_returns_tbl
## # A tibble: 300 × 3
## asset date returns
## <chr> <date> <dbl>
## 1 AAPL 2013-01-31 -0.156
## 2 AAPL 2013-02-28 -0.0256
## 3 AAPL 2013-03-28 0.00285
## 4 AAPL 2013-04-30 0.000271
## 5 AAPL 2013-05-31 0.0222
## 6 AAPL 2013-06-28 -0.126
## 7 AAPL 2013-07-31 0.132
## 8 AAPL 2013-08-30 0.0804
## 9 AAPL 2013-09-30 -0.0217
## 10 AAPL 2013-10-31 0.0920
## # ℹ 290 more rows
symbols <- asset_returns_tbl %>% distinct(asset) %>% pull()
weights <- c(0.25, 0.25, 0.2, 0.2, 0.1)
w_tbl <- tibble(symbols, weights)
w_tbl
## # A tibble: 5 × 2
## symbols weights
## <chr> <dbl>
## 1 AAPL 0.25
## 2 AMZN 0.25
## 3 PEP 0.2
## 4 TGT 0.2
## 5 WMT 0.1
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: 60 × 2
## date returns
## <date> <dbl>
## 1 2013-01-31 -0.00557
## 2 2013-02-28 0.0123
## 3 2013-03-28 0.0344
## 4 2013-04-30 0.00606
## 5 2013-05-31 0.0109
## 6 2013-06-28 -0.0221
## 7 2013-07-31 0.0689
## 8 2013-08-30 -0.0350
## 9 2013-09-30 0.0255
## 10 2013-10-31 0.0785
## # ℹ 50 more rows
mean_port_return <- mean(portfolio_returns_tbl$returns)
stddev_port_return <- sd(portfolio_returns_tbl$returns)
mean_port_return
## [1] 0.0141929
stddev_port_return
## [1] 0.03620388
sims <- 51
starts <- rep(100, sims) %>% set_names(paste0("sim", 1:sims))
set.seed(1234)
monte_carle_sim_51 <- starts %>%
map_dfc(
.f = ~simulate_accumulation(
initial_value = .x,
N = 240,
mean_return = mean_port_return,
sd_return = stddev_port_return
)
) %>%
mutate(month = 1:nrow(.)) %>%
select(month, everything()) %>%
set_names(c("month", names(starts))) %>%
pivot_longer(cols = -month, names_to = "sim", values_to = "growth")
monte_carle_sim_51
## # A tibble: 12,240 × 3
## month sim growth
## <int> <chr> <dbl>
## 1 1 sim1 97.0
## 2 1 sim2 102.
## 3 1 sim3 98.7
## 4 1 sim4 94.2
## 5 1 sim5 107.
## 6 1 sim6 96.3
## 7 1 sim7 101.
## 8 1 sim8 99.5
## 9 1 sim9 100.
## 10 1 sim10 101.
## # ℹ 12,230 more rows
monte_carle_sim_51 %>%
group_by(sim) %>%
summarise(growth = last(growth)) %>%
pull(growth) %>%
quantile(c(0, .25, .5, .75, 1)) %>%
round(2)
## 0% 25% 50% 75% 100%
## 794.61 2023.56 2909.84 4139.79 6849.72
sim_summary <- monte_carle_sim_51 %>%
group_by(sim) %>%
summarise(growth = last(growth)) %>%
summarise(
max = max(growth),
median = median(growth),
min = min(growth)
)
sim_summary
## # A tibble: 1 × 3
## max median min
## <dbl> <dbl> <dbl>
## 1 6850. 2910. 795.
monte_carle_sim_51 %>%
group_by(sim) %>%
filter(
last(growth) == sim_summary$max |
last(growth) == sim_summary$median |
last(growth) == sim_summary$min
) %>%
ungroup() %>%
ggplot(aes(month, growth, color = sim)) +
geom_line() +
theme(legend.position = "none") +
labs(
title = "Simulating Growth of $100 over 240 Months",
subtitle = "Maximum, Median, and Minimum Simulation"
)
Expected value after 20 years: ~ $6,632
Best-case scenario: ~ $25,000
Worst-case scenario: ~ $700
Assumes the future behaves like the past
Assumes smooth, normally distributed returns
Ignores market shocks, regime changes, fees, and behavioral risks