4

I am studying ggplot in R and am trying to create a stock chart. I have already been able to create a candlestick chart, now I want to make a bar chart. This consists of a vertical line from the low price to the high price. Then, on the left of the line is tick for the opening price and on the right is a tick for the closing price. I don't know how to add this tick. The closest thing I can think of is geom_errorbar, but that is something else entirely and the whiskers go both ways.

Also, since this is daily data, ggplot is leaving space for the weekends which is not necessary. Is there a way to remove those spaces? I don't see it in any of the axis formatting articles I've been searching.

Thank you.!

FOSL chart

Reproducible code to create the chart with the low-high bar.

library(ggplot2)
library(quantmod)
FOSL <- getSymbols("FOSL", from="2015-01-01", auto.assign=FALSE)
names(FOSL) <- gsub("^.+\\.","",names(FOSL))  # remove "FOSL." from column names
FOSL <- data.frame(Date=as.Date(index(FOSL)), FOSL[,1:4])

ggplot(FOSL, aes(x=Date))+
  geom_linerange(aes(ymin=Low, ymax=High)) +
  labs(title="FOSL")
mks212
  • 901
  • 1
  • 18
  • 40

1 Answers1

1
library(ggplot2)
library(quantmod)
FOSL <- getSymbols("FOSL", from="2015-01-01", auto.assign=FALSE)
names(FOSL) <- gsub("^.+\\.","",names(FOSL))  # remove "FOSL." from column names

rng <- "2015-08"
FOSL <- FOSL[rng]
FOSL <- data.frame(Date=as.POSIXct(index(FOSL)), FOSL[,1:4])

FOSL$chg <- ifelse(Cl(FOSL) > Op(FOSL), "up", "dn")
FOSL$width <- as.numeric(periodicity(FOSL)[1])

# Bar chart:

ggplot(data=FOSL, aes(x=Date, colour = chg)) +
  theme_bw() +
  geom_linerange(aes(ymin=Low, ymax=High)) +
  geom_segment(aes(y = Open, yend = Open, xend = Date - width / 2 )) +
  geom_segment(aes(y = Close, yend = Close, xend = Date + width / 2)) +
  scale_colour_manual(values = c("dn" = "darkred", "up" = "darkgreen")) + guides(colour = FALSE)

enter image description here

The space on the weekends can't be removed easily for ggplot graphs with x axis scales like scale_x_datetime. This is because ggplot interprets POSIXct and date data as numbers from an origin on the x-axis. e.g. as.numeric(as.POSIXct("2015-09-25")) would be the value on the x axis in the ggplot. For "hacks" around this, you can check this SO answer, where dates are treated as factors:

R + ggplot2: how to hide missing dates from x-axis?

**Edit 2016-05, removing weekend gaps from ggplot time series plots **

Here is a working example for drawing ggplot OHLC bars that removes weekends (this function is easy to modify for drawing candles instead of bars example here).

library(ggplot2)
library(quantmod)
library(lubridate)
# prepare data:
md <- getSymbols("FOSL", from="2015-01-01", auto.assign=FALSE)
names(md) <- gsub("^.+\\.","",names(md))  # remove "md." from column names
rng <- "2015-08"
md <- md[rng]
# Use ceiling date to ensure "Date" is rounded to 00:00:00 in POSIXct numeric scale from 1970, not Date scale.
md <- data.frame(Date = ceiling_date(as.POSIXct(index(md)), "day"), md[,1:4])
md$time_idx <- 1:NROW(md)



# Check special case of drawing of flat bars:
md[10, 2:5] <- md[10, 5]

gg_build_candlechart_without_weekends <- function(md,  # in data.frame format, with a Date column
                                               x_scale = 0.8,   # Controls thickness of bars
                                               width = 1
)
{
    stopifnot("Date" %in% colnames(md))
    md$chg <- ifelse(Cl(md) > Op(md), "up", "dn")
    md$time_idx <- 1:NROW(md)
    md$width <- width

    pl <- ggplot(md, aes(x = time_idx, colour = chg)) +
        geom_linerange(aes(ymin=Low, ymax=High)) +
        geom_segment(aes(y = Open, yend = Open, xend = time_idx - width / 2 * x_scale)) +
        geom_segment(aes(y = Close, yend = Close, xend = time_idx + width / 2 * x_scale)) +
        scale_colour_manual(values = c("dn" = "darkred", "up" = "darkgreen")) + guides(colour = FALSE) + labs(x = "Time", y = "Price")

    Nr <- NROW(md)
    maj_breaks <- round(Nr * c(0.05, 0.25, 0.5, 0.75, .95))

    periodicity <- quantile(diff(as.numeric(md$Date)), 0.5)
    if (periodicity < 86400) {
        label_timestamp <- "%b %d %H:%M:%S"
    } else {
        label_timestamp <- "%y' %b %d"
    }


    pl <- pl + scale_x_continuous(limits = c(0, Nr + 1), expand = c(0, 0), breaks = maj_breaks, labels = format(md$Date[maj_breaks], label_timestamp), minor_breaks = Nr * seq(0.1, 0.9, by = 0.2))

    # draw flat bars as special case:
    md_flatbars <- md[md$High == md$Low, ]
    if (NROW(md_flatbars) > 0) {
        pl <- pl + geom_segment(data = md_flatbars, aes(x = time_idx - width / 2 * x_scale, y = Close, yend = Close, xend = time_idx + width / 2 * x_scale))
    }

    pl
}

gg_build_candlechart_without_weekends(md, x_scale = 0.8, width = 1)

No weekend gaps

Community
  • 1
  • 1
FXQuantTrader
  • 6,821
  • 3
  • 36
  • 67