R/portfolio_simulations.R

Defines functions portfolio_simulations

#' Simulate Portfolios
#'
#' Simulate random portfolios and calculate risk, return and sharpe ratio
#' @param prices Data frame of daily asset prices
#' @param n_portfolios Number of portfolios to be simulated
#' @param risk_measure Default standard deviation. Other possible value-at-risk and expected shortfall
#' @param method Either historical or normal distribution method if the risk measure is either value-at-risk or expected shortfall then
#' @param significance Significance level for value-at-risk and expected shortfall. Deafult set to 0.05
#' @param risk_free Risk free interest rate to calculate sharpe ratio
#' @param plot If TRUE plot portfolios
#'
#' @return Portfolios with minimum risk and max sharpe ratio.
#' @export
#'
#' @examples
#' portfolio_simulations(close_prices, 1e3, risk_measure = 'standard deviation')
#' portfolio_simulations(close_prices, 1e3, risk_measure = 'value-at-risk', method = 'historical')
#' portfolio_simulations(close_prices, 1e3, risk_measure = 'value-at-risk', method = 'normal')
#' portfolio_simulations(close_prices, 1e3, risk_measure = 'expected shortfall', method = 'historical')
#' portfolio_simulations(close_prices, 1e3, risk_measure = 'expected shortfall', method = 'normal')

portfolio_simulations <- function(prices
                                  ,n_portfolios = 1e3
                                  ,risk_measure = c('standard deviation'
                                                    ,'value-at-risk'
                                                    ,'expected shortfall')
                                  ,method = c('historical', 'normal')
                                  ,significance = 0.05
                                  ,risk_free = 0.01
                                  ,plot = TRUE) {

  results <- list()

  returns <- as.data.frame(
    sapply(
      as.data.frame(
        sapply(dplyr::select(prices, where(is.numeric))
               ,log))
      ,diff
    )
  )

  # Calculate annualized asset parameters
  returns_mean <- (colMeans(returns) + 1)^252 - 1
  returns_cov <- cov(returns)*252
  returns_std <- sqrt(diag(returns_cov))

  # Initialize weights and df for portfolio parameters ##
  weights <- matrix(nrow = n_portfolios
                    ,ncol = dim(returns)[2])

  portfolios <- data.frame(return = rep(0, n_portfolios)
                           ,risk = rep(0, n_portfolios)
                           ,sharpe = rep(0, n_portfolios))

  # Simulation
  for (i in 1:n_portfolios) {

    random_weights <- runif(ncol(returns))
    random_weights <- random_weights / sum(random_weights)
    weights[i,] <- random_weights

    p_returns <- portfolio_returns(prices, random_weights)
    portfolios$return[i] <- random_weights %*% returns_mean

    portfolios$risk[i] <- switch(
      risk_measure
      ,`standard deviation` = sqrt(t(random_weights) %*% (returns_cov %*% random_weights))
      ,`value-at-risk` = -value_at_risk(p_returns
                                        ,method = method
                                        ,significance = significance
                                        ,plot = FALSE)$VaR
      ,`expected shortfall` = -value_at_risk(p_returns
                                             ,method = method
                                             ,significance = significance
                                             ,plot = FALSE)$ES
    )

    portfolios$sharpe[i] <- (portfolios$return[i] - risk_free) / portfolios$risk[i]

  }

  max_sharpe <- round(100 * weights[which.max(portfolios$sharpe),], 2)
  names(max_sharpe) <- names(returns)
  results$max_sharpe <- max_sharpe

  min_risk <- round(100 * weights[which.min(portfolios$risk),], 2)
  names(min_risk) <- names(returns)
  results$min_risk <- min_risk

  if (plot == TRUE) {

    ggplot(portfolios) +
      geom_point(aes(x = risk
                     ,y = return
                     ,color = sharpe)) +
      theme_bw() +
      labs(x = risk_measure
           ,y = 'expected returns'
           ,title = paste0('Simulation of ', n_portfolios, ' portfolios'))

  }

  return(results)

}
pawel-wieczynski/PolishStock documentation built on March 23, 2022, 3:32 p.m.