Visualization asset returns in the tidyverse and in xts
R Programming
Finance
Author
Yang Wu
Published
May 1, 2021
Visualization Asset Returns in XTS
In this post, we will explore some visualizations of asset returns. Similar to the previous post, we import our data from Yahoo Finance. The five assets under examination are Exchange Traded Funds, which are funds that can be traded on an exchange like stocks. Exchange-traded funds are a type of investment fund that offers the best attributes of two popular assets: they have the diversification benefits of mutual funds and the ease with which stocks are traded. We will take a sample of daily prices from 2012-12-31 to 2021-7-31, converting them to monthly returns. This leaves us with a sample of 103 monthly returns. If you are interested in exploring a different sample, you could expand or shorten the time horizon upon importing the data. The visualization methods in this post are agnostic to the underlying data; the interpretation of these plots, however, will be different.
The first step is to download the price series from Yahoo finance:
Show code
# Create a vector of ticker symbolssymbols <-c("SPY", "EFA", "DIA", "QQQ", "AGG")# Load data from 2012 to todayprices <-getSymbols(Symbols = symbols,src ="yahoo",from ="2012-12-31",to ="2021-7-31",auto.assign =TRUE,warnings =FALSE) %>%# The map function takes an anonymous function and will return a list of five# The Ad() function extracts the adjusted price series for each ETFmap(.f =~Ad(get(x = .x))) |># Use reduce() to merge the elements of .x consecutivelyreduce(.f = merge) |># Use a replacement function to set column names as ticker symbols# This function is in prefix form# It is equivalent to colnames(x = prices) <- value`colnames<-`(value = symbols)# Remove all objects but price series and ticker symbol vectorrm(list =setdiff(x =ls(), y =c("prices", "symbols")))
Since we will not be aggregating asset returns to compute portfolio returns, we choose log returns, i.e., the continuously compounded rate of returns, over the simple returns. Continuously compounded rate of returns should be used in statistical analysis (and visualizations) because unlike simple returns they are not positively biased. In addition, we opt to convert daily prices to monthly returns by finding the relative change of prices between the last day of each month. We could have easily chosen to use the first day of each month, and the values of the monthly returns will be different.
Show code
# Keep only the last price reading of each monthasset_returns_xts <-to.monthly(x = prices,drop.time =TRUE,indexAt ="lastof",OHLC =FALSE) |># Compute log returns PerformanceAnalytics::Return.calculate(method ="log") |># Drop the first row since we lose one observation in 12/31/2012na.omit()
Monthly Log Returns Highcharts
The highcharterpackage is a wrapper for the “Highcharts” Library, which has an amazing visualization infrastructure for time series and financial data. The highcharter package houses functions that accept xts objects (R’s time series object class) as arguments, making it seamless to move from time series data to visualizations. The plot below displays the line chart for a subset of the ETF’s. We could have easily plotted all five ETF’s on the same line chart, but it would be harder for our eyes to compare, contrast, and identify patterns.
Show code
# Crate Highstock widgethighchart(type ="stock") |># Add chart main titlehc_title(text ="Monthly Log Returns for SPY, QQQ, DIA") |># Add returns series to highchart objects# We use "symbols" to reference series since we may need to add/remove ETF's in the future# Use matrix sub-setting and character indexing to select returns by columnhc_add_series(data = asset_returns_xts[, symbols[[1]]],name = symbols[[1]] ) |>hc_add_series(data = asset_returns_xts[, symbols[[4]]],name = symbols[[4]] ) |>hc_add_series(data = asset_returns_xts[, symbols[[3]]],name = symbols[[3]] ) |># Add theme to highchart object# More themes to be found in the vignettehc_add_theme(hc_thm =hc_theme_flat()) |># Navigatorhc_navigator(enabled =TRUE) |># Scrollbarhc_scrollbar(enabled =TRUE) |># Exportinghc_exporting(enabled =TRUE) |># Add legendhc_legend(enabled =TRUE)
The navigator is a small series below the main series, displaying a view of the entire data set. It provides tools to zoom in and out on parts of the data as well as panning across the data-set.
The scroll-bar is a means of panning over the X axis of a stock chart. Scroll-bars can also be applied to other types of axes.
Note:Highcharter plots are highly interactive; that is, an user can hover over the line chart to interact with it. However, due to internal conflicts with blogdown themes, this Highcharter plot cannot be rendered dynamically. Please navigate to this link to view the interactive version of this line chart.
Monthly Log Returns Histograms
Create a function that returns a uni-variate histogram given a series of returns. The function will also take several other arguments— an xts object of returns, a vector of ticker symbols, a symbol index, and a color for plotting. Internally, the function creates a list of histogram components: counts, density, bin breaks, etc. Then, the function hchart() is called on the histogram list object to plot the uni-variate histogram; this is the final output of the function.
Show code
hc_hist_fun <-function(xts_obj, tickers, symbol_index, color) {# Check for invalid inputif (!is.xts(xts_obj) ||!is_character(color) ||!is_character(tickers)) {abort(message ="Invalid input type for xts_object, tickers, and/or color arguments" ) }# Create histogram list object with 6 elements hc_hist <-hist(xts_obj[, tickers[[symbol_index]]],breaks ="Freedman-Diaconis",plot =FALSE )# Call hchart on the histogram list objecthchart(object = hc_hist, color = color) |>hc_title(text =paste(tickers[[symbol_index]], "Log Returns Distribution", sep =" ") ) |>hc_add_theme(hc_thm =hc_theme_flat()) |>hc_exporting(enabled =TRUE) |>hc_legend(enabled =FALSE)}
Now, we utilize the functional programming tool from purrr to apply the function above to each of the five uni-variate returns series.
Show code
# Map the histogram function to each of the returns serieslist_of_histogram <-map(.x =1:5,.f =~hc_hist_fun(xts_obj = asset_returns_xts,tickers = symbols,symbol_index = .x,color ="cornflowerblue" ))
Note: Please navigate to this link to view the interactive version of these histograms.
As can be seen, most of our assets are negatively skewed, indicating that there were a few really bad months. For the iShares Core US Aggregate Bond ETF (AGG), most months have returns that may be statistically indistinguishable from zero. From a sheer numbers perspective, investors who wish to maximize gains may consider such an asset undesirable. However, other performance factors such as risk, diversifier effect, and time horizon are important. The iShares Core US Aggregate Bond ETF seeks to track the investment results of an index composed of the total U.S. investment-grade bond market. And we expect bonds to produce lower returns for investors because they are also considered less volatile than stocks.
The Invesco QQQ Trust Series 1 ETF (QQQ) stands out as a strong performing asset. Out of 103 months, about \(22%\) of its monthly returns fall between \(2\%\) and \(4\%\). Hover over the histograms here to see the counts and break points of the returns distribution.
Visualizations Using ggplot2
Similarly, we could plot our asset returns using ggplot2, which implements the layered grammar of graphics approach. For efficiency, we will convert the xts object into the long tidy format that the tidyverse functions are designed to work well with. For another method of data importation that automatically converts the data into a tidy format, please see this post.
Show code
asset_returns_dplyr <-to.monthly(x = prices,drop.time =TRUE,indexAt ="lastof",OHLC =FALSE) %>%# Create a new "date" variable by extracting the date indices from the xts objectdata.frame("date"=index(x = .)) |># Coerce to tibbleas_tibble() |># Create a key column "asset" that contains the column names, i.e. ticker symbols# Create a value column that contains all the cells associated with each column# We convert to long format since it is easier to compute the returns using lag()pivot_longer(cols =1:5,names_to ="asset",values_to ="returns" ) |># Group by ticker symbolgroup_by(asset) |># Compute log returns manuallymutate("returns"= (log(x = returns, base =exp(1)) -log(x =lag(x = returns), base =exp(1)) ) ) |># Remove NA_double_ readings for 12/31/2021na.omit()# See the resultshead(asset_returns_dplyr, 5)
Here are the histograms. Notice that we can either overlay the histograms on top of each other or show them in separate panels. I recommend using the panel approach for studying the shapes (spread, central tendency, skewness, tailed-ness, etc.) of the uni-variate distributions, and employ the overlaying histograms for making comparisons between these distributions.
We could also plot the probability density functions of these historical returns. Take a look at the y-axis of these plots and compare them to those of the histograms. This is an important distinction between these otherwise similar visualizations.
The smoothed densities produced by kernel density estimation (KDE) are valuable tools for visualizing the distribution of returns. These estimates provide insights into the relative likelihood of observing different ranges of returns. However, it is crucial to emphasize that the values on the y-axis in a density plot do not represent probabilities in a direct sense. Rather, they represent the probability density function (PDF) derived from the KDE.
For now, we have equipped ourselves with some nice visualization techniques in R. These are not the only ways to visualize financial data by any means. In future posts, I will explore other aspects of financial analytics and portfolio analytics.