Portfolio Optimisation in R

For this tutorial, both minimum-variance and mean-variance will be taught. The PortfolioAnalytics package will be used extensively throughout as it allows for a simple workflow for portfolio optimisations. The first part of the code is to define that a portfolio optimisation problem exists. The only variable that needs to be defined is the names of the components of the portfolio. It will then print out a summary of the portfolio defined so far.

port <- portfolio.spec(assets = colnames((stockReturns)))
port
## **************************************************
## PortfolioAnalytics Portfolio Specification 
## **************************************************
## 
## Call:
## portfolio.spec(assets = colnames((stockReturns)))
## 
## Number of assets: 2 
## Asset Names
## [1] "AAPL.Adjusted" "GOOG.Adjusted"

Next, you’d want to add some constraints to the portfolio. Popular ones would be full_investment, meaning that the sum of the assets must equal 100%, weight_sum, which constrains the total weight of the portfolio to a specific percentage, box, which allows for a minimum and maximum weight for individual assets, and long_only, where assets weights can only be positive. In this example, full_weights, long_only and box will be used. When you print a summary of the port object, it will now show the constraints.

port <- add.constraint(portfolio = port,
                       type = "full_investment")
port <- add.constraint(portfolio = port,
                       type = "long_only")
port <- add.constraint(portfolio = port,
                       type = "box",
                       min = 0.0,
                       max = 0.7)
port
## **************************************************
## PortfolioAnalytics Portfolio Specification 
## **************************************************
## 
## Call:
## portfolio.spec(assets = colnames((stockReturns)))
## 
## Number of assets: 2 
## Asset Names
## [1] "AAPL.Adjusted" "GOOG.Adjusted"
## 
## Constraints
## Enabled constraint types
##      - full_investment 
##      - long_only 
##      - box

Finally, you’ll want to add the objective metric to optimise. To do this, add.objective is used. To maximise the objective, one specifies the type argument to “return”; to minimise the objective, one specifies the type argument to “risk”. The first example will be for a minimum variance portfolio, so the type will be risk and the metric (argument name) that we are looking to minimise is the standard deviation. To reduce code, the portfolio port will be duplicated for portMinVar.

portMinVar <- port
portMinVar <- add.objective(portfolio = portMinVar,
                            type = "risk",
                            name = "StdDev")
portMinVar
## **************************************************
## PortfolioAnalytics Portfolio Specification 
## **************************************************
## 
## Call:
## portfolio.spec(assets = colnames((stockReturns)))
## 
## Number of assets: 2 
## Asset Names
## [1] "AAPL.Adjusted" "GOOG.Adjusted"
## 
## Constraints
## Enabled constraint types
##      - full_investment 
##      - long_only 
##      - box 
## 
## Objectives:
## Enabled objective names
##      - StdDev

Now the objective has been added. To do it for mean-variance, there would be 2 objectives: maximise returns (mean sample returns) whilst minimising standard deviation. Below is the example code:

portMeanVar <- port
portMeanVar <- add.objective(portfolio = portMeanVar,
                             type = "risk",
                             name = "StdDev")
portMeanVar <- add.objective(portfolio = portMeanVar,
                             type = "return",
                             name = "mean")
portMeanVar
## **************************************************
## PortfolioAnalytics Portfolio Specification 
## **************************************************
## 
## Call:
## portfolio.spec(assets = colnames((stockReturns)))
## 
## Number of assets: 2 
## Asset Names
## [1] "AAPL.Adjusted" "GOOG.Adjusted"
## 
## Constraints
## Enabled constraint types
##      - full_investment 
##      - long_only 
##      - box 
## 
## Objectives:
## Enabled objective names
##      - StdDev 
##      - mean

You will notice that there are now two objectives. Finally, the portfolios are going to be optimised and the returns compared. The arguments are the portfolio specified, the returns of each asset and the optimisation method. Generally, random works best as it is a global optimisation problem. One can then extract the weights and create portfolio returns using the Return.portfolio function.

set.seed(10260)
minVarOpt <- optimize.portfolio(R = stockReturns,
                                portfolio = portMinVar,
                                optimize_method = "random")
## Leverage constraint min_sum and max_sum are restrictive, 
##               consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
## Warning: executing %dopar% sequentially: no parallel backend registered
extractWeights(minVarOpt)
## AAPL.Adjusted GOOG.Adjusted 
##          0.43          0.57
minVarReturns <- Return.portfolio(stockReturns, weight = extractWeights(minVarOpt), rebalance_on = "months")
table.AnnualizedReturns(R = minVarReturns, Rf = 0.1/250)
##                               portfolio.returns
## Annualized Return                        0.1745
## Annualized Std Dev                       0.2100
## Annualized Sharpe (Rf=10.08%)            0.2951

Let’s repeat for mean-variance optimisation and compare performance.

set.seed(10260)
meanVarOpt <- optimize.portfolio(R = stockReturns,
                                portfolio = portMeanVar,
                                optimize_method = "random")
## Leverage constraint min_sum and max_sum are restrictive, 
##               consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
extractWeights(meanVarOpt)
## AAPL.Adjusted GOOG.Adjusted 
##          0.44          0.56
meanVarReturns <- Return.portfolio(stockReturns, weight = extractWeights(meanVarOpt), rebalance_on = "months")
optimisedPortfolioReturns <- cbind(minVarReturns, meanVarReturns)
colnames(optimisedPortfolioReturns) <- c("Minimum Variance", "Mean Variance")
table.AnnualizedReturns(R = optimisedPortfolioReturns, Rf = 0.1/252)
##                            Minimum Variance Mean Variance
## Annualized Return                    0.1745        0.1749
## Annualized Std Dev                   0.2100        0.2100
## Annualized Sharpe (Rf=10%)           0.2991        0.3007
Finance Train Premium
Accelerate your finance career with cutting-edge data skills.
Join Finance Train Premium for unlimited access to a growing library of ebooks, projects and code examples covering financial modeling, data analysis, data science, machine learning, algorithmic trading strategies, and more applied to real-world finance scenarios.
I WANT TO JOIN
JOIN 30,000 DATA PROFESSIONALS

Free Guides - Getting Started with R and Python

Enter your name and email address below and we will email you the guides for R programming and Python.

Saylient AI Logo

Accelerate your finance career with cutting-edge data skills.

Join Finance Train Premium for unlimited access to a growing library of ebooks, projects and code examples covering financial modeling, data analysis, data science, machine learning, algorithmic trading strategies, and more applied to real-world finance scenarios.