R offers so many benefits to those interested in Quantitative Finance. It makes life super easy in terms of getting data, and in this particular case analyzing the data and constructing an optimal portfolio. Doing this in Excel is arduous. Getting bulk data via an API is slow. And automating calculations for the efficient frontier takes forever. Python is not on R’s level yet in terms of packages for finance either.

In general, in order to construct a minimum variance portfolio you need three things:

- Historical Returns
- Historical Volatility
- Some sort of covariance matrix or correlation matrix

In order to run this script, we will need a few packages. I would like to highlight one in particular, fPortfolio. This package is specifically geared towards portfolio optimization.

We need to prepare a time series object in order to plot the Efficient Frontier for a given portfolio. First, we will construct a vector of tickers and gather prices for them using the getSymbols function within quantmod. We will next calculate returns and convert the data to a time series object.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
library(timeSeries) library(fPortfolio) library(quantmod) library(caTools) library(dplyr) library(PerformanceAnalytics) library(ggplot2) ######################STEP ONE: Create Returns Time Series######################################### #Create Vector of Tickers tickers <- c("AXP", "C", "WFC", "AMZN", "JNJ", "HD") #Calculate Returns: Daily portfolioPrices <- NULL for (Ticker in tickers) portfolioPrices <- cbind(portfolioPrices, getSymbols.google(Ticker, from="2016-01-01", auto.assign=FALSE)[,4]) #Delete all dates with no prices portfolioPrices <- portfolioPrices[apply(portfolioPrices,1,function(x) all(!is.na(x))),] #Rename Columns colnames(portfolioPrices) <- tickers #Calculate Returns: Daily RoC portfolioReturns <- na.omit(ROC(portfolioPrices, type="discrete")) portfolioReturns <- as.timeSeries(portfolioReturns) #Calculate Monthly or Weekly Returns Stock_Data <- tickers %>% lapply(function(x) getSymbols.google(x, from="2016-01-01", auto.assign=FALSE)[,4]) %>% lapply(function(x) monthlyReturn(x)) portfolioReturns <- do.call(merge, Stock_Data) # keep only the dates that have closing prices for all tickers portfolioReturns <- portfolioReturns[apply(portfolioReturns,1,function(x) all(!is.na(x))),] colnames(portfolioReturns) <- tickers portfolioReturns <- as.timeSeries(portfolioReturns) |

