You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

372 regels
15 KiB

  1. /**
  2. * jqPlot
  3. * Pure JavaScript plotting plugin using jQuery
  4. *
  5. * Version: 1.0.0b2_r792
  6. *
  7. * Copyright (c) 2009-2011 Chris Leonello
  8. * jqPlot is currently available for use in all personal or commercial projects
  9. * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
  10. * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
  11. * choose the license that best suits your project and use it accordingly.
  12. *
  13. * Although not required, the author would appreciate an email letting him
  14. * know of any substantial use of jqPlot. You can reach the author at:
  15. * chris at jqplot dot com or see http://www.jqplot.com/info.php .
  16. *
  17. * If you are feeling kind and generous, consider supporting the project by
  18. * making a donation at: http://www.jqplot.com/donate.php .
  19. *
  20. * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
  21. *
  22. * version 2007.04.27
  23. * author Ash Searle
  24. * http://hexmen.com/blog/2007/03/printf-sprintf/
  25. * http://hexmen.com/js/sprintf.js
  26. * The author (Ash Searle) has placed this code in the public domain:
  27. * "This code is unrestricted: you are free to use it however you like."
  28. *
  29. */
  30. (function($) {
  31. /**
  32. * Class: $.jqplot.OHLCRenderer
  33. * jqPlot Plugin to draw Open Hi Low Close, Candlestick and Hi Low Close charts.
  34. *
  35. * To use this plugin, include the renderer js file in
  36. * your source:
  37. *
  38. * > <script type="text/javascript" src="plugins/jqplot.ohlcRenderer.js"></script>
  39. *
  40. * You will most likely want to use a date axis renderer
  41. * for the x axis also, so include the date axis render js file also:
  42. *
  43. * > <script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.js"></script>
  44. *
  45. * Then you set the renderer in the series options on your plot:
  46. *
  47. * > series: [{renderer:$.jqplot.OHLCRenderer}]
  48. *
  49. * For OHLC and candlestick charts, data should be specified
  50. * like so:
  51. *
  52. * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...]
  53. *
  54. * If the data array has only 4 values per point instead of 5,
  55. * the renderer will create a Hi Low Close chart instead. In that case,
  56. * data should be supplied like:
  57. *
  58. * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...]
  59. *
  60. * To generate a candlestick chart instead of an OHLC chart,
  61. * set the "candlestick" option to true:
  62. *
  63. * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}],
  64. *
  65. */
  66. $.jqplot.OHLCRenderer = function(){
  67. // subclass line renderer to make use of some of it's methods.
  68. $.jqplot.LineRenderer.call(this);
  69. // prop: candleStick
  70. // true to render chart as candleStick.
  71. // Must have an open price, cannot be a hlc chart.
  72. this.candleStick = false;
  73. // prop: tickLength
  74. // length of the line in pixels indicating open and close price.
  75. // Default will auto calculate based on plot width and
  76. // number of points displayed.
  77. this.tickLength = 'auto';
  78. // prop: bodyWidth
  79. // width of the candlestick body in pixels. Default will auto calculate
  80. // based on plot width and number of candlesticks displayed.
  81. this.bodyWidth = 'auto';
  82. // prop: openColor
  83. // color of the open price tick mark. Default is series color.
  84. this.openColor = null;
  85. // prop: closeColor
  86. // color of the close price tick mark. Default is series color.
  87. this.closeColor = null;
  88. // prop: wickColor
  89. // color of the hi-lo line thorugh the candlestick body.
  90. // Default is the series color.
  91. this.wickColor = null;
  92. // prop: fillUpBody
  93. // true to render an "up" day (close price greater than open price)
  94. // with a filled candlestick body.
  95. this.fillUpBody = false;
  96. // prop: fillDownBody
  97. // true to render a "down" day (close price lower than open price)
  98. // with a filled candlestick body.
  99. this.fillDownBody = true;
  100. // prop: upBodyColor
  101. // Color of candlestick body of an "up" day. Default is series color.
  102. this.upBodyColor = null;
  103. // prop: downBodyColor
  104. // Color of candlestick body on a "down" day. Default is series color.
  105. this.downBodyColor = null;
  106. // prop: hlc
  107. // true if is a hi-low-close chart (no open price).
  108. // This is determined automatically from the series data.
  109. this.hlc = false;
  110. // prop: lineWidth
  111. // Width of the hi-low line and open/close ticks.
  112. // Must be set in the rendererOptions for the series.
  113. this.lineWidth = 1.5;
  114. this._tickLength;
  115. this._bodyWidth;
  116. };
  117. $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer();
  118. $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer;
  119. // called with scope of series.
  120. $.jqplot.OHLCRenderer.prototype.init = function(options) {
  121. options = options || {};
  122. // lineWidth has to be set on the series, changes in renderer
  123. // constructor have no effect. set the default here
  124. // if no renderer option for lineWidth is specified.
  125. this.lineWidth = options.lineWidth || 1.5;
  126. $.jqplot.LineRenderer.prototype.init.call(this, options);
  127. this._type = 'ohlc';
  128. // set the yaxis data bounds here to account for hi and low values
  129. var db = this._yaxis._dataBounds;
  130. var d = this._plotData;
  131. // if data points have less than 5 values, force a hlc chart.
  132. if (d[0].length < 5) {
  133. this.renderer.hlc = true;
  134. for (var j=0; j<d.length; j++) {
  135. if (d[j][2] < db.min || db.min == null) {
  136. db.min = d[j][2];
  137. }
  138. if (d[j][1] > db.max || db.max == null) {
  139. db.max = d[j][1];
  140. }
  141. }
  142. }
  143. else {
  144. for (var j=0; j<d.length; j++) {
  145. if (d[j][3] < db.min || db.min == null) {
  146. db.min = d[j][3];
  147. }
  148. if (d[j][2] > db.max || db.max == null) {
  149. db.max = d[j][2];
  150. }
  151. }
  152. }
  153. };
  154. // called within scope of series.
  155. $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) {
  156. var d = this.data;
  157. var xmin = this._xaxis.min;
  158. var xmax = this._xaxis.max;
  159. // index of last value below range of plot.
  160. var xminidx = 0;
  161. // index of first value above range of plot.
  162. var xmaxidx = d.length;
  163. var xp = this._xaxis.series_u2p;
  164. var yp = this._yaxis.series_u2p;
  165. var i, prevColor, ops, b, h, w, a, points;
  166. var o;
  167. var r = this.renderer;
  168. var opts = (options != undefined) ? options : {};
  169. var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
  170. var fill = (opts.fill != undefined) ? opts.fill : this.fill;
  171. var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
  172. r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth;
  173. r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength;
  174. ctx.save();
  175. if (this.show) {
  176. var x, open, hi, low, close;
  177. // need to get widths based on number of points shown,
  178. // not on total number of points. Use the results
  179. // to speed up drawing in next step.
  180. for (var i=0; i<d.length; i++) {
  181. if (d[i][0] < xmin) {
  182. xminidx = i;
  183. }
  184. else if (d[i][0] < xmax) {
  185. xmaxidx = i+1;
  186. }
  187. }
  188. var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0];
  189. var nvisiblePoints = xmaxidx - xminidx;
  190. try {
  191. var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval)) - this._xaxis.series_u2p(0));
  192. }
  193. catch (e) {
  194. var dinterval = dwidth / nvisiblePoints;
  195. }
  196. if (r.candleStick) {
  197. if (typeof(r.bodyWidth) == 'number') {
  198. r._bodyWidth = r.bodyWidth;
  199. }
  200. else {
  201. r._bodyWidth = Math.min(20, dinterval/1.75);
  202. }
  203. }
  204. else {
  205. if (typeof(r.tickLength) == 'number') {
  206. r._tickLength = r.tickLength;
  207. }
  208. else {
  209. r._tickLength = Math.min(10, dinterval/3.5);
  210. }
  211. }
  212. for (var i=xminidx; i<xmaxidx; i++) {
  213. x = xp(d[i][0]);
  214. if (r.hlc) {
  215. open = null;
  216. hi = yp(d[i][1]);
  217. low = yp(d[i][2]);
  218. close = yp(d[i][3]);
  219. }
  220. else {
  221. open = yp(d[i][1]);
  222. hi = yp(d[i][2]);
  223. low = yp(d[i][3]);
  224. close = yp(d[i][4]);
  225. }
  226. o = {};
  227. if (r.candleStick && !r.hlc) {
  228. w = r._bodyWidth;
  229. a = x - w/2;
  230. // draw candle
  231. // determine if candle up or down
  232. // up, remember grid coordinates increase downward
  233. if (close < open) {
  234. // draw wick
  235. if (r.wickColor) {
  236. o.color = r.wickColor;
  237. }
  238. else if (r.downBodyColor) {
  239. o.color = r.upBodyColor;
  240. }
  241. ops = $.extend(true, {}, opts, o);
  242. r.shapeRenderer.draw(ctx, [[x, hi], [x, close]], ops);
  243. r.shapeRenderer.draw(ctx, [[x, open], [x, low]], ops);
  244. o = {};
  245. b = close;
  246. h = open - close;
  247. // if color specified, use it
  248. if (r.fillUpBody) {
  249. o.fillRect = true;
  250. }
  251. else {
  252. o.strokeRect = true;
  253. w = w - this.lineWidth;
  254. a = x - w/2;
  255. }
  256. if (r.upBodyColor) {
  257. o.color = r.upBodyColor;
  258. o.fillStyle = r.upBodyColor;
  259. }
  260. points = [a, b, w, h];
  261. }
  262. // down
  263. else if (close > open) {
  264. // draw wick
  265. if (r.wickColor) {
  266. o.color = r.wickColor;
  267. }
  268. else if (r.downBodyColor) {
  269. o.color = r.downBodyColor;
  270. }
  271. ops = $.extend(true, {}, opts, o);
  272. r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops);
  273. r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops);
  274. o = {};
  275. b = open;
  276. h = close - open;
  277. // if color specified, use it
  278. if (r.fillDownBody) {
  279. o.fillRect = true;
  280. }
  281. else {
  282. o.strokeRect = true;
  283. w = w - this.lineWidth;
  284. a = x - w/2;
  285. }
  286. if (r.downBodyColor) {
  287. o.color = r.downBodyColor;
  288. o.fillStyle = r.downBodyColor;
  289. }
  290. points = [a, b, w, h];
  291. }
  292. // even, open = close
  293. else {
  294. // draw wick
  295. if (r.wickColor) {
  296. o.color = r.wickColor;
  297. }
  298. ops = $.extend(true, {}, opts, o);
  299. r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops);
  300. o = {};
  301. o.fillRect = false;
  302. o.strokeRect = false;
  303. a = [x - w/2, open];
  304. b = [x + w/2, close];
  305. w = null;
  306. h = null;
  307. points = [a, b];
  308. }
  309. ops = $.extend(true, {}, opts, o);
  310. r.shapeRenderer.draw(ctx, points, ops);
  311. }
  312. else {
  313. prevColor = opts.color;
  314. if (r.openColor) {
  315. opts.color = r.openColor;
  316. }
  317. // draw open tick
  318. if (!r.hlc) {
  319. r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts);
  320. }
  321. opts.color = prevColor;
  322. // draw wick
  323. if (r.wickColor) {
  324. opts.color = r.wickColor;
  325. }
  326. r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts);
  327. opts.color = prevColor;
  328. // draw close tick
  329. if (r.closeColor) {
  330. opts.color = r.closeColor;
  331. }
  332. r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts);
  333. opts.color = prevColor;
  334. }
  335. }
  336. }
  337. ctx.restore();
  338. };
  339. $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) {
  340. // This is a no-op, shadows drawn with lines.
  341. };
  342. // called with scope of plot.
  343. $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) {
  344. // provide some sensible highlighter options by default
  345. // These aren't good for hlc, only for ohlc or candlestick
  346. if (!options.highlighter) {
  347. options.highlighter = {
  348. showMarker:false,
  349. tooltipAxes: 'y',
  350. yvalues: 4,
  351. 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>'
  352. };
  353. }
  354. };
  355. //$.jqplot.preInitHooks.push($.jqplot.OHLCRenderer.checkOptions);
  356. })(jQuery);