MACDIndicator.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /* *
  2. *
  3. * License: www.highcharts.com/license
  4. *
  5. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  6. *
  7. * */
  8. 'use strict';
  9. import H from '../../Core/Globals.js';
  10. import U from '../../Core/Utilities.js';
  11. var correctFloat = U.correctFloat, defined = U.defined, merge = U.merge, seriesType = U.seriesType;
  12. var noop = H.noop, SMA = H.seriesTypes.sma, EMA = H.seriesTypes.ema;
  13. /**
  14. * The MACD series type.
  15. *
  16. * @private
  17. * @class
  18. * @name Highcharts.seriesTypes.macd
  19. *
  20. * @augments Highcharts.Series
  21. */
  22. seriesType('macd', 'sma',
  23. /**
  24. * Moving Average Convergence Divergence (MACD). This series requires
  25. * `linkedTo` option to be set and should be loaded after the
  26. * `stock/indicators/indicators.js` and `stock/indicators/ema.js`.
  27. *
  28. * @sample stock/indicators/macd
  29. * MACD indicator
  30. *
  31. * @extends plotOptions.sma
  32. * @since 6.0.0
  33. * @product highstock
  34. * @requires stock/indicators/indicators
  35. * @requires stock/indicators/macd
  36. * @optionparent plotOptions.macd
  37. */
  38. {
  39. params: {
  40. /**
  41. * The short period for indicator calculations.
  42. */
  43. shortPeriod: 12,
  44. /**
  45. * The long period for indicator calculations.
  46. */
  47. longPeriod: 26,
  48. /**
  49. * The base period for signal calculations.
  50. */
  51. signalPeriod: 9,
  52. period: 26
  53. },
  54. /**
  55. * The styles for signal line
  56. */
  57. signalLine: {
  58. /**
  59. * @sample stock/indicators/macd-zones
  60. * Zones in MACD
  61. *
  62. * @extends plotOptions.macd.zones
  63. */
  64. zones: [],
  65. styles: {
  66. /**
  67. * Pixel width of the line.
  68. */
  69. lineWidth: 1,
  70. /**
  71. * Color of the line.
  72. *
  73. * @type {Highcharts.ColorString}
  74. */
  75. lineColor: void 0
  76. }
  77. },
  78. /**
  79. * The styles for macd line
  80. */
  81. macdLine: {
  82. /**
  83. * @sample stock/indicators/macd-zones
  84. * Zones in MACD
  85. *
  86. * @extends plotOptions.macd.zones
  87. */
  88. zones: [],
  89. styles: {
  90. /**
  91. * Pixel width of the line.
  92. */
  93. lineWidth: 1,
  94. /**
  95. * Color of the line.
  96. *
  97. * @type {Highcharts.ColorString}
  98. */
  99. lineColor: void 0
  100. }
  101. },
  102. threshold: 0,
  103. groupPadding: 0.1,
  104. pointPadding: 0.1,
  105. crisp: false,
  106. states: {
  107. hover: {
  108. halo: {
  109. size: 0
  110. }
  111. }
  112. },
  113. tooltip: {
  114. pointFormat: '<span style="color:{point.color}">\u25CF</span> <b> {series.name}</b><br/>' +
  115. 'Value: {point.MACD}<br/>' +
  116. 'Signal: {point.signal}<br/>' +
  117. 'Histogram: {point.y}<br/>'
  118. },
  119. dataGrouping: {
  120. approximation: 'averages'
  121. },
  122. minPointLength: 0
  123. },
  124. /**
  125. * @lends Highcharts.Series#
  126. */
  127. {
  128. nameComponents: ['longPeriod', 'shortPeriod', 'signalPeriod'],
  129. requiredIndicators: ['ema'],
  130. // "y" value is treated as Histogram data
  131. pointArrayMap: ['y', 'signal', 'MACD'],
  132. parallelArrays: ['x', 'y', 'signal', 'MACD'],
  133. pointValKey: 'y',
  134. // Columns support:
  135. markerAttribs: noop,
  136. getColumnMetrics: H.seriesTypes.column.prototype.getColumnMetrics,
  137. crispCol: H.seriesTypes.column.prototype.crispCol,
  138. // Colors and lines:
  139. init: function () {
  140. SMA.prototype.init.apply(this, arguments);
  141. // Check whether series is initialized. It may be not initialized,
  142. // when any of required indicators is missing.
  143. if (this.options) {
  144. // Set default color for a signal line and the histogram:
  145. this.options = merge({
  146. signalLine: {
  147. styles: {
  148. lineColor: this.color
  149. }
  150. },
  151. macdLine: {
  152. styles: {
  153. color: this.color
  154. }
  155. }
  156. }, this.options);
  157. // Zones have indexes automatically calculated, we need to
  158. // translate them to support multiple lines within one indicator
  159. this.macdZones = {
  160. zones: this.options.macdLine.zones,
  161. startIndex: 0
  162. };
  163. this.signalZones = {
  164. zones: this.macdZones.zones.concat(this.options.signalLine.zones),
  165. startIndex: this.macdZones.zones.length
  166. };
  167. this.resetZones = true;
  168. }
  169. },
  170. toYData: function (point) {
  171. return [point.y, point.signal, point.MACD];
  172. },
  173. translate: function () {
  174. var indicator = this, plotNames = ['plotSignal', 'plotMACD'];
  175. H.seriesTypes.column.prototype.translate.apply(indicator);
  176. indicator.points.forEach(function (point) {
  177. [point.signal, point.MACD].forEach(function (value, i) {
  178. if (value !== null) {
  179. point[plotNames[i]] =
  180. indicator.yAxis.toPixels(value, true);
  181. }
  182. });
  183. });
  184. },
  185. destroy: function () {
  186. // this.graph is null due to removing two times the same SVG element
  187. this.graph = null;
  188. this.graphmacd = this.graphmacd && this.graphmacd.destroy();
  189. this.graphsignal = this.graphsignal && this.graphsignal.destroy();
  190. SMA.prototype.destroy.apply(this, arguments);
  191. },
  192. drawPoints: H.seriesTypes.column.prototype.drawPoints,
  193. drawGraph: function () {
  194. var indicator = this, mainLinePoints = indicator.points, pointsLength = mainLinePoints.length, mainLineOptions = indicator.options, histogramZones = indicator.zones, gappedExtend = {
  195. options: {
  196. gapSize: mainLineOptions.gapSize
  197. }
  198. }, otherSignals = [[], []], point;
  199. // Generate points for top and bottom lines:
  200. while (pointsLength--) {
  201. point = mainLinePoints[pointsLength];
  202. if (defined(point.plotMACD)) {
  203. otherSignals[0].push({
  204. plotX: point.plotX,
  205. plotY: point.plotMACD,
  206. isNull: !defined(point.plotMACD)
  207. });
  208. }
  209. if (defined(point.plotSignal)) {
  210. otherSignals[1].push({
  211. plotX: point.plotX,
  212. plotY: point.plotSignal,
  213. isNull: !defined(point.plotMACD)
  214. });
  215. }
  216. }
  217. // Modify options and generate smoothing line:
  218. ['macd', 'signal'].forEach(function (lineName, i) {
  219. indicator.points = otherSignals[i];
  220. indicator.options = merge(mainLineOptions[lineName + 'Line'].styles, gappedExtend);
  221. indicator.graph = indicator['graph' + lineName];
  222. // Zones extension:
  223. indicator.currentLineZone = lineName + 'Zones';
  224. indicator.zones =
  225. indicator[indicator.currentLineZone].zones;
  226. SMA.prototype.drawGraph.call(indicator);
  227. indicator['graph' + lineName] = indicator.graph;
  228. });
  229. // Restore options:
  230. indicator.points = mainLinePoints;
  231. indicator.options = mainLineOptions;
  232. indicator.zones = histogramZones;
  233. indicator.currentLineZone = null;
  234. // indicator.graph = null;
  235. },
  236. getZonesGraphs: function (props) {
  237. var allZones = SMA.prototype.getZonesGraphs.call(this, props), currentZones = allZones;
  238. if (this.currentLineZone) {
  239. currentZones = allZones.splice(this[this.currentLineZone].startIndex + 1);
  240. if (!currentZones.length) {
  241. // Line has no zones, return basic graph "zone"
  242. currentZones = [props[0]];
  243. }
  244. else {
  245. // Add back basic prop:
  246. currentZones.splice(0, 0, props[0]);
  247. }
  248. }
  249. return currentZones;
  250. },
  251. applyZones: function () {
  252. // Histogram zones are handled by drawPoints method
  253. // Here we need to apply zones for all lines
  254. var histogramZones = this.zones;
  255. // signalZones.zones contains all zones:
  256. this.zones = this.signalZones.zones;
  257. SMA.prototype.applyZones.call(this);
  258. // applyZones hides only main series.graph, hide macd line manually
  259. if (this.graphmacd && this.options.macdLine.zones.length) {
  260. this.graphmacd.hide();
  261. }
  262. this.zones = histogramZones;
  263. },
  264. getValues: function (series, params) {
  265. var j = 0, MACD = [], xMACD = [], yMACD = [], signalLine = [], shortEMA, longEMA, i;
  266. if (series.xData.length <
  267. params.longPeriod + params.signalPeriod) {
  268. return;
  269. }
  270. // Calculating the short and long EMA used when calculating the MACD
  271. shortEMA = EMA.prototype.getValues(series, {
  272. period: params.shortPeriod
  273. });
  274. longEMA = EMA.prototype.getValues(series, {
  275. period: params.longPeriod
  276. });
  277. shortEMA = shortEMA.values;
  278. longEMA = longEMA.values;
  279. // Subtract each Y value from the EMA's and create the new dataset
  280. // (MACD)
  281. for (i = 1; i <= shortEMA.length; i++) {
  282. if (defined(longEMA[i - 1]) &&
  283. defined(longEMA[i - 1][1]) &&
  284. defined(shortEMA[i + params.shortPeriod + 1]) &&
  285. defined(shortEMA[i + params.shortPeriod + 1][0])) {
  286. MACD.push([
  287. shortEMA[i + params.shortPeriod + 1][0],
  288. 0,
  289. null,
  290. shortEMA[i + params.shortPeriod + 1][1] -
  291. longEMA[i - 1][1]
  292. ]);
  293. }
  294. }
  295. // Set the Y and X data of the MACD. This is used in calculating the
  296. // signal line.
  297. for (i = 0; i < MACD.length; i++) {
  298. xMACD.push(MACD[i][0]);
  299. yMACD.push([0, null, MACD[i][3]]);
  300. }
  301. // Setting the signalline (Signal Line: X-day EMA of MACD line).
  302. signalLine = EMA.prototype.getValues({
  303. xData: xMACD,
  304. yData: yMACD
  305. }, {
  306. period: params.signalPeriod,
  307. index: 2
  308. });
  309. signalLine = signalLine.values;
  310. // Setting the MACD Histogram. In comparison to the loop with pure
  311. // MACD this loop uses MACD x value not xData.
  312. for (i = 0; i < MACD.length; i++) {
  313. // detect the first point
  314. if (MACD[i][0] >= signalLine[0][0]) {
  315. MACD[i][2] = signalLine[j][1];
  316. yMACD[i] = [0, signalLine[j][1], MACD[i][3]];
  317. if (MACD[i][3] === null) {
  318. MACD[i][1] = 0;
  319. yMACD[i][0] = 0;
  320. }
  321. else {
  322. MACD[i][1] = correctFloat(MACD[i][3] -
  323. signalLine[j][1]);
  324. yMACD[i][0] = correctFloat(MACD[i][3] -
  325. signalLine[j][1]);
  326. }
  327. j++;
  328. }
  329. }
  330. return {
  331. values: MACD,
  332. xData: xMACD,
  333. yData: yMACD
  334. };
  335. }
  336. });
  337. /**
  338. * A `MACD` series. If the [type](#series.macd.type) option is not
  339. * specified, it is inherited from [chart.type](#chart.type).
  340. *
  341. * @extends series,plotOptions.macd
  342. * @since 6.0.0
  343. * @product highstock
  344. * @excluding dataParser, dataURL
  345. * @requires stock/indicators/indicators
  346. * @requires stock/indicators/macd
  347. * @apioption series.macd
  348. */
  349. ''; // to include the above in the js output