3 * Pure JavaScript plotting plugin using jQuery
8 * Copyright (c) 2009-2012 Chris Leonello
9 * jqPlot is currently available for use in all personal or commercial projects
10 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12 * choose the license that best suits your project and use it accordingly.
14 * Although not required, the author would appreciate an email letting him
15 * know of any substantial use of jqPlot. You can reach the author at:
16 * chris at jqplot dot com or see http://www.jqplot.com/info.php .
18 * If you are feeling kind and generous, consider supporting the project by
19 * making a donation at: http://www.jqplot.com/donate.php .
21 * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
25 * http://hexmen.com/blog/2007/03/printf-sprintf/
26 * http://hexmen.com/js/sprintf.js
27 * The author (Ash Searle) has placed this code in the public domain:
28 * "This code is unrestricted: you are free to use it however you like."
33 * Class: $.jqplot.OHLCRenderer
34 * jqPlot Plugin to draw Open Hi Low Close, Candlestick and Hi Low Close charts.
36 * To use this plugin, include the renderer js file in
39 * > <script type="text/javascript" src="plugins/jqplot.ohlcRenderer.js"></script>
41 * You will most likely want to use a date axis renderer
42 * for the x axis also, so include the date axis render js file also:
44 * > <script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.js"></script>
46 * Then you set the renderer in the series options on your plot:
48 * > series: [{renderer:$.jqplot.OHLCRenderer}]
50 * For OHLC and candlestick charts, data should be specified
53 * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...]
55 * If the data array has only 4 values per point instead of 5,
56 * the renderer will create a Hi Low Close chart instead. In that case,
57 * data should be supplied like:
59 * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...]
61 * To generate a candlestick chart instead of an OHLC chart,
62 * set the "candlestick" option to true:
64 * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}],
67 $.jqplot.OHLCRenderer = function(){
68 // subclass line renderer to make use of some of it's methods.
69 $.jqplot.LineRenderer.call(this);
71 // true to render chart as candleStick.
72 // Must have an open price, cannot be a hlc chart.
73 this.candleStick = false;
75 // length of the line in pixels indicating open and close price.
76 // Default will auto calculate based on plot width and
77 // number of points displayed.
78 this.tickLength = 'auto';
80 // width of the candlestick body in pixels. Default will auto calculate
81 // based on plot width and number of candlesticks displayed.
82 this.bodyWidth = 'auto';
84 // color of the open price tick mark. Default is series color.
85 this.openColor = null;
87 // color of the close price tick mark. Default is series color.
88 this.closeColor = null;
90 // color of the hi-lo line thorugh the candlestick body.
91 // Default is the series color.
92 this.wickColor = null;
94 // true to render an "up" day (close price greater than open price)
95 // with a filled candlestick body.
96 this.fillUpBody = false;
98 // true to render a "down" day (close price lower than open price)
99 // with a filled candlestick body.
100 this.fillDownBody = true;
102 // Color of candlestick body of an "up" day. Default is series color.
103 this.upBodyColor = null;
104 // prop: downBodyColor
105 // Color of candlestick body on a "down" day. Default is series color.
106 this.downBodyColor = null;
108 // true if is a hi-low-close chart (no open price).
109 // This is determined automatically from the series data.
112 // Width of the hi-low line and open/close ticks.
113 // Must be set in the rendererOptions for the series.
114 this.lineWidth = 1.5;
119 $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer();
120 $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer;
122 // called with scope of series.
123 $.jqplot.OHLCRenderer.prototype.init = function(options) {
124 options = options || {};
125 // lineWidth has to be set on the series, changes in renderer
126 // constructor have no effect. set the default here
127 // if no renderer option for lineWidth is specified.
128 this.lineWidth = options.lineWidth || 1.5;
129 $.jqplot.LineRenderer.prototype.init.call(this, options);
131 // set the yaxis data bounds here to account for hi and low values
132 var db = this._yaxis._dataBounds;
133 var d = this._plotData;
134 // if data points have less than 5 values, force a hlc chart.
135 if (d[0].length < 5) {
136 this.renderer.hlc = true;
138 for (var j=0; j<d.length; j++) {
139 if (d[j][2] < db.min || db.min == null) {
142 if (d[j][1] > db.max || db.max == null) {
148 for (var j=0; j<d.length; j++) {
149 if (d[j][3] < db.min || db.min == null) {
152 if (d[j][2] > db.max || db.max == null) {
160 // called within scope of series.
161 $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) {
163 var xmin = this._xaxis.min;
164 var xmax = this._xaxis.max;
165 // index of last value below range of plot.
167 // index of first value above range of plot.
168 var xmaxidx = d.length;
169 var xp = this._xaxis.series_u2p;
170 var yp = this._yaxis.series_u2p;
171 var i, prevColor, ops, b, h, w, a, points;
173 var r = this.renderer;
174 var opts = (options != undefined) ? options : {};
175 var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
176 var fill = (opts.fill != undefined) ? opts.fill : this.fill;
177 var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
178 r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth;
179 r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength;
182 var x, open, hi, low, close;
183 // need to get widths based on number of points shown,
184 // not on total number of points. Use the results
185 // to speed up drawing in next step.
186 for (var i=0; i<d.length; i++) {
187 if (d[i][0] < xmin) {
190 else if (d[i][0] < xmax) {
195 var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0];
196 var nvisiblePoints = xmaxidx - xminidx;
198 var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval, 10)) - this._xaxis.series_u2p(0));
202 var dinterval = dwidth / nvisiblePoints;
206 if (typeof(r.bodyWidth) == 'number') {
207 r._bodyWidth = r.bodyWidth;
210 r._bodyWidth = Math.min(20, dinterval/1.65);
214 if (typeof(r.tickLength) == 'number') {
215 r._tickLength = r.tickLength;
218 r._tickLength = Math.min(10, dinterval/3.5);
222 for (var i=xminidx; i<xmaxidx; i++) {
237 if (r.candleStick && !r.hlc) {
241 // determine if candle up or down
242 // up, remember grid coordinates increase downward
246 o.color = r.wickColor;
248 else if (r.downBodyColor) {
249 o.color = r.upBodyColor;
251 ops = $.extend(true, {}, opts, o);
252 r.shapeRenderer.draw(ctx, [[x, hi], [x, close]], ops);
253 r.shapeRenderer.draw(ctx, [[x, open], [x, low]], ops);
257 // if color specified, use it
263 w = w - this.lineWidth;
267 o.color = r.upBodyColor;
268 o.fillStyle = r.upBodyColor;
270 points = [a, b, w, h];
273 else if (close > open) {
276 o.color = r.wickColor;
278 else if (r.downBodyColor) {
279 o.color = r.downBodyColor;
281 ops = $.extend(true, {}, opts, o);
282 r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops);
283 r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops);
289 // if color specified, use it
290 if (r.fillDownBody) {
295 w = w - this.lineWidth;
298 if (r.downBodyColor) {
299 o.color = r.downBodyColor;
300 o.fillStyle = r.downBodyColor;
302 points = [a, b, w, h];
304 // even, open = close
308 o.color = r.wickColor;
310 ops = $.extend(true, {}, opts, o);
311 r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops);
314 o.strokeRect = false;
316 b = [x + w/2, close];
321 ops = $.extend(true, {}, opts, o);
322 r.shapeRenderer.draw(ctx, points, ops);
325 prevColor = opts.color;
327 opts.color = r.openColor;
331 r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts);
333 opts.color = prevColor;
336 opts.color = r.wickColor;
338 r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts);
339 opts.color = prevColor;
342 opts.color = r.closeColor;
344 r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts);
345 opts.color = prevColor;
353 $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) {
354 // This is a no-op, shadows drawn with lines.
357 // called with scope of plot.
358 $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) {
359 // provide some sensible highlighter options by default
360 // These aren't good for hlc, only for ohlc or candlestick
361 if (!options.highlighter) {
362 options.highlighter = {
366 formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'
371 //$.jqplot.preInitHooks.push($.jqplot.OHLCRenderer.checkOptions);