Skip to content

Commit

Permalink
Merge pull request #128 from pshevtsov/candlestick
Browse files Browse the repository at this point in the history
Candlestick plotter
  • Loading branch information
jjallaire authored Nov 1, 2016
2 parents b8a06aa + 6574bc3 commit de46fd7
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 0 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export(dyAnnotation)
export(dyAxis)
export(dyCSS)
export(dyCallbacks)
export(dyCandlestick)
export(dyEvent)
export(dyHighlight)
export(dyLegend)
Expand Down
37 changes: 37 additions & 0 deletions R/candlestick.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#' Candlestick plotter for dygraph chart
#'
#' Draw a candlestick chart.
#'
#' @param dygraph Dygraph to draw chart on
#'
#' @return Dygraph with specified candlestick plotter
#'
#' @examples
#' library(xts)
#' data(sample_matrix)
#' library(dygraphs)
#' dygraph(sample_matrix) %>%
#' dyCandlestick()
#'
#' @export
dyCandlestick <- function(dygraph) {
name <- "candlestickPlotter"
version <- "1.0"
path <- system.file("plugins/candlestick.js", package = "dygraphs")
path <- normalizePath(path)
plotterDependency <- htmlDependency(paste0("Dygraph.Plotters", name),
version,
src = dirname(path),
script = basename(path),
all_files = FALSE)

# add the plotter javascript to the dependencies
if (is.null(dygraph$dependencies)) {
dygraph$dependencies <- list()
}
dygraph$dependencies[[length(dygraph$dependencies) + 1]] <- plotterDependency

dygraph$x$plotter <- name

dygraph
}
30 changes: 30 additions & 0 deletions docs/gallery-candlestick.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: "Candlestick charts"
---

```{r, include=FALSE}
library(dygraphs)
```

You can create candlestick/OHLC charts with `dyCandlestick` function. For example:

```{r message=FALSE}
library(xts)
data(sample_matrix)
a <- tail(sample_matrix, n = 32)
dygraph(a) %>%
dyCandlestick()
```

Candlestick chart uses the first four data series to plot, the rest data series (if
any) are rendered with line plotter:

```{r message=FALSE}
library(xts)
data(sample_matrix)
a <- tail(sample_matrix, n = 32)
a <- cbind(a, apply(a[, 1:3], 1, mean))
colnames(a)[5] <- "Mean"
dygraph(a) %>%
dyCandlestick()
```
1 change: 1 addition & 0 deletions docs/include/before_body.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ <h4>dygraphs for R</h4>
<li><a href="gallery-event-lines.html">Events and Limits</a></li>
<li><a href="gallery-upper-lower-bars.html">Upper/Lower Bars</a></li>
<li><a href="gallery-plugins.html">Plugins</a></li>
<li><a href="gallery-candlestick.html">Candlestick Charts</a></li>
</ul>

</div>
Expand Down
5 changes: 5 additions & 0 deletions inst/htmlwidgets/dygraphs.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ HTMLWidgets.widget({
}
}
}

// custom plotter
if (x.plotter) {
attrs.plotter = Dygraph.Plotters[x.plotter];
}

// if there is no existing dygraph perform initialization
if (!dygraph) {
Expand Down
79 changes: 79 additions & 0 deletions inst/plugins/candlestick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* The Candle chart plotter is adapted from code written by
* Zhenlei Cai ([email protected])
* https://github.com/danvk/dygraphs/pull/141/files
*/

(function() {
"use strict";

function getPrices(sets) {
var prices = [];
var price;
for (var p = 0 ; p < sets[0].length; p++) {
price = {
open : sets[0][p].yval,
high : sets[1][p].yval,
low : sets[2][p].yval,
close : sets[3][p].yval,
openY : sets[0][p].y,
highY : sets[1][p].y,
lowY : sets[2][p].y,
closeY : sets[3][p].y
};
prices.push(price);
}
return prices;
}

function candlestickPlotter(e) {
if (e.seriesIndex > 3) {
Dygraph.Plotters.linePlotter(e);
return;
}
// This is the officially endorsed way to plot all the series at once.
if (e.seriesIndex !== 0) return;

var sets = e.allSeriesPoints.slice(0, 4); // Slice first four sets for candlestick chart
var prices = getPrices(sets);
var area = e.plotArea;
var ctx = e.drawingContext;
ctx.strokeStyle = '#202020';
ctx.lineWidth = 0.6;

var minBarWidth = 2;
var numBars = prices.length + 1; // To compensate the probably removed first "incomplete" bar
var barWidth = Math.round((area.w / numBars) / 2);
if (barWidth % 2 !== 0) {
barWidth++;
}
barWidth = Math.max(barWidth, minBarWidth);

var price;
for (var p = 0 ; p < prices.length; p++) {
ctx.beginPath();

price = prices[p];
var topY = area.h * price.highY + area.y;
var bottomY = area.h * price.lowY + area.y;
var centerX = Math.floor(area.x + sets[0][p].x * area.w) + 0.5; // crisper rendering
ctx.moveTo(centerX, topY);
ctx.lineTo(centerX, bottomY);
ctx.closePath();
ctx.stroke();
var bodyY;
if (price.open > price.close) {
ctx.fillStyle ='rgba(244,44,44,1.0)';
bodyY = area.h * price.openY + area.y;
}
else {
ctx.fillStyle ='rgba(44,244,44,1.0)';
bodyY = area.h * price.closeY + area.y;
}
var bodyHeight = area.h * Math.abs(price.openY - price.closeY);
ctx.fillRect(centerX - barWidth / 2, bodyY, barWidth, bodyHeight);
}
};
candlestickPlotter._getPrices = getPrices; // for testing
Dygraph.Plotters.candlestickPlotter = candlestickPlotter;
})();
12 changes: 12 additions & 0 deletions man/dyCandlestick.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit de46fd7

Please sign in to comment.