indicators.src.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /**
  2. * @license Highstock JS v8.1.2 (2020-06-16)
  3. *
  4. * Indicator series type for Highstock
  5. *
  6. * (c) 2010-2019 Pawel Fus, Sebastian Bochan
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/indicators/indicators', ['highcharts', 'highcharts/modules/stock'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'mixins/indicator-required.js', [_modules['parts/Utilities.js']], function (U) {
  32. /**
  33. *
  34. * (c) 2010-2020 Daniel Studencki
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * */
  41. var error = U.error;
  42. /* eslint-disable no-invalid-this, valid-jsdoc */
  43. var requiredIndicatorMixin = {
  44. /**
  45. * Check whether given indicator is loaded, else throw error.
  46. * @private
  47. * @param {Highcharts.Indicator} indicator
  48. * Indicator constructor function.
  49. * @param {string} requiredIndicator
  50. * Required indicator type.
  51. * @param {string} type
  52. * Type of indicator where function was called (parent).
  53. * @param {Highcharts.IndicatorCallbackFunction} callback
  54. * Callback which is triggered if the given indicator is loaded.
  55. * Takes indicator as an argument.
  56. * @param {string} errMessage
  57. * Error message that will be logged in console.
  58. * @return {boolean}
  59. * Returns false when there is no required indicator loaded.
  60. */
  61. isParentLoaded: function (indicator, requiredIndicator, type, callback, errMessage) {
  62. if (indicator) {
  63. return callback ? callback(indicator) : true;
  64. }
  65. error(errMessage || this.generateMessage(type, requiredIndicator));
  66. return false;
  67. },
  68. /**
  69. * @private
  70. * @param {string} indicatorType
  71. * Indicator type
  72. * @param {string} required
  73. * Required indicator
  74. * @return {string}
  75. * Error message
  76. */
  77. generateMessage: function (indicatorType, required) {
  78. return 'Error: "' + indicatorType +
  79. '" indicator type requires "' + required +
  80. '" indicator loaded before. Please read docs: ' +
  81. 'https://api.highcharts.com/highstock/plotOptions.' +
  82. indicatorType;
  83. }
  84. };
  85. return requiredIndicatorMixin;
  86. });
  87. _registerModule(_modules, 'indicators/indicators.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['mixins/indicator-required.js']], function (H, U, requiredIndicatorMixin) {
  88. /* *
  89. *
  90. * License: www.highcharts.com/license
  91. *
  92. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  93. *
  94. * */
  95. var addEvent = U.addEvent, error = U.error, extend = U.extend, isArray = U.isArray, pick = U.pick, seriesType = U.seriesType, splat = U.splat;
  96. var Series = H.Series, seriesTypes = H.seriesTypes, ohlcProto = H.seriesTypes.ohlc.prototype, generateMessage = requiredIndicatorMixin.generateMessage;
  97. /**
  98. * The parameter allows setting line series type and use OHLC indicators. Data
  99. * in OHLC format is required.
  100. *
  101. * @sample {highstock} stock/indicators/use-ohlc-data
  102. * Plot line on Y axis
  103. *
  104. * @type {boolean}
  105. * @product highstock
  106. * @apioption plotOptions.line.useOhlcData
  107. */
  108. /* eslint-disable no-invalid-this */
  109. addEvent(H.Series, 'init', function (eventOptions) {
  110. var series = this, options = eventOptions.options;
  111. if (options.useOhlcData &&
  112. options.id !== 'highcharts-navigator-series') {
  113. extend(series, {
  114. pointValKey: ohlcProto.pointValKey,
  115. keys: ohlcProto.keys,
  116. pointArrayMap: ohlcProto.pointArrayMap,
  117. toYData: ohlcProto.toYData
  118. });
  119. }
  120. });
  121. addEvent(Series, 'afterSetOptions', function (e) {
  122. var options = e.options, dataGrouping = options.dataGrouping;
  123. if (dataGrouping &&
  124. options.useOhlcData &&
  125. options.id !== 'highcharts-navigator-series') {
  126. dataGrouping.approximation = 'ohlc';
  127. }
  128. });
  129. /* eslint-enable no-invalid-this */
  130. /**
  131. * The SMA series type.
  132. *
  133. * @private
  134. * @class
  135. * @name Highcharts.seriesTypes.sma
  136. *
  137. * @augments Highcharts.Series
  138. */
  139. seriesType('sma', 'line',
  140. /**
  141. * Simple moving average indicator (SMA). This series requires `linkedTo`
  142. * option to be set.
  143. *
  144. * @sample stock/indicators/sma
  145. * Simple moving average indicator
  146. *
  147. * @extends plotOptions.line
  148. * @since 6.0.0
  149. * @excluding allAreas, colorAxis, dragDrop, joinBy, keys,
  150. * navigatorOptions, pointInterval, pointIntervalUnit,
  151. * pointPlacement, pointRange, pointStart, showInNavigator,
  152. * stacking, useOhlcData
  153. * @product highstock
  154. * @requires stock/indicators/indicators
  155. * @optionparent plotOptions.sma
  156. */
  157. {
  158. /**
  159. * The name of the series as shown in the legend, tooltip etc. If not
  160. * set, it will be based on a technical indicator type and default
  161. * params.
  162. *
  163. * @type {string}
  164. */
  165. name: void 0,
  166. tooltip: {
  167. /**
  168. * Number of decimals in indicator series.
  169. */
  170. valueDecimals: 4
  171. },
  172. /**
  173. * The main series ID that indicator will be based on. Required for this
  174. * indicator.
  175. *
  176. * @type {string}
  177. */
  178. linkedTo: void 0,
  179. /**
  180. * Whether to compare indicator to the main series values
  181. * or indicator values.
  182. *
  183. * @sample {highstock} stock/plotoptions/series-comparetomain/
  184. * Difference between comparing SMA values to the main series
  185. * and its own values.
  186. *
  187. * @type {boolean}
  188. */
  189. compareToMain: false,
  190. /**
  191. * Paramters used in calculation of regression series' points.
  192. */
  193. params: {
  194. /**
  195. * The point index which indicator calculations will base. For
  196. * example using OHLC data, index=2 means the indicator will be
  197. * calculated using Low values.
  198. */
  199. index: 0,
  200. /**
  201. * The base period for indicator calculations. This is the number of
  202. * data points which are taken into account for the indicator
  203. * calculations.
  204. */
  205. period: 14
  206. }
  207. },
  208. /**
  209. * @lends Highcharts.Series.prototype
  210. */
  211. {
  212. processData: function () {
  213. var series = this, compareToMain = series.options.compareToMain, linkedParent = series.linkedParent;
  214. Series.prototype.processData.apply(series, arguments);
  215. if (linkedParent && linkedParent.compareValue && compareToMain) {
  216. series.compareValue = linkedParent.compareValue;
  217. }
  218. return;
  219. },
  220. bindTo: {
  221. series: true,
  222. eventName: 'updatedData'
  223. },
  224. hasDerivedData: true,
  225. useCommonDataGrouping: true,
  226. nameComponents: ['period'],
  227. nameSuffixes: [],
  228. calculateOn: 'init',
  229. // Defines on which other indicators is this indicator based on.
  230. requiredIndicators: [],
  231. requireIndicators: function () {
  232. var obj = {
  233. allLoaded: true
  234. };
  235. // Check whether all required indicators are loaded, else return
  236. // the object with missing indicator's name.
  237. this.requiredIndicators.forEach(function (indicator) {
  238. if (seriesTypes[indicator]) {
  239. seriesTypes[indicator].prototype.requireIndicators();
  240. }
  241. else {
  242. obj.allLoaded = false;
  243. obj.needed = indicator;
  244. }
  245. });
  246. return obj;
  247. },
  248. init: function (chart, options) {
  249. var indicator = this, requiredIndicators = indicator.requireIndicators();
  250. // Check whether all required indicators are loaded.
  251. if (!requiredIndicators.allLoaded) {
  252. return error(generateMessage(indicator.type, requiredIndicators.needed));
  253. }
  254. Series.prototype.init.call(indicator, chart, options);
  255. // Make sure we find series which is a base for an indicator
  256. chart.linkSeries();
  257. indicator.dataEventsToUnbind = [];
  258. /**
  259. * @private
  260. * @return {void}
  261. */
  262. function recalculateValues() {
  263. var oldData = indicator.points || [], oldDataLength = (indicator.xData || []).length, processedData = indicator.getValues(indicator.linkedParent, indicator.options.params) || {
  264. values: [],
  265. xData: [],
  266. yData: []
  267. }, croppedDataValues = [], overwriteData = true, oldFirstPointIndex, oldLastPointIndex, croppedData, min, max, i;
  268. // We need to update points to reflect changes in all,
  269. // x and y's, values. However, do it only for non-grouped
  270. // data - grouping does it for us (#8572)
  271. if (oldDataLength &&
  272. !indicator.hasGroupedData &&
  273. indicator.visible &&
  274. indicator.points) {
  275. // When data is cropped update only avaliable points (#9493)
  276. if (indicator.cropped) {
  277. if (indicator.xAxis) {
  278. min = indicator.xAxis.min;
  279. max = indicator.xAxis.max;
  280. }
  281. croppedData = indicator.cropData(processedData.xData, processedData.yData, min, max);
  282. for (i = 0; i < croppedData.xData.length; i++) {
  283. // (#10774)
  284. croppedDataValues.push([
  285. croppedData.xData[i]
  286. ].concat(splat(croppedData.yData[i])));
  287. }
  288. oldFirstPointIndex = processedData.xData.indexOf(indicator.xData[0]);
  289. oldLastPointIndex = processedData.xData.indexOf(indicator.xData[indicator.xData.length - 1]);
  290. // Check if indicator points should be shifted (#8572)
  291. if (oldFirstPointIndex === -1 &&
  292. oldLastPointIndex === processedData.xData.length - 2) {
  293. if (croppedDataValues[0][0] === oldData[0].x) {
  294. croppedDataValues.shift();
  295. }
  296. }
  297. indicator.updateData(croppedDataValues);
  298. // Omit addPoint() and removePoint() cases
  299. }
  300. else if (processedData.xData.length !== oldDataLength - 1 &&
  301. processedData.xData.length !== oldDataLength + 1) {
  302. overwriteData = false;
  303. indicator.updateData(processedData.values);
  304. }
  305. }
  306. if (overwriteData) {
  307. indicator.xData = processedData.xData;
  308. indicator.yData = processedData.yData;
  309. indicator.options.data = processedData.values;
  310. }
  311. // Removal of processedXData property is required because on
  312. // first translate processedXData array is empty
  313. if (indicator.bindTo.series === false) {
  314. delete indicator.processedXData;
  315. indicator.isDirty = true;
  316. indicator.redraw();
  317. }
  318. indicator.isDirtyData = false;
  319. }
  320. if (!indicator.linkedParent) {
  321. return error('Series ' +
  322. indicator.options.linkedTo +
  323. ' not found! Check `linkedTo`.', false, chart);
  324. }
  325. indicator.dataEventsToUnbind.push(addEvent(indicator.bindTo.series ?
  326. indicator.linkedParent : indicator.linkedParent.xAxis, indicator.bindTo.eventName, recalculateValues));
  327. if (indicator.calculateOn === 'init') {
  328. recalculateValues();
  329. }
  330. else {
  331. var unbinder = addEvent(indicator.chart, indicator.calculateOn, function () {
  332. recalculateValues();
  333. // Call this just once, on init
  334. unbinder();
  335. });
  336. }
  337. return indicator;
  338. },
  339. getName: function () {
  340. var name = this.name, params = [];
  341. if (!name) {
  342. (this.nameComponents || []).forEach(function (component, index) {
  343. params.push(this.options.params[component] +
  344. pick(this.nameSuffixes[index], ''));
  345. }, this);
  346. name = (this.nameBase || this.type.toUpperCase()) +
  347. (this.nameComponents ? ' (' + params.join(', ') + ')' : '');
  348. }
  349. return name;
  350. },
  351. getValues: function (series, params) {
  352. var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal.length, range = 0, sum = 0, SMA = [], xData = [], yData = [], index = -1, i, SMAPoint;
  353. if (xVal.length < period) {
  354. return;
  355. }
  356. // Switch index for OHLC / Candlestick / Arearange
  357. if (isArray(yVal[0])) {
  358. index = params.index ? params.index : 0;
  359. }
  360. // Accumulate first N-points
  361. while (range < period - 1) {
  362. sum += index < 0 ? yVal[range] : yVal[range][index];
  363. range++;
  364. }
  365. // Calculate value one-by-one for each period in visible data
  366. for (i = range; i < yValLen; i++) {
  367. sum += index < 0 ? yVal[i] : yVal[i][index];
  368. SMAPoint = [xVal[i], sum / period];
  369. SMA.push(SMAPoint);
  370. xData.push(SMAPoint[0]);
  371. yData.push(SMAPoint[1]);
  372. sum -= (index < 0 ?
  373. yVal[i - range] :
  374. yVal[i - range][index]);
  375. }
  376. return {
  377. values: SMA,
  378. xData: xData,
  379. yData: yData
  380. };
  381. },
  382. destroy: function () {
  383. this.dataEventsToUnbind.forEach(function (unbinder) {
  384. unbinder();
  385. });
  386. Series.prototype.destroy.apply(this, arguments);
  387. }
  388. });
  389. /**
  390. * A `SMA` series. If the [type](#series.sma.type) option is not specified, it
  391. * is inherited from [chart.type](#chart.type).
  392. *
  393. * @extends series,plotOptions.sma
  394. * @since 6.0.0
  395. * @product highstock
  396. * @excluding dataParser, dataURL, useOhlcData
  397. * @requires stock/indicators/indicators
  398. * @apioption series.sma
  399. */
  400. ''; // adds doclet above to the transpiled file
  401. });
  402. _registerModule(_modules, 'masters/indicators/indicators.src.js', [], function () {
  403. });
  404. }));