Creating a Table of Monthly Returns With R and a Volatility Trading Interview
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
This post will cover two aspects: the first will be a function to convert daily returns into a table of monthly returns, complete with drawdowns and annual returns. The second will be an interview I had with David Lincoln (now on youtube) to talk about the events of Feb. 5, 2018, and my philosophy on volatility trading.
So, to start off with, a function that I wrote that’s supposed to mimic PerforamnceAnalytics’s table.CalendarReturns is below. What table.CalendarReturns is supposed to do is to create a month X year table of monthly returns with months across and years down. However, it never seemed to give me the output I was expecting, so I went and wrote another function.
Here’s the code for the function:
require(data.table)
require(PerformanceAnalytics)
require(scales)
require(Quandl)
# helper functions
pastePerc <- function(x) {return(paste0(comma(x),"%"))}
rowGsub <- function(x) {x <- gsub("NA%", "NA", x);x}
calendarReturnTable <- function(rets, digits = 3, percent = FALSE) {
# get maximum drawdown using daily returns
dds <- apply.yearly(rets, maxDrawdown)
# get monthly returns
rets <- apply.monthly(rets, Return.cumulative)
# convert to data frame with year, month, and monthly return value
dfRets <- cbind(year(index(rets)), month(index(rets)), coredata(rets))
# convert to data table and reshape into year x month table
dfRets <- data.frame(dfRets)
colnames(dfRets) <- c("Year", "Month", "Value")
monthNames <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
for(i in 1:length(monthNames)) {
dfRets$Month[dfRets$Month==i] <- monthNames[i]
}
dfRets <- data.table(dfRets)
dfRets <- data.table::dcast(dfRets, Year~Month)
# create row names and rearrange table in month order
dfRets <- data.frame(dfRets)
yearNames <- dfRets$Year
rownames(dfRets) <- yearNames; dfRets$Year <- NULL
dfRets <- dfRets[,monthNames]
# append yearly returns and drawdowns
yearlyRets <- apply.yearly(rets, Return.cumulative)
dfRets$Annual <- yearlyRets
dfRets$DD <- dds
# convert to percentage
if(percent) {
dfRets <- dfRets * 100
}
# round for formatting
dfRets <- apply(dfRets, 2, round, digits)
# paste the percentage sign
if(percent) {
dfRets <- apply(dfRets, 2, pastePerc)
dfRets <- apply(dfRets, 2, rowGsub)
dfRets <- data.frame(dfRets)
rownames(dfRets) <- yearNames
}
return(dfRets)
}
Here’s how the output looks like.
spy <- Quandl("EOD/SPY", type='xts', start_date='1990-01-01')
spyRets <- Return.calculate(spy$Adj_Close)
calendarReturnTable(spyRets, percent = FALSE)
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual DD
1993 0.000 0.011 0.022 -0.026 0.027 0.004 -0.005 0.038 -0.007 0.020 -0.011 0.012 0.087 0.047
1994 0.035 -0.029 -0.042 0.011 0.016 -0.023 0.032 0.038 -0.025 0.028 -0.040 0.007 0.004 0.085
1995 0.034 0.041 0.028 0.030 0.040 0.020 0.032 0.004 0.042 -0.003 0.044 0.016 0.380 0.026
1996 0.036 0.003 0.017 0.011 0.023 0.009 -0.045 0.019 0.056 0.032 0.073 -0.024 0.225 0.076
1997 0.062 0.010 -0.044 0.063 0.063 0.041 0.079 -0.052 0.048 -0.025 0.039 0.019 0.335 0.112
1998 0.013 0.069 0.049 0.013 -0.021 0.043 -0.014 -0.141 0.064 0.081 0.056 0.065 0.287 0.190
1999 0.035 -0.032 0.042 0.038 -0.023 0.055 -0.031 -0.005 -0.022 0.064 0.017 0.057 0.204 0.117
2000 -0.050 -0.015 0.097 -0.035 -0.016 0.020 -0.016 0.065 -0.055 -0.005 -0.075 -0.005 -0.097 0.171
2001 0.044 -0.095 -0.056 0.085 -0.006 -0.024 -0.010 -0.059 -0.082 0.013 0.078 0.006 -0.118 0.288
2002 -0.010 -0.018 0.033 -0.058 -0.006 -0.074 -0.079 0.007 -0.105 0.082 0.062 -0.057 -0.216 0.330
2003 -0.025 -0.013 0.002 0.085 0.055 0.011 0.018 0.021 -0.011 0.054 0.011 0.050 0.282 0.137
2004 0.020 0.014 -0.013 -0.019 0.017 0.018 -0.032 0.002 0.010 0.013 0.045 0.030 0.107 0.075
2005 -0.022 0.021 -0.018 -0.019 0.032 0.002 0.038 -0.009 0.008 -0.024 0.044 -0.002 0.048 0.070
2006 0.024 0.006 0.017 0.013 -0.030 0.003 0.004 0.022 0.027 0.032 0.020 0.013 0.158 0.076
2007 0.015 -0.020 0.012 0.044 0.034 -0.015 -0.031 0.013 0.039 0.014 -0.039 -0.011 0.051 0.099
2008 -0.060 -0.026 -0.009 0.048 0.015 -0.084 -0.009 0.015 -0.094 -0.165 -0.070 0.010 -0.368 0.476
2009 -0.082 -0.107 0.083 0.099 0.058 -0.001 0.075 0.037 0.035 -0.019 0.062 0.019 0.264 0.271
2010 -0.036 0.031 0.061 0.015 -0.079 -0.052 0.068 -0.045 0.090 0.038 0.000 0.067 0.151 0.157
2011 0.023 0.035 0.000 0.029 -0.011 -0.017 -0.020 -0.055 -0.069 0.109 -0.004 0.010 0.019 0.186
2012 0.046 0.043 0.032 -0.007 -0.060 0.041 0.012 0.025 0.025 -0.018 0.006 0.009 0.160 0.097
2013 0.051 0.013 0.038 0.019 0.024 -0.013 0.052 -0.030 0.032 0.046 0.030 0.026 0.323 0.056
2014 -0.035 0.046 0.008 0.007 0.023 0.021 -0.013 0.039 -0.014 0.024 0.027 -0.003 0.135 0.073
2015 -0.030 0.056 -0.016 0.010 0.013 -0.020 0.023 -0.061 -0.025 0.085 0.004 -0.017 0.013 0.119
2016 -0.050 -0.001 0.067 0.004 0.017 0.003 0.036 0.001 0.000 -0.017 0.037 0.020 0.120 0.103
2017 0.018 0.039 0.001 0.010 0.014 0.006 0.021 0.003 0.020 0.024 0.031 0.012 0.217 0.026
2018 0.056 -0.031 NA NA NA NA NA NA NA NA NA NA 0.023 0.101
And with percentage formatting:
calendarReturnTable(spyRets, percent = TRUE)
Using 'Value' as value column. Use 'value.var' to override
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Annual DD
1993 0.000% 1.067% 2.241% -2.559% 2.697% 0.367% -0.486% 3.833% -0.726% 1.973% -1.067% 1.224% 8.713% 4.674%
1994 3.488% -2.916% -4.190% 1.121% 1.594% -2.288% 3.233% 3.812% -2.521% 2.843% -3.982% 0.724% 0.402% 8.537%
1995 3.361% 4.081% 2.784% 2.962% 3.967% 2.021% 3.217% 0.445% 4.238% -0.294% 4.448% 1.573% 38.046% 2.595%
1996 3.558% 0.319% 1.722% 1.087% 2.270% 0.878% -4.494% 1.926% 5.585% 3.233% 7.300% -2.381% 22.489% 7.629%
1997 6.179% 0.957% -4.414% 6.260% 6.321% 4.112% 7.926% -5.180% 4.808% -2.450% 3.870% 1.910% 33.478% 11.203%
1998 1.288% 6.929% 4.876% 1.279% -2.077% 4.259% -1.351% -14.118% 6.362% 8.108% 5.568% 6.541% 28.688% 19.030%
1999 3.523% -3.207% 4.151% 3.797% -2.287% 5.538% -3.102% -0.518% -2.237% 6.408% 1.665% 5.709% 20.388% 11.699%
2000 -4.979% -1.523% 9.690% -3.512% -1.572% 1.970% -1.570% 6.534% -5.481% -0.468% -7.465% -0.516% -9.730% 17.120%
2001 4.446% -9.539% -5.599% 8.544% -0.561% -2.383% -1.020% -5.933% -8.159% 1.302% 7.798% 0.562% -11.752% 28.808%
2002 -0.980% -1.794% 3.324% -5.816% -0.593% -7.376% -7.882% 0.680% -10.485% 8.228% 6.168% -5.663% -21.588% 32.968%
2003 -2.459% -1.348% 0.206% 8.461% 5.484% 1.066% 1.803% 2.063% -1.089% 5.353% 1.092% 5.033% 28.176% 13.725%
2004 1.977% 1.357% -1.320% -1.892% 1.712% 1.849% -3.222% 0.244% 1.002% 1.288% 4.451% 3.015% 10.704% 7.526%
2005 -2.242% 2.090% -1.828% -1.874% 3.222% 0.150% 3.826% -0.937% 0.800% -2.365% 4.395% -0.190% 4.827% 6.956%
2006 2.401% 0.573% 1.650% 1.263% -3.012% 0.264% 0.448% 2.182% 2.699% 3.152% 1.989% 1.337% 15.847% 7.593%
2007 1.504% -1.962% 1.160% 4.430% 3.392% -1.464% -3.131% 1.283% 3.870% 1.357% -3.873% -1.133% 5.136% 9.925%
2008 -6.046% -2.584% -0.903% 4.766% 1.512% -8.350% -0.899% 1.545% -9.437% -16.519% -6.961% 0.983% -36.807% 47.592%
2009 -8.211% -10.745% 8.348% 9.935% 5.845% -0.068% 7.461% 3.694% 3.545% -1.923% 6.161% 1.907% 26.364% 27.132%
2010 -3.634% 3.119% 6.090% 1.547% -7.945% -5.175% 6.830% -4.498% 8.955% 3.820% 0.000% 6.685% 15.057% 15.700%
2011 2.330% 3.474% 0.010% 2.896% -1.121% -1.688% -2.000% -5.498% -6.945% 10.915% -0.406% 1.044% 1.888% 18.609%
2012 4.637% 4.341% 3.216% -0.668% -6.006% 4.053% 1.183% 2.505% 2.535% -1.820% 0.566% 0.900% 15.991% 9.687%
2013 5.119% 1.276% 3.798% 1.921% 2.361% -1.336% 5.168% -2.999% 3.168% 4.631% 2.964% 2.589% 32.307% 5.552%
2014 -3.525% 4.552% 0.831% 0.695% 2.321% 2.064% -1.344% 3.946% -1.379% 2.355% 2.747% -0.256% 13.462% 7.273%
2015 -2.963% 5.620% -1.574% 0.983% 1.286% -2.029% 2.259% -6.095% -2.543% 8.506% 0.366% -1.718% 1.252% 11.910%
2016 -4.979% -0.083% 6.724% 0.394% 1.701% 0.350% 3.647% 0.120% 0.008% -1.734% 3.684% 2.028% 12.001% 10.306%
2017 1.789% 3.929% 0.126% 0.993% 1.411% 0.637% 2.055% 0.292% 2.014% 2.356% 3.057% 1.209% 21.700% 2.609%
2018 5.636% -3.118% NA NA NA NA NA NA NA NA NA NA 2.342% 10.102%
That covers it for the function. Now, onto volatility trading. Dodging the February short volatility meltdown has, in my opinion, been one of the best out-of-sample validators for my volatility trading research. My subscriber numbers confirm it, as I’ve received 12 new subscribers this month, as individuals interested in the volatility trading space have gained a newfound respect for the risk management that my system uses. After all, it’s the down months that vindicate system traders like myself that do not employ leverage in the up times. Those interested in following my trades can subscribe here. Furthermore, recently, I was able to get a chance to speak with David Lincoln about my background, and philosophy on trading in general, and trading volatility in particular. Those interested can view the interview here.
Thanks for reading.
NOTE: I am currently interested in networking, full-time positions related to my skill set, and long-term consulting projects. Those interested in discussing professional opportunities can find me on LinkedIn after writing a note expressing their interest.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.