From a26d5880c0574312bb50549f9169d6fbbc7064f8 Mon Sep 17 00:00:00 2001 From: Eric Hung Date: Sat, 30 Jul 2016 17:49:53 +0800 Subject: [PATCH 1/4] Refactor reChart to handle new chartSeries plot_object$subset doesn't work for plot.xts based object. The issue occurs in xts:::chart.lines where it calls "lines" to create original series for x by plot_object$Env$xycoords$x and the subset series for y. So it returns an error: "'x' and 'y' lengths differ." See issue #146. --- R/reChart.R | 119 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 42 deletions(-) diff --git a/R/reChart.R b/R/reChart.R index d72f3e00..1e8adfc3 100644 --- a/R/reChart.R +++ b/R/reChart.R @@ -7,15 +7,15 @@ function (type = c("auto", "candlesticks", "matchsticks", yrange=NULL, up.col, dn.col, color.vol = TRUE, multi.col = FALSE, ...) { - chob <- get.current.chob() + chob <- current.chob() #sys.TZ <- Sys.getenv('TZ') #Sys.setenv(TZ='GMT') #on.exit(Sys.setenv(TZ=sys.TZ)) - x <- chob@xdata + x <- chob$Env$xdata ########### name ########### - if(!missing(name)) chob@name <- name + if(!missing(name)) chob$Env$main <- name ########### end name ########### ########### type ########### @@ -38,9 +38,9 @@ function (type = c("auto", "candlesticks", "matchsticks", width <- 3 if(NROW(x) > 60) width <- 1 } - chob@spacing <- spacing - chob@width <- width - chob@type <- chart[1] +# chob@spacing <- spacing + chob$Env$theme$width <- width + chob$Env$range.bars.type <- chart[1] } ########### end type ########### @@ -65,39 +65,73 @@ function (type = c("auto", "candlesticks", "matchsticks", else xsubset <- 1:NROW(x) if(!is.null(subset)) { - chob@xsubset <- xsubset + chob$Env$xsubset <- xsubset x <- x[xsubset,] - chob@xrange <- c(1, NROW(x)) + xlim <- c(1, NROW(x)) + chob$set_xlim(c(xlim[1]-xlim[2]*0.04,xlim[2]+xlim[2]*0.04)) if (is.OHLC(x)) { - chob@yrange <- c(min(Lo(x), na.rm = TRUE), max(Hi(x), - na.rm = TRUE)) + chob$Env$ylim[[2]] <- structure(c(min(Lo(x), na.rm = TRUE), max(Hi(x), + na.rm = TRUE)), fixed = TRUE) } - else chob@yrange <- range(x[, 1], na.rm = TRUE) - if(!is.null(yrange) && length(yrange)==2) chob@yrange <- yrange + else chob$Env$ylim[[2]] <- structure(range(x[, 1], na.rm = TRUE), fixed = TRUE) + if(!is.null(yrange) && length(yrange)==2) chob$Env$ylim[[2]] <- structure(yrangea, fixed = TRUE) } - chob@xsubset <- xsubset + chob$Env$xsubset <- xsubset if(missing(major.ticks)) { - majorticks <- chob@major.ticks + majorticks <- chob$Env$major.ticks } else majorticks <- major.ticks - chob@bp <- axTicksByTime(x,majorticks) - chob@x.labels <- names(chob@bp) - chob@length <- NROW(x) + chob$Env$bp <- axTicksByTime(x,majorticks) +# chob$Env$x.labels <- names(chob$Env$bp) + chob$Env$length <- NROW(x) } ########### end subset ########## if(!missing(major.ticks)) { - chob@bp <- axTicksByTime(x[chob@xsubset],major.ticks) - chob@x.labels <- names(chob@bp) - chob@major.ticks <- major.ticks + chob$Env$bp <- axTicksByTime(chob$Env$xdata[chob$Env$xsubset],major.ticks) +# chob@x.labels <- names(chob@bp) + chob$Env$major.ticks <- major.ticks } if(!missing(minor.ticks)) - chob@minor.ticks = minor.ticks + chob$Env$minor.ticks <- minor.ticks ########### chartTheme ########## if(!missing(theme)) { if(inherits(theme,'chart.theme')) { - chob@colors <- theme - } else chob@colors <- chartTheme(theme) + theme <- theme + } else theme <- chartTheme(theme) + + chob$Env$theme$bg <- theme$bg.col + chob$Env$theme$fg <- theme$fg.col + chob$Env$theme$labels <- theme$major.tick + # deprecated arguments(? + chob$Env$theme$border <- theme$border + #chob$Env$theme$minor.tick + #chob$Env$theme$main.color + #chob$Env$theme$sub.col + chob$Env$theme$fill <- theme$area + + chob$Env$color.vol <- color.vol + chob$Env$multi.col <- multi.col + chob$Env$show.vol <- show.vol + chob$Env$bar.type <- bar.type + chob$Env$line.type <- line.type + #chob$Env$theme$spacing <- spacing + chob$Env$theme$Expiry <- theme$Expiry + chob$Env$theme$width <- width + chob$Env$layout <- layout + chob$Env$minor.ticks <- minor.ticks + chob$Env$major.ticks <- major.ticks + if(!show.grid){ + chob$Env$theme$grid <- NULL + chob$Env$theme$grid2 <- NULL + } else { + chob$Env$theme$grid <- theme$grid.col + chob$Env$theme$grid2 <- theme$grid.col + } + + chob$Env$theme$bbands$col$fill <- theme$BBands.fill + chob$Env$theme$bbands$col$upper <- theme$BBands.col + chob$Env$theme$bbands$col$lower <- theme$BBands.col } ########### end chartTheme ########## @@ -105,8 +139,8 @@ function (type = c("auto", "candlesticks", "matchsticks", if(missing(theme) & !missing(multi.col) ) stop(paste(sQuote('theme'),'must be specified in conjunction with', sQuote('multi.col'))) - theme <- chob@colors - if(missing(multi.col)) multi.col <- chob@multi.col + theme <- chob$Env$theme + if(missing(multi.col)) multi.col <- chob$Env$multi.col if(is.OHLC(x)) { Opens <- as.numeric(Op(x)) @@ -120,13 +154,13 @@ function (type = c("auto", "candlesticks", "matchsticks", type <- "line" color.vol <- FALSE } - if(has.Vo(x)) { - Volumes <- as.numeric(Vo(x)) + if(!is.null(chob$Env$vo)) { + Volumes <- chob$Env$vo[xsubset] show.vol <- TRUE } else show.vol <- FALSE if(missing(time.scale)) { - time.scale <- chob@time.scale + time.scale <- chob$Env$time.scale } if(!missing(up.col)) theme$up.col <- up.col @@ -148,20 +182,21 @@ function (type = c("auto", "candlesticks", "matchsticks", theme$dn.col <- theme$dn.dn.col multi.col <- TRUE } - chob@colors <- theme - chob@multi.col <- multi.col - chob@color.vol <- color.vol + # set bar color + chob$Env$theme$dn.up.col <- theme$dn.up.col + chob$Env$theme$up.up.col <- theme$up.up.col + chob$Env$theme$up.dn.col <- theme$up.dn.col + chob$Env$theme$dn.dn.col <- theme$dn.dn.col + + # set border color + chob$Env$theme$dn.up.border <- theme$dn.up.border + chob$Env$theme$up.up.border <- theme$up.up.border + chob$Env$theme$up.dn.border <- theme$up.dn.border + chob$Env$theme$dn.dn.border <- theme$dn.dn.border + + chob$Env$multi.col <- multi.col + chob$Env$color.vol <- color.vol ########### end multi.col ########## - chob@passed.args$TA <- sapply(chob@passed.args$TA, - function(x) eval(x@call) - ) - - chartSeries.chob(chob) - - chob@device <- as.numeric(dev.cur()) - - write.chob(chob,chob@device) - invisible(chob) - + chob } From 315c12968c7c34b0a95e76e7344ef6a31ba1514a Mon Sep 17 00:00:00 2001 From: Eric Hung Date: Tue, 2 Aug 2016 15:36:41 +0800 Subject: [PATCH 2/4] Add new chart.lines function to allow subset settings To allow "subset" to work, new chart.lines function is given to handle 'x' and 'y' length differ error occurred in lines(xx$Env$xycoords$x, ...). --- R/reChart.R | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/R/reChart.R b/R/reChart.R index 1e8adfc3..0e2a69d3 100644 --- a/R/reChart.R +++ b/R/reChart.R @@ -7,8 +7,41 @@ function (type = c("auto", "candlesticks", "matchsticks", yrange=NULL, up.col, dn.col, color.vol = TRUE, multi.col = FALSE, ...) { + # refactor xts:::chart.lines to make subset functionality work + chart.lines <- function (x, type = "l", lty = 1, lwd = 2, lend = 1, col = 1:10, + up.col = NULL, dn.col = NULL, legend.loc = NULL, ...) + { + if (is.null(up.col)) + up.col <- "green" + if (is.null(dn.col)) + dn.col <- "red" + xx <- xts:::current.xts_chob() + switch(type, h = { + colors <- ifelse(x[, 1] < 0, dn.col, up.col) + lines(xx$Env$xycoords$x[match(index(x), index(xx$Env$xdata))], x[, 1], lwd = 2, col = colors, + lend = lend, lty = 1, type = "h", ...) + }, p = , l = , b = , c = , o = , s = , S = , n = { + if (length(lty) < NCOL(x)) lty <- rep(lty, length.out = NCOL(x)) + if (length(lwd) < NCOL(x)) lwd <- rep(lwd, length.out = NCOL(x)) + if (length(col) < NCOL(x)) col <- rep(col, length.out = NCOL(x)) + for (i in NCOL(x):1) { + lines(xx$Env$xycoords$x[match(index(x), index(xx$Env$xdata))], x[, i], type = type, lend = lend, + col = col[i], lty = lty[i], lwd = lwd[i], ...) + } + }, { + warning(paste(type, "not recognized. Type must be one of\n 'p', 'l', 'b, 'c', 'o', 'h', 's', 'S', 'n'.\n plot.xts supports the same types as plot.default,\n see ?plot for valid arguments for type")) + }) + if (!is.null(legend.loc)) { + lc <- legend.coords(legend.loc, xx$Env$xlim, range(x, + na.rm = TRUE)) + legend(x = lc$x, y = lc$y, legend = colnames(x), xjust = lc$xjust, + yjust = lc$yjust, fill = col[1:NCOL(x)], bty = "n") + } + } + + chob <- current.chob() - + chob$Env$chart.lines <- chart.lines #sys.TZ <- Sys.getenv('TZ') #Sys.setenv(TZ='GMT') #on.exit(Sys.setenv(TZ=sys.TZ)) @@ -74,7 +107,7 @@ function (type = c("auto", "candlesticks", "matchsticks", na.rm = TRUE)), fixed = TRUE) } else chob$Env$ylim[[2]] <- structure(range(x[, 1], na.rm = TRUE), fixed = TRUE) - if(!is.null(yrange) && length(yrange)==2) chob$Env$ylim[[2]] <- structure(yrangea, fixed = TRUE) + if(!is.null(yrange) && length(yrange)==2) chob$Env$ylim[[2]] <- structure(yrange, fixed = TRUE) } chob$Env$xsubset <- xsubset @@ -85,6 +118,7 @@ function (type = c("auto", "candlesticks", "matchsticks", # chob$Env$x.labels <- names(chob$Env$bp) chob$Env$length <- NROW(x) } + xsubset <- chob$Env$xsubset ########### end subset ########## if(!missing(major.ticks)) { From 7af5a8716f1ed2a1df25a196a8915e96a7831b15 Mon Sep 17 00:00:00 2001 From: Eric Hung Date: Wed, 3 Aug 2016 16:26:08 +0800 Subject: [PATCH 3/4] Apply different bar types according to the length of series Different from zoom_Chart() for chart_Series, range.bars.type is specified only if type is given when reChart is called. If length of the series is greater than 300 range.bars.type will be "matchsticks" but, when zoomChart is called, even if the length of the subset series is lower than 300 range.bars.type doesn't change because users cannot specify type in zoomChart. Type of the bars should be able to change with the length of the subset series. --- R/reChart.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/reChart.R b/R/reChart.R index 0e2a69d3..46572895 100644 --- a/R/reChart.R +++ b/R/reChart.R @@ -52,11 +52,11 @@ function (type = c("auto", "candlesticks", "matchsticks", ########### end name ########### ########### type ########### - if(!missing(type)) { +# if(!missing(type)) { chart.options <- c("auto","candlesticks","matchsticks","line","bars") chart <- chart.options[pmatch(type,chart.options)] if(chart[1]=="auto") { - chart <- ifelse(NROW(x) > 300,"matchsticks","candlesticks") + chart <- ifelse(NROW(x[subset]) > 300,"matchsticks","candlesticks") } if(chart[1]=="candlesticks") { spacing <- 3 @@ -69,12 +69,12 @@ function (type = c("auto", "candlesticks", "matchsticks", if(chart[1]=="bars") { spacing <- 4 width <- 3 - if(NROW(x) > 60) width <- 1 + if(NROW(x[subset]) > 60) width <- 1 } # chob@spacing <- spacing chob$Env$theme$width <- width chob$Env$range.bars.type <- chart[1] - } +# } ########### end type ########### ########### subset ########## From 08beb81fa446094f09b40febc00b35b0e25dc9ee Mon Sep 17 00:00:00 2001 From: Eric Hung Date: Thu, 18 Aug 2016 17:23:30 +0800 Subject: [PATCH 4/4] Update warning of NAs introduced by coercion A warning occurred when we specify "last n months" to x[subset]. It throws warnings: NAs introduced by coercion. Since we consider the length of subset series to determine the range.bars type, handling for subset should be done before type. By doing so, subset is well-handled to numeric vector. Change bbands to BBands to coordinate with addBBands() function. --- R/reChart.R | 59 +++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/R/reChart.R b/R/reChart.R index 46572895..e5f46a9f 100644 --- a/R/reChart.R +++ b/R/reChart.R @@ -51,32 +51,6 @@ function (type = c("auto", "candlesticks", "matchsticks", if(!missing(name)) chob$Env$main <- name ########### end name ########### - ########### type ########### -# if(!missing(type)) { - chart.options <- c("auto","candlesticks","matchsticks","line","bars") - chart <- chart.options[pmatch(type,chart.options)] - if(chart[1]=="auto") { - chart <- ifelse(NROW(x[subset]) > 300,"matchsticks","candlesticks") - } - if(chart[1]=="candlesticks") { - spacing <- 3 - width <- 3 - } else - if(chart[1]=="matchsticks" || chart[1]=='line') { - spacing <- 1 - width <- 1 - } else - if(chart[1]=="bars") { - spacing <- 4 - width <- 3 - if(NROW(x[subset]) > 60) width <- 1 - } -# chob@spacing <- spacing - chob$Env$theme$width <- width - chob$Env$range.bars.type <- chart[1] -# } - ########### end type ########### - ########### subset ########## if(!missing(subset)) { if (!is.null(subset) & is.character(subset)) { @@ -120,6 +94,32 @@ function (type = c("auto", "candlesticks", "matchsticks", } xsubset <- chob$Env$xsubset ########### end subset ########## + + ########### type ########### + # if(!missing(type)) { + chart.options <- c("auto","candlesticks","matchsticks","line","bars") + chart <- chart.options[pmatch(type,chart.options)] + if(chart[1]=="auto") { + chart <- ifelse(NROW(x) > 300,"matchsticks","candlesticks") + } + if(chart[1]=="candlesticks") { + spacing <- 3 + width <- 3 + } else + if(chart[1]=="matchsticks" || chart[1]=='line') { + spacing <- 1 + width <- 1 + } else + if(chart[1]=="bars") { + spacing <- 4 + width <- 3 + if(NROW(x) > 60) width <- 1 + } + # chob@spacing <- spacing + chob$Env$theme$width <- width + chob$Env$range.bars.type <- chart[1] + # } + ########### end type ########### if(!missing(major.ticks)) { chob$Env$bp <- axTicksByTime(chob$Env$xdata[chob$Env$xsubset],major.ticks) @@ -163,9 +163,10 @@ function (type = c("auto", "candlesticks", "matchsticks", chob$Env$theme$grid2 <- theme$grid.col } - chob$Env$theme$bbands$col$fill <- theme$BBands.fill - chob$Env$theme$bbands$col$upper <- theme$BBands.col - chob$Env$theme$bbands$col$lower <- theme$BBands.col + chob$Env$theme$BBands$col$fill <- theme$BBands$col$fill + chob$Env$theme$BBands$col$upper <- theme$BBands$col$upper + chob$Env$theme$BBands$col$lower <- theme$BBands$col$lower + chob$Env$theme$BBands$col$ma <- theme$BBands$col$ma } ########### end chartTheme ##########