We can now calculate and plot the Efficient Frontier. We will do this by using the portfolioFrontier() function within R. This is what makes this package so great. We can overlay many different series’ on top of the frontier. The options are displayed below. We can also output the covariance matrix and correlation matrix for our portfolio of assets. On top of all of this, we can extract certain portfolios such as the Minimum Variance Portfolio or Max Return Portfolio as well as their individual characteristics. These characteristics include the expected return for each asset, the expected volatility for each asset, as well as different Value at Risk calculations. We can isolate any of these characteristics as well to graph and compare. Let’s examine some.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
#################STEP TWO: Calculate and Plot Frontier and Efficient Portfolios############## # calculate the efficient frontier effFrontier <- portfolioFrontier(portfolioReturns, constraints = "LongOnly") # plot frontier #'Options #'1: Plot Efficient Frontier #'2: Plot Minimum Variance Portfolio #'3: Plot Tangency Portfolio #'4: Plot Risk Returns of Each Asset #'5: Plot Equal Weights Portfolio #'6: Plot Two Asset Frontiers (Long) #'7: Plot Monte Carlo Portfolios #'8: Plot Sharpe Ratio plot(effFrontier,c(1,2,3,4)) #Plot Frontier Weights (Can Adjust Number of Points) frontierWeights <- getWeights(effFrontier) # get allocations for each instrument for each point on the efficient frontier colnames(frontierWeights) <- tickers risk_return <- frontierPoints(effFrontier) write.csv(risk_return, "risk_return.csv") #Output Correlation cor_matrix <- cor(portfolioReturns) cov_matrix <- cov(portfolioReturns) write.csv(cov_matrix, "covmatrix.csv") cov_matrix #Annualize Data riskReturnPoints <- frontierPoints(effFrontier) # get risk and return values for points on the efficient frontier annualizedPoints <- data.frame(targetRisk=riskReturnPoints[, "targetRisk"] * sqrt(252), targetReturn=riskReturnPoints[,"targetReturn"] * 252) plot(annualizedPoints) # plot Sharpe ratios for each point on the efficient frontier riskFreeRate <- 0 plot((annualizedPoints[,"targetReturn"]-riskFreeRate) / annualizedPoints[,"targetRisk"], xlab="point on efficient frontier", ylab="Sharpe ratio") #Plot Frontier Weights (Need to transpose matrix first) barplot(t(frontierWeights), main="Frontier Weights", col=cm.colors(ncol(frontierWeights)+2), legend=colnames(frontierWeights)) #Get Minimum Variance Port, Tangency Port, etc. mvp <- minvariancePortfolio(portfolioReturns, spec=portfolioSpec(), constraints="LongOnly") mvp tangencyPort <- tangencyPortfolio(portfolioReturns, spec=portfolioSpec(), constraints="LongOnly") tangencyPort mvpweights <- getWeights(mvp) tangencyweights <- getWeights(tangencyPort) #Extract value at risk covRisk(portfolioReturns, mvpweights) varRisk(portfolioReturns, mvpweights, alpha = 0.05) cvarRisk(portfolioReturns, mvpweights, alpha = 0.05) #Plot MVP Weights: Basic Graphs barplot(mvpweights, main="Minimum Variance Portfolio Weights", xlab="Assset", ylab="Weight In Portfolio (%)", col=cm.colors(ncol(frontierWeights)+2), legend=colnames(weights)) pie(mvpweights, col=cm.colors(ncol(frontierWeights)+2)) #ggplot MVP Weights df <- data.frame(mvpweights) assets <- colnames(frontierWeights) ggplot(data=df, aes(x=assets, y=mvpweights, fill=assets)) + geom_bar(stat="identity", position=position_dodge(),colour="black") + geom_text(aes(label=sprintf("%.02f %%",mvpweights*100)), position=position_dodge(width=0.9), vjust=-0.25, check_overlap = TRUE) + ggtitle("Minimum Variance Portfolio Optimal Weights")+ theme(plot.title = element_text(hjust = 0.5)) + labs(x= "Assets", y = "Weight (%)") dft <- data.frame(tangencyweights) assets <- colnames(frontierWeights) ggplot(data=dft, aes(x=assets, y=tangencyweights, fill=assets)) + geom_bar(stat="identity", position=position_dodge(),colour="black") + geom_text(aes(label=sprintf("%.02f %%",tangencyweights*100)), position=position_dodge(width=0.9), vjust=-0.25, check_overlap = TRUE) + ggtitle("Tangency Portfolio Weights")+ theme(plot.title = element_text(hjust = 0.5)) + labs(x= "Assets", y = "Weight (%)") #ggplot Pie bar <- ggplot(df, aes(x = "", y = mvpweights, fill=assets)) + geom_bar(width= 1, stat="identity") + ggtitle("Minimum Variance Portfolio Weights")+ theme(plot.title = element_text(hjust = 0.5)) pie <- bar + coord_polar("y", start=0) pie + scale_fill_brewer(palette="Blues")+ theme_minimal() bar <- ggplot(dft, aes(x = "", y = tangencyweights, fill=assets)) + geom_bar(width= 1, stat="identity") + ggtitle("Tangency Portfolio Weights")+ theme(plot.title = element_text(hjust = 0.5)) pie <- bar + coord_polar("y", start=0) pie + scale_fill_brewer(palette="Blues")+ theme_minimal() |

We can output the weights generated by the frontier and save them to a CSV file. We can also output the mean and volatility for each point also.

Let’s examine the weights of each point on the frontier graphically:

We can annualize this data and plot the risk returns on a scatter plot also. Note we can also annualize the returns data with the performance analytics library also as mentioned before. We can also plot the Sharpe Ratio of each point on the frontier on a scatter graph also.

Let’s examine the correlation matrix and covariance matrix associated with our assets also.

Next, we can examine specific classes of portfolios that could lie on the frontier. These can include the Tangency Portfolio, Minimum Variance Portfolio. In the documentation for fPortfolio, the following options are available as portfolio classes:

- feasiblePortfolio
- cmlPortfolio (deprecated)
- tangencyPortfolio
- efficientPortfolio
- minvariancePortfolio

Let’s output some basic stats for the Minimum Variance Portfolio and Tangency Portfolio. The Tangency Portfolio is one where the Sharpe Ratio is maximized. We can also use the ggplot2 library to output some awesome graphs.

We can also examine the Efficient Frontier under different constraints and specifications. I provided a list of the available constraints and specifications below.

Constraints List (Truncated)

minW | minimum weight in asset |

maxW | maximum weight in asset |

eqsumW | sum of weights (assets) |

minsumW | minimum sum of weights (assets) |

maxsumW | maximum sum of weights (assets) |

Specs List

setType | Sets type of portfolio optimization |

setOptimize | Sets what to optimize, min risk or max return |

setEstimator | Sets names of mean and covariance estimators |

setParams | Sets optional model parameters |

setWeights | Sets weights vector |

setTargetReturn | Sets target return value |

setTargetRisk | Sets target risk value |

setTargetAlpha | Sets CVaR target alpha value |

setRiskFreeRate | Sets risk-free rate value |

