The stocks package has a variety of functions for analyzing investments and investment strategies. I use it for a lot of my articles on Seeking Alpha. The package relies heavily on Yahoo! Finance for historical prices and on the quantmod package for downloading that data into R.
There are functions for calculating performance metrics, backtesting trading strategies, and visualizing the performance of funds or strategies. I would say it is similar in spirit to the website Portfolio Visualizer and the R package PerformanceAnalytics.
Like most vignettes, the purpose here will be to illustrate typical usage of the functions in stocks. I’ll try to incorporate some interesting examples.
To start, here are the functions:
Function | Purpose |
---|---|
load_prices | Download and align historical prices for a set of tickers. |
load_gains | Download and align gains for a set of tickers. |
prices_rate | Calculate growth rate from a vector of prices. |
gains_rate | Calculate growth rate from a vector of gains. |
sharpe | Sharpe ratio. |
sortino | Sortino ratio. |
mdd | Maximum drawdown. |
rrr | Risk-return ratio. |
metrics | Calculate various performance metrics. |
twofunds_graph | Graph one performance metric vs. another for two-fund portfolios as allocation varies. |
threefunds_graph | Graph One performance metric vs. another for three-fund portfolios as allocation varies. |
onemetric_graph | Graph performance metric for various investments. |
onemetric_overtime_graph | Graph performance metric over time for various investments. |
twometrics_graph | Graph one performance metric vs. another for various investments. |
targetbeta_twofunds | Backtest a two-fund strategy that targets a certain beta. |
targetall | Backtest a fixed-allocation trading strategy. |
The functions load_prices and load_gains let you download historical data from Yahoo! Finance. They use quantmod’s getSymbols function internally. Here are some examples. (Note: R output is printed in nice tables rather than usual-looking R output because I loaded printr)
library("stocks")
library("printr")
prices <- load_prices(c("SPY", "TLT"), to = "2018-03-02")
head(prices)
SPY | TLT | |
---|---|---|
2002-07-30 | 66.85 | 45.54 |
2002-07-31 | 67.02 | 46.11 |
2002-08-01 | 65.27 | 46.37 |
2002-08-02 | 63.80 | 46.84 |
2002-08-05 | 61.58 | 47.05 |
2002-08-06 | 63.66 | 46.65 |
gains <- load_gains(c("SPY", "TLT"), to = "2018-03-02")
head(gains)
SPY | TLT | |
---|---|---|
2002-07-31 | 0.0024 | 0.0124 |
2002-08-01 | -0.0261 | 0.0057 |
2002-08-02 | -0.0224 | 0.0102 |
2002-08-05 | -0.0348 | 0.0044 |
2002-08-06 | 0.0337 | -0.0085 |
2002-08-07 | 0.0174 | 0.0024 |
In either case, data going as far back as possible for SPY and TLT (which happens to be mid-2002) are retrieved.
There are functions for calculating various performance metrics based on a vector of prices or gains, or a matrix where each column has prices or gains for a different fund. To illustrate a few, here’s net growth based on either data type:
prices_rate(prices)
#> SPY TLT
#> 3.025 1.599
gains_rate(gains)
#> SPY TLT
#> 3.025 1.599
Values of 3.025 and 1.599 mean SPY grew 302.5% and TLT 159.9%. For annualized rather than total growth:
prices_rate(prices, units.rate = 252)
#> SPY TLT
#> 0.09352 0.06324
gains_rate(gains, units.rate = 252)
#> SPY TLT
#> 0.09352 0.06324
Sharpe ratio and max drawdown:
sharpe(prices = prices)
#> SPY TLT
#> 0.03608 0.03271
sharpe(gains = gains)
#> SPY TLT
#> 0.03608 0.03271
mdd(prices = prices)
#> SPY TLT
#> 0.5519 0.2659
mdd(gains = gains)
#> SPY TLT
#> 0.5519 0.2659
Better yet, a single call to metrics produces a data frame with all of these metrics (and potentially others) and a correlation matrix for gains.
bond.metrics <- metrics(gains = gains, perf.metrics = c("growth", "cagr", "sharpe", "mdd"))
bond.metrics$perf.metrics
Growth | CAGR | Sharpe | MDD | |
---|---|---|---|---|
SPY | 3.025 | 0.0935 | 0.0361 | 0.5519 |
TLT | 1.599 | 0.0632 | 0.0327 | 0.2659 |
bond.metrics$cor.mat
SPY | TLT | |
---|---|---|
SPY | 1.0000 | -0.4066 |
TLT | -0.4066 | 1.0000 |
Looking at the performance metrics, we see that SPY achieved almost 2x the growth of TLT, but also had a max drawdown more than 2x as severe. A higher Sharpe ratio suggests better risk-adjusted returns for SPY.
Let’s look at the famous FANG stocks (Facebook, Amazon, Netflix, Google) for the past 3 years, with SPY (an S&P 500 ETF) included for reference.
fig <- growth_graph(c("SPY", "FB", "AMZN", "NFLX", "GOOG"), from = "2015-03-02",
to = "2018-03-02")
Notice that I specified a vector of ticker symbols, not a matrix of prices or gains. All of the graphing functions in stocks have the ability to download data on the fly.
Clearly the FANG stocks have continued with their remarkable growth over the past 3 years. I actually held Amazon and Google briefly, but sold them once I decided I couldn’t beat the market through stock-picking. I can’t, but in this case I wish I would have kept trying.
Before buying a fund, you should have a rough idea of how it tends to move with the market. The classic way of doing this is plotting the fund’s daily gains vs. a benchmark (typically the S&P) and looking at the estimated intercept (“alpha”) and slope (“beta”). For example, for Vanguard’s long-term bond fund VBLTX, over the past 5 years:
fig <- gains_graph(c("SPY", "VBLTX"), from = "2013-03-02", to = "2018-03-02")
Definitely a negative trend there, although a weak one: \(R^2 = 0.08\) means only 8% of variability in VBLTX gains are explained by the regression on SPY.
Looking at the regression equation, we see positive alpha and negative beta, suggesting VBLTX and SPY would complement each other nicely in a retirement portfolio.
To compare annualized growth for the 10 Select Sector SPDR ETFs over the past year:
data(sector_spdr_etfs)
fig <- onemetric_graph(tickers = sector_spdr_etfs$ticker,
from = "2017-03-02", to = "2018-03-02")
Notice that I loaded the 10 ticker symbols with data(sector_spdr_etfs)
. That worked because a dataset called sector_spdr_etfs
is included in stocks. Quite a few other ticker lists are also available (run data(package = "stocks")
to see them).
Looks like only 6 of the 10 sectors had positive growth over the past year; XLK (technology) was the best performer with 30.3% growth.
To simultaneousy look at annualized growth and maximum drawdown for the sector ETFs:
fig <- twometrics_graph(tickers = sector_spdr_etfs$ticker,
from = "2017-03-02", to = "2018-03-02",
x.metric = "mdd", y.metric = "cagr")
Here XLY (consumer discretionary) looks pretty good, with the best max drawdown and second-best growth.
Let’s move to another example: Warren Buffett’s Berkshire Hathaway (BRK-B). Let’s see whether BRK-B has consistently outperformed SPY over the past 15 years, looking at the 100-day moving Sharpe ratio:
fig <- onemetric_overtime_graph(tickers = c("SPY", "BRK-B"),
from = "2003-03-02", to = "2018-03-02",
window.units = 100, y.metric = "sharpe")
There are certainly some periods where SPy does a lot better, and some where BRK-B does a lot better. There’s no obvious winner here, visually. The actual 100-day Sharpe ratios are returned by onemetric_overtime_graph, so we can compare the medians and see what percent of the time BRK-B’s is higher:
apply(fig, 2, median)
#> SPY BRK-B
#> 0.07349 0.05481
mean(fig[, 2] > fig[, 1])
#> [1] 0.4331
BRK-B has a tendency for worse risk-adjusted returns than SPY - not good, Mr. Buffett!
Until recently, my own retirement portfolio was simply 1/3 UPRO, 2/3 VBLTX. UPRO is a 3x daily S&P 500 ETF, and VBLTX is Vanguard’s long-term bond mutual fund. A good way of visualizing the characteristics of a two-fund portfolio is to plot mean vs. standard deviation of daily gains, for allocations ranging from 100% fund 1 to 100% fund 2. To do that, using data over UPRO’s lifetime, going back to 6/25/09:
fig <- twofunds_graph(tickers = c("UPRO", "VBLTX"), reference.tickers = "SPY",
to = "2018-03-02")
The UPRO/VBLTX strategy sort of trumps SPY, because there are regions on the curve with higher returns and lower volatility than SPY.
For another example, we, could look at annualized growth vs. maximum drawdown rather than mean vs. standard deviation, and look at UPRO/VBLTX as well as UPRO/VBMFX, to see how swapping the long-term fund VBLTX with the total bond fund VBMFX would affect our portfolio.
fig <- twofunds_graph(tickers = c("UPRO", "VBLTX", "UPRO", "VBMFX"),
reference.tickers = "SPY",
to = "2018-03-02",
x.metric = "mdd", y.metric = "cagr")
UPRO/VBLTX clearly has much better performance. One more example, this time Sharpe ratio vs. allocation, for the same two funds plus UPRO/TLT:
fig <- twofunds_graph(tickers = c("UPRO", "VBLTX", "UPRO", "VBMFX", "UPRO", "TLT"),
reference.tickers = "SPY",
to = "2018-03-02",
x.metric = "mean", y.metric = "sharpe",
plot.list = list(ylim = c(0, 0.12)))
The rationale for this graph is that we might want to target the same level of expected returns as SPY, but with as high of a Sharpe ratio as possible. If you go straight up on the graph from the SPY data point, you hit the red curve first, then the orange, and finally the blue. So we would choose UPRO/VBLTX, with something like 75-80% allocated to VBLTX.
The change I made recently was adding Vanguard’s high-yield (aka “junk bond”) mutual fund, VWEHX, to the mix. To visualize characteristics of VWEHX/UPRO/VBLTX:
fig <- threefunds_graph(tickers = c("VWEHX", "UPRO", "VBLTX"),
reference.tickers = "SPY",
to = "2018-03-02")
Each curve is for a fixed allocation to the first fund. The black curve is 0% VWEHX, so it is the same as the UPRO/VBLTX curve in the previous graph. The red curve is 20% VWEHX, and the remaining 80% allocated entirely to VBLTX (bottom-most point), entirely to UPRO (top-right data point), and everywhere in between.
At SPY’s SD, the black curve (0% VWEHX) achieves the highest mean. At SPY’s mean, the blue (40% VWEHX) or red (20% VWEHX) curve has the lowest SD. So depending on what you’re going for, it may or may not be worthwhile adding VWEHX. My rationale was sort of qualitative: two nearly independent alpha-generating bond funds seems better than one.
Hopefully this gives you a good idea of the main functions in stocks. There are a lot of options I didn’t cover, especially for the graphics functions. You can read about these options by looking at the relevant help files (e.g. run ?twofunds_graph
in R). Please feel free to e-mail me at vandomed@gmail.com with questions or suggestions, or, better yet, contribute to the project on GitHub.
Peterson, Brian G., and Peter Carl. 2014. PerformanceAnalytics: Econometric Tools for Performance and Risk Analysis. https://CRAN.R-project.org/package=PerformanceAnalytics.
Ryan, Jeffrey A., and Joshua M. Ulrich. 2017. Quantmod: Quantitative Financial Modelling Framework. https://CRAN.R-project.org/package=quantmod.
Xie, Yihui. 2017. Printr: Automatically Print R Objects to Appropriate Formats According to the ’Knitr’ Output Format. https://CRAN.R-project.org/package=printr.