Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
braverock
GitHub Repository: braverock/portfolioanalytics
Path: blob/master/R/backtest.plot.R
1433 views
1
###############################################################################
2
# R (https://r-project.org/) Numeric Methods for Optimization of Portfolios
3
#
4
# Copyright (c) 2022-2032 Peter Carl, Yifu Kang, Xinran Zhao, Doug Martin
5
#
6
# This library is distributed under the terms of the GNU Public License (GPL)
7
# for full details see the file COPYING
8
#
9
# $Id$
10
#
11
###############################################################################
12
13
14
#' generate plots of the cumulative returns and drawdown for back-testing
15
#' @param R an xts, vector, matrix, data frame, timeSeries or zoo object of asset returns
16
#' @param log_return arithmetic return or log return, the default is arithmetic return
17
#' @param drawdown_on the plot will shadow the full time period of the maximum drawdown and recovery of the first portfolio.
18
#' Use number (e.g. 1, 2, 3) to indicate which portfolio drawdown interval you wish to track, or NULL to not shadow any period.
19
#' @param plotType "cumRet", "drawdown", or the default is both
20
#' @param main users can design title by providing a character of main
21
#' @param colorSet users can design the color by providing a vector of color
22
#' @param ltySet users can design lty by providing a vector of lty
23
#' @param lwdSet users can design lwd by providing a vector of lwd
24
#' @importFrom stats end
25
#' @importFrom grDevices col2rgb
26
#' @author Peter Carl, Xinran Zhao, Yifu Kang
27
#' @export backtest.plot
28
backtest.plot <- function(R, log_return = FALSE, drawdown_on = 1, plotType='both', main = NULL, colorSet=NULL, ltySet=NULL, lwdSet=NULL){
29
## Cumulative Returns
30
c.xts <- if (log_return) {
31
1 + cumsum(R)
32
} else {
33
cumprod(1+R)
34
}
35
n = dim(c.xts)[2] ## number of portfolio
36
37
## sort xts
38
legendOrder = order(c.xts[end(c.xts)], decreasing = TRUE)
39
40
## Drawdowns
41
d.xts <- PerformanceAnalytics::Drawdowns(R)
42
43
## get longest drawdown dates for xts object, which is the worst drawdown
44
if (is.null(drawdown_on)){
45
x <- c(index(R)[1], index(R[1]))
46
} else {
47
dt <- table.Drawdowns(R[, drawdown_on], top = 1)
48
if(is.na(dt$To) == TRUE){
49
dt$To = index(R)[dim(R)[1]]
50
}
51
dt2 <- t(dt[,c("From", "To")])
52
x <- as.vector(dt2[,NCOL(dt2)])
53
}
54
55
## style set
56
if (is.null(colorSet))
57
colorSet <- 1:n
58
if (is.null(ltySet))
59
ltySet <- rep(1, n)
60
if (is.null(lwdSet))
61
lwdSet <- rep(2, n)
62
63
# title of plot
64
if(!is.null(main))
65
main = paste(main, '\n')
66
if(plotType == 'drawdown'){
67
main = paste(main, "Drawdown", sep = "")
68
} else {
69
main = paste(main, "Cumulative Returns", sep = "")
70
}
71
72
## plots of return and drawdown
73
if (plotType == 'both'){
74
p <- xts::plot.xts(c.xts[,1], main = main,
75
grid.ticks.lwd=1, grid.ticks.on = "years",
76
labels.col="grey20", col = colorSet[1], lty = ltySet[1],
77
lwd = lwdSet[1], cex.axis=0.8, format.labels = "%b\n%Y",
78
ylim = c(min(c.xts), max(c.xts)))
79
p <- xts::addSeries(d.xts[,1], main="Drawdown", ylim = c(min(d.xts), 0),
80
col = colorSet[1], lty = ltySet[1], lwd = lwdSet[1])
81
if(n > 1){
82
for(i in 2:n){
83
p <- xts::addSeries(c.xts[,i], on=1, col = colorSet[i], lty = ltySet[i], lwd = lwdSet[i])
84
p <- xts::addSeries(d.xts[,i], on=2, col = colorSet[i], lty = ltySet[i], lwd = lwdSet[i])
85
}
86
}
87
p <- xts::addLegend("topleft", on = 1, legend.names = names(c.xts)[legendOrder], bty = "o",
88
box.col = "white", col = colorSet[legendOrder], lty = ltySet[legendOrder],
89
lwd = lwdSet[legendOrder],
90
bg=rgb(t(col2rgb("white")), alpha = 200, maxColorValue = 255))
91
92
## ylim panel
93
# ylim1 <- c(p$Env$ylim[[2]][1], p$Env$ylim[[2]][2])
94
# ylim2 <- c(p$Env$ylim[[4]][1], p$Env$ylim[[4]][2])
95
ylim1 <- p$Env$panels[[1]]$ylim
96
ylim2 <- p$Env$panels[[2]]$ylim
97
98
xy1 <- as.xts(matrix(rep(ylim1, length(x)),ncol=length(ylim1), byrow=TRUE),
99
order.by=as.Date(x))
100
xy2 <- as.xts(matrix(rep(ylim2, length(x)),ncol=length(ylim2), byrow=TRUE),
101
order.by=as.Date(x))
102
p <- xts::addPolygon(xy1, on=-1, col="lightgrey") # top panel
103
p <- xts::addPolygon(xy2, on=-2, col="lightgrey") # lower panel
104
}
105
106
## plot of returns
107
if (plotType == 'ret' || plotType == 'cumGrossRet' || plotType == 'cumRet' || plotType == 'cumret'){
108
p <- xts::plot.xts(c.xts[,1], main = main,
109
grid.ticks.lwd=1, grid.ticks.on = "years", cex.axis=0.8,
110
col = colorSet[1], lty = ltySet[1], lwd = lwdSet[1],
111
format.labels = "%b\n%Y", labels.col="grey20",
112
ylim = c(min(c.xts), max(c.xts)))
113
if(n > 1){
114
for(i in 2:n){
115
p <- xts::addSeries(c.xts[,i], on=1, col = colorSet[i], lty = ltySet[i], lwd = lwdSet[i])
116
}
117
}
118
p <- xts::addLegend("topleft", on = 1, legend.names = names(c.xts)[legendOrder], bty = "o",
119
box.col = "white", col = colorSet[legendOrder], lty = ltySet[legendOrder],
120
lwd = lwdSet[legendOrder],
121
bg=rgb(t(col2rgb("white")), alpha = 200, maxColorValue = 255))
122
123
## ylim panel
124
# ylim1 <- c(p$Env$ylim[[2]][1], p$Env$ylim[[2]][2])
125
ylim1 <- p$Env$panels[[1]]$ylim
126
127
xy1 <- as.xts(matrix(rep(ylim1, length(x)),ncol=length(ylim1), byrow=TRUE),
128
order.by=as.Date(x))
129
p <- xts::addPolygon(xy1, on=-1, col="lightgrey")
130
}
131
132
## plot of drawdown
133
if (plotType == 'drawdown'){
134
p <- xts::plot.xts(d.xts[,1], main=main,
135
grid.ticks.lwd=1, grid.ticks.on = "years", cex.axis=0.8,
136
col = colorSet[1], lty = ltySet[1], lwd = lwdSet[1],
137
format.labels = "%b\n%Y", labels.col="grey20",
138
ylim = c(min(d.xts), 0.1))
139
if(n > 1){
140
for(i in 2:n){
141
p <- xts::addSeries(d.xts[,i], on=1, col = colorSet[i], lty = ltySet[i], lwd = lwdSet[i])
142
}
143
}
144
p <- xts::addLegend("topleft", on = 1, legend.names = names(c.xts)[legendOrder], bty = "o",
145
box.col = "white", col = colorSet[legendOrder], lty = ltySet[legendOrder],
146
lwd = lwdSet[legendOrder],
147
bg=rgb(t(col2rgb("white")), alpha = 200, maxColorValue = 255))
148
149
## ylim panel
150
# ylim1 <- c(p$Env$ylim[[2]][1], p$Env$ylim[[2]][2])
151
ylim1 <- p$Env$panels[[1]]$ylim
152
153
xy1 <- as.xts(matrix(rep(ylim1, length(x)),ncol=length(ylim1), byrow=TRUE),
154
order.by=as.Date(x))
155
p <- xts::addPolygon(xy1, on=-1, col="lightgrey") # top panel
156
}
157
158
return(p)
159
}
160
161