A Single-Index Model Shiny App for ETFs

In the quantitative finance world the Single-Index Model (SIM) is commonly used to price assets by measuring both volatility and return of a stock. According to this model, the return of any stock can be decomposed into expected excess returns (the returns above those from a market index, for example ASX200) due to firm-specific factors and macroeconomic factors.

Luckily R has many useful applications for quantitative finance in libraries stockPortfolio and PerformanceAnalytics which I have been using for the last 2 years to balance a portfolio of Exchange-Traded Funds (ETFs).

EDIT: I was forced to re-write the app using tidyquant to get returns as the function stockPortfolio::getReturns stopped working with Yahoo Finance data.

(If you are familiar with ETFs, go ahead and skip the following paragraph).

ETFs are investment funds traded on stock exchanges just like a stock. ETFs can hold many types of assets such as bonds, commodities, stocks and its value changes over the course of the trading day, again, just like a regular stock. Most ETF’s aim to follow a given index instead of trying to aggressively beat the market like hedge funds, making it attractive due to low costs and relative transparency. Another advantage is the exposure to different markets and industries. For example, in Australia I am able to invest in the Chinese market or the American health industry by buying stocks of specific ETFs. Finally, another advantage is that, by following an index, ETFs have extremely reduce firm-specific risk and are mostly exposed to the macroeconomic environment.

I decided to use R and Shiny to build a visual interface that takes a number of inputs, runs a SIM and returns the weights for each stock for an optimal portfolio of Australian ETFs.

You can easily run the app locally on your own computer by running the following lines in your R console:

packages = c("shiny", "shinydashboard", "stockPortfolio", "PerformanceAnalytics", "highcharter", "DT")
install.packages(packages, repos = "https://cran.rstudio.com/")
library(shiny)
runGitHub("ETF-SIM", "Maiae")

 

ETF-SIM Shiny App

ETF-SIM Shiny App

 

Model Inputs

The sidebar contains input fields used by the SIM function stockPortfolio::stockModel. You can choose up to 6 ETF’s from a list of 148 ETFs currently traded in the Australian Stock Exchange (ASX).

The date range is used to select the range of returns to be downloaded from Yahoo Finance. The default option is the last 3 years. Frequency of returns can be weekly or daily. The model output will be affected by this option since some ETFs are more volatile daily but not so much when you look at weekly returns. Further, the time to run will increase when using daily data.

The risk free rate is linked to the concept of opportunity cost. If you think where could I invest my money with absolutely no risk and get the maximum return available? The closest answer is government bonds and in Australia this is represented by Australia Government Bond 10-year yield. As of December 2016 this yield was 2.87%.

The final input is portfolio value which is the total amount of cash to be invested.

 

Model Outputs

After hitting the button “Run Model”, four outputs are produced.

Output1

The first chart shows cumulative returns, weekly or daily, for the period selected. The next chart shows expected returns (average returns) vs standard deviation (volatility) for each ETF, the ASX200 index (AXJO) and the optimal portfolio.

Output2

The next set of charts show the optimal portfolio allocation weights and value.

 

Known Issues

There are some issues with the app that I have noticed when certain ETFs are selected perhaps because of cumulative returns that are lower than 0% or missing data points. For example AAA.AX or IAF.AX. If you get weird results or weights that do not sum up to 100% try to select a different set of ETFs.

Another issue affecting some Australian ETFs prevents the function tidyquant::tq_get from getting data before 18th of August 2016. Again, something in Yahoo Finance is broken.

I will address these issues in the future when I have some time.

Alternatively, if you feel like diving into the code and contributing, feel free to fork it on GitHub.

 

Leave a comment