setNFrontierPoints | Sets number of frontier points |

setStatus | Sets status value |

setSolver | Sets the type of solver to be used |

setObjective | Sets objective function name to be used |

setTrace | Sets the logical trace flag. |

Let’s implement some constraints and specifications in our portfolio. We will allow short selling in this portfolio and set the minimum and maximum weight in one given asset throughout the entire list of tickers.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
###########################Examine Constraints and Stats##################################### #Example Constraints: #"minW[asset]=percentage" for box constraints resp. #"maxsumW[assets]=percentage" for sector constraints. #eqsumWConstraints(data, spec=portfolioSpec(), constraints="LongOnly") #Set Specs Spec = portfolioSpec() setSolver(Spec) = "solveRshortExact" setTargetRisk(Spec) = .12 constraints <- c("minW[1:length(tickers)]=-1","maxW[1:length(tickers)]=.60", "Short") effFrontierShort <- portfolioFrontier(portfolioReturns, Spec, constraints = constraints) weights <- getWeights(effFrontierShort) write.csv(weights, "weightsShort.csv") colnames(weights) <- tickers plot(effFrontierShort, c(1, 2, 3)) #Plot Frontier Weights (Need to transpose matrix first) barplot(t(weights), main="Frontier Weights", col=cm.colors(ncol(weights)+2), legend=colnames(weights)) effPortShort <- minvariancePortfolio(portfolioReturns, Spec, constraints=constraints) optWeights <- getWeights(effPortShort) tanPortShort <- tangencyPortfolio(portfolioReturns, Spec, constraints=constraints) tanWeights <- getWeights(tanPortShort) maxR <- maxreturnPortfolio(portfolioReturns , Spec, constraints=constraints) maxWeights <- getWeights(maxR) #ggplot MVP Weights df <- data.frame(tanWeights) assets <- colnames(frontierWeights) ggplot(data=df, aes(x=assets, y=tanWeights, fill=assets)) + geom_bar(stat="identity", position=position_dodge(),colour="black") + geom_text(aes(label=sprintf("%.02f %%",tanWeights*100)), position=position_dodge(width=0.9), vjust=-0.25, check_overlap = TRUE) + ggtitle("Tangency Portfolio With Shorts Allowed")+ theme(plot.title = element_text(hjust = 0.5)) + labs(x= "Assets", y = "Weight (%)") |

Let’s examine the output on a minimal level.

In short, fPortfolio is extremely useful when analyzing a given portfolio of assets. We could prepare good looking automated reports with this package. fPortfolio also allows for backtesting strategies also and examining portfolios under rolling time frames as well. Theoretically, we could iterate over thousands of portfolio optimization models under different constraints and compare them. The backtesting features of this module are outside the scope of this post and will be discussed in later posts.

Hi Frank. Extremely interesting post but I can’t get it to work. Running the portfolioFrontier function throws the following error:

Error in

`colnames<-`

(`*tmp*`

, value = c("AXP", "C", "WFC", "AMZN", "JNJ", :attempt to set 'colnames' on an object with less than two dimensions

Any suggestions on how to solve this?

If you have 5 stocks (columns), you need to have more than 5 observations (prices) so rows must be greater than collumns.

Hi. I try to replicate the results. However, the target return and sharp ratio plot y-axes values are much larger than what you plotted. Are those plots annualized return based?

Another thing is that the spec constraint doesn’t really constrain the asset weights as it should be. I have a warning message:

In as.vector(invSigma %*% one)/(one %*% invSigma %*% one) :

Recycling array of length 1 in vector-array arithmetic is deprecated.

Use c() or as.vector() instead.

when I run the portfolio calculation involving spec and constraints.

I don’t know what went wrong.

Hi Frank. This is awesome. Thank you for sharing. I’m anxious waiting for the backtest and compare the performance of the optimized portfolio and naive diversified portfolio (1/n).

frankly this is awesome

Error in download.file(paste(google.URL, “q=”, Symbols.name, “&startdate=”, :

cannot open URL ‘http://finance.google.com/finance/historical?q=AXP&startdate=Jan+01,+2016&enddate=Mar+31,+2018&output=csv’

In addition: Warning message:

In download.file(paste(google.URL, “q=”, Symbols.name, “&startdate=”, :

cannot open URL ‘http://finance.google.com/finance/historical?q=AXP&startdate=Jan+01,+2016&enddate=Mar+31,+2018&output=csv’: HTTP status was ‘403 Forbidden’

could you pls update the correct download code beacause it seems that ist does not work anymore?

Hi Frank:

Thank you very much for the above. After I changed the sources for the prices it seemed to work. I have had many issues with fPortfolio before but your code didn’t throw any errors apart from getting data.

By the way, what are major differences between fPortfolio and FRAPO which appears to do the same thing.