1 Introduction

This analysis compares three investment portfolios using their discrete return distributions. It calculates expected return, variance, and standard deviation, then uses Monte Carlo simulation to estimate return distributions and the probability of a loss. The results support recommendations for conservative, moderate, and aggressive investors.

2 Input data

The probabilities for each portfolio should sum to 1.

portfolio_returns <- data.frame(
  Portfolio = c("A", "A", "A", "B", "B", "B", "C", "C", "C"),
  Return = c(0.15, 0.08, -0.05, 0.10, 0.06, -0.02, 0.07, 0.05, -0.01),
  Probability = c(0.30, 0.50, 0.20, 0.40, 0.40, 0.20, 0.60, 0.30, 0.10)
)
knitr::kable(portfolio_returns, digits = 3,
             caption = "Portfolio return outcomes and probabilities")
Portfolio return outcomes and probabilities
Portfolio Return Probability
A 0.15 0.3
A 0.08 0.5
A -0.05 0.2
B 0.10 0.4
B 0.06 0.4
B -0.02 0.2
C 0.07 0.6
C 0.05 0.3
C -0.01 0.1
probability_check <- aggregate(Probability ~ Portfolio,
                               data = portfolio_returns, sum)
knitr::kable(probability_check, digits = 2,
             caption = "Probability totals by portfolio")
Probability totals by portfolio
Portfolio Probability
A 1
B 1
C 1

3 Part A: Statistical analysis

For a discrete return variable \(X\), expected return is

\[E(X) = \sum_i x_i p_i\]

Variance is

\[\operatorname{Var}(X) = \sum_i p_i(x_i - E(X))^2\]

Standard deviation is the square root of variance:

\[SD(X) = \sqrt{\operatorname{Var}(X)}\]

portfolio_summary <- do.call(rbind, lapply(
  split(portfolio_returns, portfolio_returns$Portfolio),
  function(d) {
    expected_return <- sum(d$Return * d$Probability)
    variance <- sum(d$Probability * (d$Return - expected_return)^2)
    data.frame(
      Portfolio = d$Portfolio[1],
      Expected_Return = expected_return,
      Variance = variance,
      Standard_Deviation = sqrt(variance),
      Probability_of_Loss = sum(d$Probability[d$Return < 0])
    )
  }
))
rownames(portfolio_summary) <- NULL

formatted_summary <- data.frame(
  Portfolio = portfolio_summary$Portfolio,
  `Expected return` = sprintf("%.2f%%", 100 * portfolio_summary$Expected_Return),
  Variance = sprintf("%.6f", portfolio_summary$Variance),
  `Standard deviation` = sprintf("%.2f%%", 100 * portfolio_summary$Standard_Deviation),
  `Probability of loss` = sprintf("%.1f%%", 100 * portfolio_summary$Probability_of_Loss),
  check.names = FALSE
)
knitr::kable(formatted_summary, caption = "Analytical risk and return measures")
Analytical risk and return measures
Portfolio Expected return Variance Standard deviation Probability of loss
A 7.50% 0.004825 6.95% 20.0%
B 6.00% 0.001920 4.38% 20.0%
C 5.60% 0.000564 2.37% 10.0%

3.1 Interpretation

Portfolio A has the highest expected return, while Portfolio C has the lowest variance and standard deviation. Portfolio B falls between A and C in expected return and risk. Expected return is a probability-weighted average, not a guaranteed result.

4 Part B: Monte Carlo simulation

Each simulation draws 10,000 returns from each portfolio’s discrete distribution using the supplied probabilities. This simulates repeated one-period outcomes and does not assume normally distributed returns.

n_simulations <- 10000

portfolio_groups <- split(portfolio_returns, portfolio_returns$Portfolio)
simulated_returns <- lapply(portfolio_groups, function(d) {
  sample(d$Return, size = n_simulations, replace = TRUE, prob = d$Probability)
})

simulation_summary <- data.frame(
  Portfolio = names(simulated_returns),
  Simulated_Mean = sapply(simulated_returns, mean),
  Simulated_SD = sapply(simulated_returns, sd),
  Estimated_Loss_Probability = sapply(simulated_returns, function(x) mean(x < 0)),
  row.names = NULL
)

comparison <- merge(portfolio_summary, simulation_summary,
                    by = "Portfolio", sort = TRUE)

