UNPKG

highcharts

Version:
1,232 lines (1,225 loc) 347 kB
/** * @license Highstock JS v8.0.0 (2019-12-10) * * All technical indicators for Highstock * * (c) 2010-2019 Pawel Fus * * License: www.highcharts.com/license */ 'use strict'; (function (factory) { if (typeof module === 'object' && module.exports) { factory['default'] = factory; module.exports = factory; } else if (typeof define === 'function' && define.amd) { define('highcharts/indicators/indicators-all', ['highcharts', 'highcharts/modules/stock'], function (Highcharts) { factory(Highcharts); factory.Highcharts = Highcharts; return factory; }); } else { factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined); } }(function (Highcharts) { var _modules = Highcharts ? Highcharts._modules : {}; function _registerModule(obj, path, args, fn) { if (!obj.hasOwnProperty(path)) { obj[path] = fn.apply(null, args); } } _registerModule(_modules, 'mixins/indicator-required.js', [_modules['parts/Globals.js']], function (H) { /** * * (c) 2010-2019 Daniel Studencki * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var error = H.error; /* eslint-disable no-invalid-this, valid-jsdoc */ var requiredIndicatorMixin = { /** * Check whether given indicator is loaded, else throw error. * @private * @param {Highcharts.Indicator} indicator * Indicator constructor function. * @param {string} requiredIndicator * Required indicator type. * @param {string} type * Type of indicator where function was called (parent). * @param {Highcharts.IndicatorCallbackFunction} callback * Callback which is triggered if the given indicator is loaded. * Takes indicator as an argument. * @param {string} errMessage * Error message that will be logged in console. * @return {boolean} * Returns false when there is no required indicator loaded. */ isParentLoaded: function (indicator, requiredIndicator, type, callback, errMessage) { if (indicator) { return callback ? callback(indicator) : true; } error(errMessage || this.generateMessage(type, requiredIndicator)); return false; }, /** * @private * @param {string} indicatorType * Indicator type * @param {string} required * Required indicator * @return {string} * Error message */ generateMessage: function (indicatorType, required) { return 'Error: "' + indicatorType + '" indicator type requires "' + required + '" indicator loaded before. Please read docs: ' + 'https://api.highcharts.com/highstock/plotOptions.' + indicatorType; } }; return requiredIndicatorMixin; }); _registerModule(_modules, 'indicators/indicators.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['mixins/indicator-required.js']], function (H, U, requiredIndicatorMixin) { /* * * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var extend = U.extend, isArray = U.isArray, pick = U.pick, splat = U.splat; var error = H.error, Series = H.Series, addEvent = H.addEvent, seriesType = H.seriesType, seriesTypes = H.seriesTypes, ohlcProto = H.seriesTypes.ohlc.prototype, generateMessage = requiredIndicatorMixin.generateMessage; /** * The parameter allows setting line series type and use OHLC indicators. Data * in OHLC format is required. * * @sample {highstock} stock/indicators/use-ohlc-data * Plot line on Y axis * * @type {boolean} * @product highstock * @apioption plotOptions.line.useOhlcData */ /* eslint-disable no-invalid-this */ addEvent(H.Series, 'init', function (eventOptions) { var series = this, options = eventOptions.options; if (options.useOhlcData && options.id !== 'highcharts-navigator-series') { extend(series, { pointValKey: ohlcProto.pointValKey, keys: ohlcProto.keys, pointArrayMap: ohlcProto.pointArrayMap, toYData: ohlcProto.toYData }); } }); addEvent(Series, 'afterSetOptions', function (e) { var options = e.options, dataGrouping = options.dataGrouping; if (dataGrouping && options.useOhlcData && options.id !== 'highcharts-navigator-series') { dataGrouping.approximation = 'ohlc'; } }); /* eslint-enable no-invalid-this */ /** * The SMA series type. * * @private * @class * @name Highcharts.seriesTypes.sma * * @augments Highcharts.Series */ seriesType('sma', 'line', /** * Simple moving average indicator (SMA). This series requires `linkedTo` * option to be set. * * @sample stock/indicators/sma * Simple moving average indicator * * @extends plotOptions.line * @since 6.0.0 * @excluding allAreas, colorAxis, dragDrop, joinBy, keys, * navigatorOptions, pointInterval, pointIntervalUnit, * pointPlacement, pointRange, pointStart, showInNavigator, * stacking, useOhlcData * @product highstock * @requires stock/indicators/indicators * @optionparent plotOptions.sma */ { /** * The name of the series as shown in the legend, tooltip etc. If not * set, it will be based on a technical indicator type and default * params. * * @type {string} */ name: void 0, tooltip: { /** * Number of decimals in indicator series. */ valueDecimals: 4 }, /** * The main series ID that indicator will be based on. Required for this * indicator. * * @type {string} */ linkedTo: void 0, /** * Whether to compare indicator to the main series values * or indicator values. * * @sample {highstock} stock/plotoptions/series-comparetomain/ * Difference between comparing SMA values to the main series * and its own values. * * @type {boolean} */ compareToMain: false, /** * Paramters used in calculation of regression series' points. */ params: { /** * The point index which indicator calculations will base. For * example using OHLC data, index=2 means the indicator will be * calculated using Low values. */ index: 0, /** * The base period for indicator calculations. This is the number of * data points which are taken into account for the indicator * calculations. */ period: 14 } }, /** * @lends Highcharts.Series.prototype */ { processData: function () { var series = this, compareToMain = series.options.compareToMain, linkedParent = series.linkedParent; Series.prototype.processData.apply(series, arguments); if (linkedParent && linkedParent.compareValue && compareToMain) { series.compareValue = linkedParent.compareValue; } return; }, bindTo: { series: true, eventName: 'updatedData' }, hasDerivedData: true, useCommonDataGrouping: true, nameComponents: ['period'], nameSuffixes: [], calculateOn: 'init', // Defines on which other indicators is this indicator based on. requiredIndicators: [], requireIndicators: function () { var obj = { allLoaded: true }; // Check whether all required indicators are loaded, else return // the object with missing indicator's name. this.requiredIndicators.forEach(function (indicator) { if (seriesTypes[indicator]) { seriesTypes[indicator].prototype.requireIndicators(); } else { obj.allLoaded = false; obj.needed = indicator; } }); return obj; }, init: function (chart, options) { var indicator = this, requiredIndicators = indicator.requireIndicators(); // Check whether all required indicators are loaded. if (!requiredIndicators.allLoaded) { return error(generateMessage(indicator.type, requiredIndicators.needed)); } Series.prototype.init.call(indicator, chart, options); // Make sure we find series which is a base for an indicator chart.linkSeries(); indicator.dataEventsToUnbind = []; /** * @private * @return {void} */ function recalculateValues() { var oldData = indicator.points || [], oldDataLength = (indicator.xData || []).length, processedData = indicator.getValues(indicator.linkedParent, indicator.options.params) || { values: [], xData: [], yData: [] }, croppedDataValues = [], overwriteData = true, oldFirstPointIndex, oldLastPointIndex, croppedData, min, max, i; // We need to update points to reflect changes in all, // x and y's, values. However, do it only for non-grouped // data - grouping does it for us (#8572) if (oldDataLength && !indicator.hasGroupedData && indicator.visible && indicator.points) { // When data is cropped update only avaliable points (#9493) if (indicator.cropped) { if (indicator.xAxis) { min = indicator.xAxis.min; max = indicator.xAxis.max; } croppedData = indicator.cropData(processedData.xData, processedData.yData, min, max); for (i = 0; i < croppedData.xData.length; i++) { // (#10774) croppedDataValues.push([ croppedData.xData[i] ].concat(splat(croppedData.yData[i]))); } oldFirstPointIndex = processedData.xData.indexOf(indicator.xData[0]); oldLastPointIndex = processedData.xData.indexOf(indicator.xData[indicator.xData.length - 1]); // Check if indicator points should be shifted (#8572) if (oldFirstPointIndex === -1 && oldLastPointIndex === processedData.xData.length - 2) { if (croppedDataValues[0][0] === oldData[0].x) { croppedDataValues.shift(); } } indicator.updateData(croppedDataValues); // Omit addPoint() and removePoint() cases } else if (processedData.xData.length !== oldDataLength - 1 && processedData.xData.length !== oldDataLength + 1) { overwriteData = false; indicator.updateData(processedData.values); } } if (overwriteData) { indicator.xData = processedData.xData; indicator.yData = processedData.yData; indicator.options.data = processedData.values; } // Removal of processedXData property is required because on // first translate processedXData array is empty if (indicator.bindTo.series === false) { delete indicator.processedXData; indicator.isDirty = true; indicator.redraw(); } indicator.isDirtyData = false; } if (!indicator.linkedParent) { return error('Series ' + indicator.options.linkedTo + ' not found! Check `linkedTo`.', false, chart); } indicator.dataEventsToUnbind.push(addEvent(indicator.bindTo.series ? indicator.linkedParent : indicator.linkedParent.xAxis, indicator.bindTo.eventName, recalculateValues)); if (indicator.calculateOn === 'init') { recalculateValues(); } else { var unbinder = addEvent(indicator.chart, indicator.calculateOn, function () { recalculateValues(); // Call this just once, on init unbinder(); }); } return indicator; }, getName: function () { var name = this.name, params = []; if (!name) { (this.nameComponents || []).forEach(function (component, index) { params.push(this.options.params[component] + pick(this.nameSuffixes[index], '')); }, this); name = (this.nameBase || this.type.toUpperCase()) + (this.nameComponents ? ' (' + params.join(', ') + ')' : ''); } return name; }, getValues: function (series, params) { var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal.length, range = 0, sum = 0, SMA = [], xData = [], yData = [], index = -1, i, SMAPoint; if (xVal.length < period) { return; } // Switch index for OHLC / Candlestick / Arearange if (isArray(yVal[0])) { index = params.index ? params.index : 0; } // Accumulate first N-points while (range < period - 1) { sum += index < 0 ? yVal[range] : yVal[range][index]; range++; } // Calculate value one-by-one for each period in visible data for (i = range; i < yValLen; i++) { sum += index < 0 ? yVal[i] : yVal[i][index]; SMAPoint = [xVal[i], sum / period]; SMA.push(SMAPoint); xData.push(SMAPoint[0]); yData.push(SMAPoint[1]); sum -= (index < 0 ? yVal[i - range] : yVal[i - range][index]); } return { values: SMA, xData: xData, yData: yData }; }, destroy: function () { this.dataEventsToUnbind.forEach(function (unbinder) { unbinder(); }); Series.prototype.destroy.apply(this, arguments); } }); /** * A `SMA` series. If the [type](#series.sma.type) option is not specified, it * is inherited from [chart.type](#chart.type). * * @extends series,plotOptions.sma * @since 6.0.0 * @product highstock * @excluding dataParser, dataURL, useOhlcData * @requires stock/indicators/indicators * @apioption series.sma */ ''; // adds doclet above to the transpiled file }); _registerModule(_modules, 'indicators/accumulation-distribution.src.js', [_modules['parts/Globals.js']], function (H) { /* * * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * */ var seriesType = H.seriesType; /* eslint-disable valid-jsdoc */ // Utils: /** * @private */ function populateAverage(xVal, yVal, yValVolume, i) { var high = yVal[i][1], low = yVal[i][2], close = yVal[i][3], volume = yValVolume[i], adY = close === high && close === low || high === low ? 0 : ((2 * close - low - high) / (high - low)) * volume, adX = xVal[i]; return [adX, adY]; } /* eslint-enable valid-jsdoc */ /** * The AD series type. * * @private * @class * @name Highcharts.seriesTypes.ad * * @augments Highcharts.Series */ seriesType('ad', 'sma', /** * Accumulation Distribution (AD). This series requires `linkedTo` option to * be set. * * @sample stock/indicators/accumulation-distribution * Accumulation/Distribution indicator * * @extends plotOptions.sma * @since 6.0.0 * @product highstock * @requires stock/indicators/indicators * @requires stock/indicators/accumulation-distribution * @optionparent plotOptions.ad */ { params: { /** * The id of volume series which is mandatory. * For example using OHLC data, volumeSeriesID='volume' means * the indicator will be calculated using OHLC and volume values. * * @since 6.0.0 */ volumeSeriesID: 'volume' } }, /** * @lends Highcharts.Series# */ { nameComponents: false, nameBase: 'Accumulation/Distribution', getValues: function (series, params) { var period = params.period, xVal = series.xData, yVal = series.yData, volumeSeriesID = params.volumeSeriesID, volumeSeries = series.chart.get(volumeSeriesID), yValVolume = volumeSeries && volumeSeries.yData, yValLen = yVal ? yVal.length : 0, AD = [], xData = [], yData = [], len, i, ADPoint; if (xVal.length <= period && yValLen && yVal[0].length !== 4) { return; } if (!volumeSeries) { H.error('Series ' + volumeSeriesID + ' not found! Check `volumeSeriesID`.', true, series.chart); return; } // i = period <-- skip first N-points // Calculate value one-by-one for each period in visible data for (i = period; i < yValLen; i++) { len = AD.length; ADPoint = populateAverage(xVal, yVal, yValVolume, i, period); if (len > 0) { ADPoint[1] += AD[len - 1][1]; } AD.push(ADPoint); xData.push(ADPoint[0]); yData.push(ADPoint[1]); } return { values: AD, xData: xData, yData: yData }; } }); /** * A `AD` series. If the [type](#series.ad.type) option is not * specified, it is inherited from [chart.type](#chart.type). * * @extends series,plotOptions.ad * @since 6.0.0 * @excluding dataParser, dataURL * @product highstock * @requires stock/indicators/indicators * @requires stock/indicators/accumulation-distribution * @apioption series.ad */ ''; // add doclet above to transpiled file }); _registerModule(_modules, 'indicators/ao.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) { /* * * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var correctFloat = U.correctFloat, isArray = U.isArray; var noop = H.noop; /** * The AO series type * * @private * @class * @name Highcharts.seriesTypes.ao * * @augments Highcharts.Series */ H.seriesType('ao', 'sma', /** * Awesome Oscillator. This series requires the `linkedTo` option to * be set and should be loaded after the `stock/indicators/indicators.js` * * @sample {highstock} stock/indicators/ao * Awesome * * @extends plotOptions.sma * @since 7.0.0 * @product highstock * @excluding allAreas, colorAxis, joinBy, keys, navigatorOptions, * params, pointInterval, pointIntervalUnit, pointPlacement, * pointRange, pointStart, showInNavigator, stacking * @requires stock/indicators/indicators * @requires stock/indicators/ao * @optionparent plotOptions.ao */ { /** * Color of the Awesome oscillator series bar that is greater than the * previous one. Note that if a `color` is defined, the `color` * takes precedence and the `greaterBarColor` is ignored. * * @sample {highstock} stock/indicators/ao/ * greaterBarColor * * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} * @since 7.0.0 */ greaterBarColor: '#06B535', /** * Color of the Awesome oscillator series bar that is lower than the * previous one. Note that if a `color` is defined, the `color` * takes precedence and the `lowerBarColor` is ignored. * * @sample {highstock} stock/indicators/ao/ * lowerBarColor * * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject} * @since 7.0.0 */ lowerBarColor: '#F21313', threshold: 0, groupPadding: 0.2, pointPadding: 0.2, states: { hover: { halo: { size: 0 } } } }, /** * @lends Highcharts.Series# */ { nameBase: 'AO', nameComponents: false, // Columns support: markerAttribs: noop, getColumnMetrics: H.seriesTypes.column.prototype.getColumnMetrics, crispCol: H.seriesTypes.column.prototype.crispCol, translate: H.seriesTypes.column.prototype.translate, drawPoints: H.seriesTypes.column.prototype.drawPoints, drawGraph: function () { var indicator = this, options = indicator.options, points = indicator.points, userColor = indicator.userOptions.color, positiveColor = options.greaterBarColor, negativeColor = options.lowerBarColor, firstPoint = points[0], i; if (!userColor && firstPoint) { firstPoint.color = positiveColor; for (i = 1; i < points.length; i++) { if (points[i].y > points[i - 1].y) { points[i].color = positiveColor; } else if (points[i].y < points[i - 1].y) { points[i].color = negativeColor; } else { points[i].color = points[i - 1].color; } } } }, getValues: function (series) { var shortPeriod = 5, longPeriod = 34, xVal = series.xData || [], yVal = series.yData || [], yValLen = yVal.length, AO = [], // 0- date, 1- Awesome Oscillator xData = [], yData = [], high = 1, low = 2, shortSum = 0, longSum = 0, shortSMA, // Shorter Period SMA longSMA, // Longer Period SMA awesome, shortLastIndex, longLastIndex, price, i, j; if (xVal.length <= longPeriod || !isArray(yVal[0]) || yVal[0].length !== 4) { return; } for (i = 0; i < longPeriod - 1; i++) { price = (yVal[i][high] + yVal[i][low]) / 2; if (i >= longPeriod - shortPeriod) { shortSum = correctFloat(shortSum + price); } longSum = correctFloat(longSum + price); } for (j = longPeriod - 1; j < yValLen; j++) { price = (yVal[j][high] + yVal[j][low]) / 2; shortSum = correctFloat(shortSum + price); longSum = correctFloat(longSum + price); shortSMA = shortSum / shortPeriod; longSMA = longSum / longPeriod; awesome = correctFloat(shortSMA - longSMA); AO.push([xVal[j], awesome]); xData.push(xVal[j]); yData.push(awesome); shortLastIndex = j + 1 - shortPeriod; longLastIndex = j + 1 - longPeriod; shortSum = correctFloat(shortSum - (yVal[shortLastIndex][high] + yVal[shortLastIndex][low]) / 2); longSum = correctFloat(longSum - (yVal[longLastIndex][high] + yVal[longLastIndex][low]) / 2); } return { values: AO, xData: xData, yData: yData }; } }); /** * An `AO` series. If the [type](#series.ao.type) * option is not specified, it is inherited from [chart.type](#chart.type). * * @extends series,plotOptions.ao * @since 7.0.0 * @product highstock * @excluding allAreas, colorAxis, dataParser, dataURL, joinBy, keys, * navigatorOptions, pointInterval, pointIntervalUnit, * pointPlacement, pointRange, pointStart, showInNavigator, stacking * @requires stock/indicators/indicators * @requires stock/indicators/ao * @apioption series.ao */ ''; // for including the above in the doclets }); _registerModule(_modules, 'mixins/multipe-lines.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) { /** * * (c) 2010-2019 Wojciech Chmiel * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var defined = U.defined; var each = H.each, merge = H.merge, error = H.error, SMA = H.seriesTypes.sma; /** * Mixin useful for all indicators that have more than one line. * Merge it with your implementation where you will provide * getValues method appropriate to your indicator and pointArrayMap, * pointValKey, linesApiNames properites. Notice that pointArrayMap * should be consistent with amount of lines calculated in getValues method. * * @private * @mixin multipleLinesMixin */ var multipleLinesMixin = { /* eslint-disable valid-jsdoc */ /** * Lines ids. Required to plot appropriate amount of lines. * Notice that pointArrayMap should have more elements than * linesApiNames, because it contains main line and additional lines ids. * Also it should be consistent with amount of lines calculated in * getValues method from your implementation. * * @private * @name multipleLinesMixin.pointArrayMap * @type {Array<string>} */ pointArrayMap: ['top', 'bottom'], /** * Main line id. * * @private * @name multipleLinesMixin.pointValKey * @type {string} */ pointValKey: 'top', /** * Additional lines DOCS names. Elements of linesApiNames array should * be consistent with DOCS line names defined in your implementation. * Notice that linesApiNames should have decreased amount of elements * relative to pointArrayMap (without pointValKey). * * @private * @name multipleLinesMixin.linesApiNames * @type {Array<string>} */ linesApiNames: ['bottomLine'], /** * Create translatedLines Collection based on pointArrayMap. * * @private * @function multipleLinesMixin.getTranslatedLinesNames * @param {string} [excludedValue] * Main line id * @return {Array<string>} * Returns translated lines names without excluded value. */ getTranslatedLinesNames: function (excludedValue) { var translatedLines = []; each(this.pointArrayMap, function (propertyName) { if (propertyName !== excludedValue) { translatedLines.push('plot' + propertyName.charAt(0).toUpperCase() + propertyName.slice(1)); } }); return translatedLines; }, /** * @private * @function multipleLinesMixin.toYData * @param {Highcharts.Point} point * Indicator point * @return {Array<number>} * Returns point Y value for all lines */ toYData: function (point) { var pointColl = []; each(this.pointArrayMap, function (propertyName) { pointColl.push(point[propertyName]); }); return pointColl; }, /** * Add lines plot pixel values. * * @private * @function multipleLinesMixin.translate * @return {void} */ translate: function () { var indicator = this, pointArrayMap = indicator.pointArrayMap, LinesNames = [], value; LinesNames = indicator.getTranslatedLinesNames(); SMA.prototype.translate.apply(indicator, arguments); each(indicator.points, function (point) { each(pointArrayMap, function (propertyName, i) { value = point[propertyName]; if (value !== null) { point[LinesNames[i]] = indicator.yAxis.toPixels(value, true); } }); }); }, /** * Draw main and additional lines. * * @private * @function multipleLinesMixin.drawGraph * @return {void} */ drawGraph: function () { var indicator = this, pointValKey = indicator.pointValKey, linesApiNames = indicator.linesApiNames, mainLinePoints = indicator.points, pointsLength = mainLinePoints.length, mainLineOptions = indicator.options, mainLinePath = indicator.graph, gappedExtend = { options: { gapSize: mainLineOptions.gapSize } }, // additional lines point place holders: secondaryLines = [], secondaryLinesNames = indicator.getTranslatedLinesNames(pointValKey), point; // Generate points for additional lines: each(secondaryLinesNames, function (plotLine, index) { // create additional lines point place holders secondaryLines[index] = []; while (pointsLength--) { point = mainLinePoints[pointsLength]; secondaryLines[index].push({ x: point.x, plotX: point.plotX, plotY: point[plotLine], isNull: !defined(point[plotLine]) }); } pointsLength = mainLinePoints.length; }); // Modify options and generate additional lines: each(linesApiNames, function (lineName, i) { if (secondaryLines[i]) { indicator.points = secondaryLines[i]; if (mainLineOptions[lineName]) { indicator.options = merge(mainLineOptions[lineName].styles, gappedExtend); } else { error('Error: "There is no ' + lineName + ' in DOCS options declared. Check if linesApiNames' + ' are consistent with your DOCS line names."' + ' at mixin/multiple-line.js:34'); } indicator.graph = indicator['graph' + lineName]; SMA.prototype.drawGraph.call(indicator); // Now save lines: indicator['graph' + lineName] = indicator.graph; } else { error('Error: "' + lineName + ' doesn\'t have equivalent ' + 'in pointArrayMap. To many elements in linesApiNames ' + 'relative to pointArrayMap."'); } }); // Restore options and draw a main line: indicator.points = mainLinePoints; indicator.options = mainLineOptions; indicator.graph = mainLinePath; SMA.prototype.drawGraph.call(indicator); } }; return multipleLinesMixin; }); _registerModule(_modules, 'indicators/aroon.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['mixins/multipe-lines.js']], function (H, U, multipleLinesMixin) { /* * * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var pick = U.pick; /* eslint-disable valid-jsdoc */ // Utils // Index of element with extreme value from array (min or max) /** * @private */ function getExtremeIndexInArray(arr, extreme) { var extremeValue = arr[0], valueIndex = 0, i; for (i = 1; i < arr.length; i++) { if (extreme === 'max' && arr[i] >= extremeValue || extreme === 'min' && arr[i] <= extremeValue) { extremeValue = arr[i]; valueIndex = i; } } return valueIndex; } /* eslint-enable valid-jsdoc */ /** * The Aroon series type. * * @private * @class * @name Highcharts.seriesTypes.aroon * * @augments Highcharts.Series */ H.seriesType('aroon', 'sma', /** * Aroon. This series requires the `linkedTo` option to be * set and should be loaded after the `stock/indicators/indicators.js`. * * @sample {highstock} stock/indicators/aroon * Aroon * * @extends plotOptions.sma * @since 7.0.0 * @product highstock * @excluding allAreas, colorAxis, compare, compareBase, joinBy, keys, * navigatorOptions, pointInterval, pointIntervalUnit, * pointPlacement, pointRange, pointStart, showInNavigator, * stacking * @requires stock/indicators/indicators * @requires stock/indicators/aroon * @optionparent plotOptions.aroon */ { /** * Paramters used in calculation of aroon series points. * * @excluding periods, index */ params: { /** * Period for Aroon indicator */ period: 25 }, marker: { enabled: false }, tooltip: { pointFormat: '<span style="color:{point.color}">\u25CF</span><b> {series.name}</b><br/>Aroon Up: {point.y}<br/>Aroon Down: {point.aroonDown}<br/>' }, /** * aroonDown line options. */ aroonDown: { /** * Styles for an aroonDown line. */ styles: { /** * Pixel width of the line. */ lineWidth: 1, /** * Color of the line. If not set, it's inherited from * [plotOptions.aroon.color](#plotOptions.aroon.color). * * @type {Highcharts.ColorString} */ lineColor: void 0 } }, dataGrouping: { approximation: 'averages' } }, /** * @lends Highcharts.Series# */ H.merge(multipleLinesMixin, { nameBase: 'Aroon', pointArrayMap: ['y', 'aroonDown'], pointValKey: 'y', linesApiNames: ['aroonDown'], getValues: function (series, params) { var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, // 0- date, 1- Aroon Up, 2- Aroon Down AR = [], xData = [], yData = [], slicedY, low = 2, high = 1, aroonUp, aroonDown, xLow, xHigh, i; // For a N-period, we start from N-1 point, to calculate Nth point // That is why we later need to comprehend slice() elements list // with (+1) for (i = period - 1; i < yValLen; i++) { slicedY = yVal.slice(i - period + 1, i + 2); xLow = getExtremeIndexInArray(slicedY.map(function (elem) { return pick(elem[low], elem); }), 'min'); xHigh = getExtremeIndexInArray(slicedY.map(function (elem) { return pick(elem[high], elem); }), 'max'); aroonUp = (xHigh / period) * 100; aroonDown = (xLow / period) * 100; if (xVal[i + 1]) { AR.push([xVal[i + 1], aroonUp, aroonDown]); xData.push(xVal[i + 1]); yData.push([aroonUp, aroonDown]); } } return { values: AR, xData: xData, yData: yData }; } })); /** * A Aroon indicator. If the [type](#series.aroon.type) option is not * specified, it is inherited from [chart.type](#chart.type). * * @extends series,plotOptions.aroon * @since 7.0.0 * @product highstock * @excluding allAreas, colorAxis, compare, compareBase, dataParser, dataURL, * joinBy, keys, navigatorOptions, pointInterval, pointIntervalUnit, * pointPlacement, pointRange, pointStart, showInNavigator, stacking * @requires stock/indicators/indicators * @requires stock/indicators/aroon * @apioption series.aroon */ ''; // to avoid removal of the above jsdoc }); _registerModule(_modules, 'indicators/aroon-oscillator.src.js', [_modules['parts/Globals.js'], _modules['mixins/multipe-lines.js'], _modules['mixins/indicator-required.js']], function (H, multipleLinesMixin, requiredIndicatorMixin) { /* * * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ var AROON = H.seriesTypes.aroon, requiredIndicator = requiredIndicatorMixin; /** * The Aroon Oscillator series type. * * @private * @class * @name Highcharts.seriesTypes.aroonoscillator * * @augments Highcharts.Series */ H.seriesType('aroonoscillator', 'aroon', /** * Aroon Oscillator. This series requires the `linkedTo` option to be set * and should be loaded after the `stock/indicators/indicators.js` and * `stock/indicators/aroon.js`. * * @sample {highstock} stock/indicators/aroon-oscillator * Aroon Oscillator * * @extends plotOptions.aroon * @since 7.0.0 * @product highstock * @excluding allAreas, aroonDown, colorAxis, compare, compareBase, * joinBy, keys, navigatorOptions, pointInterval, * pointIntervalUnit, pointPlacement, pointRange, pointStart, * showInNavigator, stacking * @requires stock/indicators/indicators * @requires stock/indicators/aroon * @requires stock/indicators/aroon-oscillator * @optionparent plotOptions.aroonoscillator */ { /** * Paramters used in calculation of aroon oscillator series points. * * @excluding periods, index */ params: { /** * Period for Aroon Oscillator * * @since 7.0.0 * @product highstock */ period: 25 }, tooltip: { pointFormat: '<span style="color:{point.color}">\u25CF</span><b> {series.name}</b>: {point.y}' } }, /** * @lends Highcharts.Series# */ H.merge(multipleLinesMixin, { nameBase: 'Aroon Oscillator', pointArrayMap: ['y'], pointValKey: 'y', linesApiNames: [], init: function () { var args = arguments, ctx = this; requiredIndicator.isParentLoaded(AROON, 'aroon', ctx.type, function (indicator) { indicator.prototype.init.apply(ctx, args); return; }); }, getValues: function (series, params) { // 0- date, 1- Aroon Oscillator var ARO = [], xData = [], yData = [], aroon, aroonUp, aroonDown, oscillator, i; aroon = AROON.prototype.getValues.call(this, series, params); for (i = 0; i < aroon.yData.length; i++) { aroonUp = aroon.yData[i][0]; aroonDown = aroon.yData[i][1]; oscillator = aroonUp - aroonDown; ARO.push([aroon.xData[i], oscillator]); xData.push(aroon.xData[i]); yData.push(oscillator); } return { values: ARO, xData: xData, yData: yData }; } })); /** * An `Aroon Oscillator` series. If the [type](#series.aroonoscillator.type) * option is not specified, it is inherited from [chart.type](#chart.type). * * @extends series,plotOptions.aroonoscillator * @since 7.0.0 * @product highstock * @excluding allAreas, aroonDown, colorAxis, compare, compareBase, dataParser, * dataURL, joinBy, keys, navigatorOptions, pointInterval, * pointIntervalUnit, pointPlacement, pointRange, pointStart, * showInNavigator, stacking * @requires stock/indicators/indicators * @requires stock/indicators/aroon