Portfolio Optimisation in R

Premium

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.

1port <- portfolio.spec(assets = colnames((stockReturns)))
2port
3## **************************************************
4## PortfolioAnalytics Portfolio Specification 
5## **************************************************
6## 
7## Call:
8## portfolio.spec(assets = colnames((stockReturns)))
9## 
10## Number of assets: 2 
11## Asset Names
12## [1] "AAPL.Adjusted" "GOOG.Adjusted"
13

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.

1port <- add.constraint(portfolio = port,
2                       type = "full_investment")
3port <- add.constraint(portfolio = port,
4                       type = "long_only")
5port <- add.constraint(portfolio = port,
6                       type = "box",
7                       min = 0.0,
8                       max = 0.7)
9port
10## **************************************************
11## PortfolioAnalytics Portfolio Specification 
12## **************************************************
13## 
14## Call:
15## portfolio.spec(assets = colnames((stockReturns)))
16## 
17## Number of assets: 2 
18## Asset Names
19## [1] "AAPL.Adjusted" "GOOG.Adjusted"
20## 
21## Constraints
22## Enabled constraint types
23##      - full_investment 
24##      - long_only 
25##      - box
26

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.

1portMinVar <- port
2portMinVar <- add.objective(portfolio = portMinVar,
3                            type = "risk",
4                            name = "StdDev")
5portMinVar
6## **************************************************
7## PortfolioAnalytics Portfolio Specification 
8## **************************************************
9## 
10## Call:
11## portfolio.spec(assets = colnames((stockReturns)))
12## 
13## Number of assets: 2 
14## Asset Names
15## [1] "AAPL.Adjusted" "GOOG.Adjusted"
16## 
17## Constraints
18## Enabled constraint types
19##      - full_investment 
20##      - long_only 
21##      - box 
22## 
23## Objectives:
24## Enabled objective names
25##      - StdDev
26

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:

1portMeanVar <- port
2portMeanVar <- add.objective(portfolio = portMeanVar,
3                             type = "risk",
4                             name = "StdDev")
5portMeanVar <- add.objective(portfolio = portMeanVar,
6                             type = "return",
7                             name = "mean")
8portMeanVar
9## **************************************************
10## PortfolioAnalytics Portfolio Specification 
11## **************************************************
12## 
13## Call:
14## portfolio.spec(assets = colnames((stockReturns)))
15## 
16## Number of assets: 2 
17## Asset Names
18## [1] "AAPL.Adjusted" "GOOG.Adjusted"
19## 
20## Constraints
21## Enabled constraint types
22##      - full_investment 
23##      - long_only 
24##      - box 
25## 
26## Objectives:
27## Enabled objective names
28##      - StdDev 
29##      - mean
30

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.

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

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

1set.seed(10260)
2meanVarOpt <- optimize.portfolio(R = stockReturns,
3                                portfolio = portMeanVar,
4                                optimize_method = "random")
5## Leverage constraint min_sum and max_sum are restrictive, 
6##               consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
7extractWeights(meanVarOpt)
8## AAPL.Adjusted GOOG.Adjusted 
9##          0.44          0.56
10meanVarReturns <- Return.portfolio(stockReturns, weight = extractWeights(meanVarOpt), rebalance_on = "months")
11optimisedPortfolioReturns <- cbind(minVarReturns, meanVarReturns)
12colnames(optimisedPortfolioReturns) <- c("Minimum Variance", "Mean Variance")
13table.AnnualizedReturns(R = optimisedPortfolioReturns, Rf = 0.1/252)
14##                            Minimum Variance Mean Variance
15## Annualized Return                    0.1745        0.1749
16## Annualized Std Dev                   0.2100        0.2100
17## Annualized Sharpe (Rf=10%)           0.2991        0.3007
18