simulation_table <- data.frame(
  Portfolio = comparison$Portfolio,
  `Expected return (analytical)` = sprintf("%.2f%%", 100 * comparison$Expected_Return),
  `Mean return (simulated)` = sprintf("%.2f%%", 100 * comparison$Simulated_Mean),
  `SD (analytical)` = sprintf("%.2f%%", 100 * comparison$Standard_Deviation),
  `SD (simulated)` = sprintf("%.2f%%", 100 * comparison$Simulated_SD),
  `Loss probability (analytical)` = sprintf("%.2f%%", 100 * comparison$Probability_of_Loss),
  `Loss probability (simulated)` = sprintf("%.2f%%", 100 * comparison$Estimated_Loss_Probability),
  check.names = FALSE
)
knitr::kable(simulation_table,
             caption = "Analytical results compared with Monte Carlo estimates")
Analytical results compared with Monte Carlo estimates
Portfolio Expected return (analytical) Mean return (simulated) SD (analytical) SD (simulated) Loss probability (analytical) Loss probability (simulated)
A 7.50% 7.50% 6.95% 6.94% 20.00% 19.98%
B 6.00% 6.01% 4.38% 4.36% 20.00% 19.70%
C 5.60% 5.62% 2.37% 2.37% 10.00% 9.88%

4.1 Compare the return distributions

plot(NULL, xlim = range(portfolio_returns$Return) * 100,
     ylim = c(0, 0.7),
     xlab = "Return (%)", ylab = "Simulated probability",
     main = "Monte Carlo Return Distributions")
plot_colors <- c(A = "steelblue", B = "darkorange", C = "darkgreen")
for (p in names(simulated_returns)) {
  outcomes <- sort(unique(portfolio_groups[[p]]$Return))
  probs <- vapply(outcomes, function(outcome) {
    mean(simulated_returns[[p]] == outcome)
  }, numeric(1))
  points(outcomes * 100, probs, type = "b", pch = 16, col = plot_colors[p])
}
legend("topright", legend = paste("Portfolio", names(plot_colors)),
       col = plot_colors, pch = 16, lty = 1, bty = "n")

4.2 Estimated probability of loss

A loss is defined as a return strictly below zero.

barplot(
  height = 100 * simulation_summary$Estimated_Loss_Probability,
  names.arg = simulation_summary$Portfolio,
  xlab = "Portfolio",
  ylab = "Estimated probability of loss (%)",
  main = "Monte Carlo Estimated Probability of Loss",
  ylim = c(0, max(25, 100 * simulation_summary$Estimated_Loss_Probability + 5))
)

4.3 Interpretation of simulation

The simulation estimates should be close to the theoretical probabilities: 20% for Portfolio A, 20% for Portfolio B, and 10% for Portfolio C. Small differences are expected because the simulation uses random sampling.

5 Part C: Investment recommendation

recommendations <- data.frame(
  Investor = c("Conservative", "Moderate", "Aggressive"),
  Portfolio = c("C", "B", "A")
)
for (i in seq_len(nrow(recommendations))) {
  p <- recommendations$Portfolio[i]
  row <- portfolio_summary[portfolio_summary$Portfolio == p, ]
  cat(
    "**", recommendations$Investor[i], " investors — Portfolio ", p, "**\n\n",
    "Expected return: ", sprintf("%.2f%%", 100 * row$Expected_Return),
    "; standard deviation: ", sprintf("%.2f%%", 100 * row$Standard_Deviation),
    "; probability of loss: ", sprintf("%.1f%%", 100 * row$Probability_of_Loss),
    ".\n\n", sep = ""
  )
}

Conservative investors — Portfolio C

Expected return: 5.60%; standard deviation: 2.37%; probability of loss: 10.0%.

Moderate investors — Portfolio B

Expected return: 6.00%; standard deviation: 4.38%; probability of loss: 20.0%.

Aggressive investors — Portfolio A

Expected return: 7.50%; standard deviation: 6.95%; probability of loss: 20.0%.

Portfolio C is most suitable for conservative investors because it has the lowest standard deviation and probability of loss. Portfolio B may suit moderate investors who want a somewhat higher expected return while accepting more variability. Portfolio A may suit aggressive investors seeking the highest expected return and willing to tolerate the greatest variation in outcomes.

These recommendations are based on the supplied one-period return distributions. Real investment decisions should also consider diversification, fees, liquidity needs, investment horizon, and the investor’s ability to withstand losses.

6 Conclusion

Portfolio A has the highest expected return, Portfolio C has the lowest variability and lowest probability of loss, and Portfolio B occupies an intermediate position. The Monte Carlo simulation provides estimates that can be compared with the analytical results to check that the simulation behaves as expected.