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

Get smart about tech at work.

As a non-technical professional, learn how software works with simple explanations of tech concepts. Learn more...

Data Science for Finance Bundle: 43% OFF

Get our Data Science for Finance Bundle for just $29 $51.
Get it now for just $29

Checkout our eBooks and Templates

eBooks and templates related to finance, R programming, Python, and Excel.
Visit Store
Get our Data Science for Finance Bundle for just $29 $51. That's 43% OFF.
Get it for $51 $29