UNPKG

highcharts

Version:
1,397 lines (1,366 loc) 50 kB
// SPDX-License-Identifier: LicenseRef-Highcharts /** * @license Highstock JS v13.0.1 (2026-08-17) * @module highcharts/indicators/indicators * @requires highcharts * @requires highcharts/modules/stock * * Indicator series type for Highcharts Stock * * (c) 2010-2026 Highsoft AS * Author: Paweł Fus, Sebastian Bochan * * A commercial license may be required depending on use, * see www.highcharts.com/license */ import * as __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__ from "../highcharts.src.js"; /******/ // The require scope /******/ const __webpack_require__ = {}; /******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = (module) => { /******/ const getter = module && module.__esModule ? /******/ () => (module['default']) : /******/ () => (module); /******/ __webpack_require__.d(getter, { a: getter }); /******/ return getter; /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter/value functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ if(Array.isArray(definition)) { /******/ var i = 0; /******/ while(i < definition.length) { /******/ var key = definition[i++]; /******/ var binding = definition[i++]; /******/ if(!__webpack_require__.o(exports, key)) { /******/ if(binding === 0) { /******/ Object.defineProperty(exports, key, { enumerable: true, value: definition[i++] }); /******/ } else { /******/ Object.defineProperty(exports, key, { enumerable: true, get: binding }); /******/ } /******/ } else if(binding === 0) { i++; } /******/ } /******/ } else { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /************************************************************************/ ;// external ["../highcharts.src.js","default"] const external_highcharts_src_js_default_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"]; var external_highcharts_src_js_default_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_namespaceObject); ;// external ["../highcharts.src.js","default","Chart"] const external_highcharts_src_js_default_Chart_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"].Chart; var external_highcharts_src_js_default_Chart_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_Chart_namespaceObject); ;// ./code/es-modules/Data/ColumnUtils.js /* * * * (c) 2020-2026 Highsoft AS * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * Authors: * - Dawid Draguła * * */ /* * * * Functions * * */ /** * Sets the length of the column array. * * @param {DataTableColumn} column * Column to be modified. * * @param {number} length * New length of the column. * * @param {boolean} asSubarray * If column is a typed array, return a subarray instead of a new array. It * is faster `O(1)`, but the entire buffer will be kept in memory until all * views of it are destroyed. Default is `false`. * * @return {DataTableColumn} * Modified column. * * @private */ function setLength(column, length, asSubarray) { if (Array.isArray(column)) { column.length = length; return column; } return column[asSubarray ? 'subarray' : 'slice'](0, length); } /** * Splices a column array. * * @param {DataTableColumn} column * Column to be modified. * * @param {number} start * Index at which to start changing the array. * * @param {number} deleteCount * An integer indicating the number of old array elements to remove. * * @param {boolean} removedAsSubarray * If column is a typed array, return a subarray instead of a new array. It * is faster `O(1)`, but the entire buffer will be kept in memory until all * views to it are destroyed. Default is `true`. * * @param {Array<number>|TypedArray} items * The elements to add to the array, beginning at the start index. If you * don't specify any elements, `splice()` will only remove elements from the * array. * * @return {SpliceResult} * Object containing removed elements and the modified column. * * @private */ function splice(column, start, deleteCount, removedAsSubarray, items = []) { if (Array.isArray(column)) { if (!Array.isArray(items)) { items = Array.from(items); } return { removed: column.splice(start, deleteCount, ...items), array: column }; } const Constructor = Object.getPrototypeOf(column) .constructor; const removed = column[removedAsSubarray ? 'subarray' : 'slice'](start, start + deleteCount); const newLength = column.length - deleteCount + items.length; const result = new Constructor(newLength); result.set(column.subarray(0, start), 0); result.set(items, start); result.set(column.subarray(start + deleteCount), start + items.length); return { removed: removed, array: result }; } /** * Converts a cell value to a number. * * @param {DataTableCellType} value * Cell value to convert to a number. * * @param {boolean} useNaN * If `true`, returns `NaN` for non-numeric values; if `false`, * returns `null` instead. * * @return {number | null} * Number or `null` if the value is not a number. * * @private */ function convertToNumber(value, useNaN) { switch (typeof value) { case 'boolean': return (value ? 1 : 0); case 'number': return (isNaN(value) && !useNaN ? null : value); default: value = parseFloat(`${value ?? ''}`); return (isNaN(value) && !useNaN ? null : value); } } /* * * * Default Export * * */ const ColumnUtils = { convertToNumber, setLength, splice }; /* harmony default export */ const Data_ColumnUtils = (ColumnUtils); ;// ./code/es-modules/Data/DataTableCore.js /* * * * (c) 2009-2026 Highsoft AS * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * Authors: * - Sophie Bremer * - Gøran Slettemark * - Torstein Hønsi * * */ const { setLength: DataTableCore_setLength, splice: DataTableCore_splice } = Data_ColumnUtils; /* * * * Class * * */ /** * Class to manage columns and rows in a table structure. It provides methods * to add, remove, and manipulate columns and rows, as well as to retrieve data * from specific cells. * * Highcharts allows passing a `DataTable` or a configuration object for a data * table in the `dataTable` property, either chart-level * [dataTable](https://api.highcharts.com/highcharts/dataTable) or as * [series.dataTable](https://api.highcharts.com/highcharts/series.dataTable). * The `DataTable` is then used as a source for the series data points, mapped * by the `series.dataMapping` option. * * After chart instantiation, the data table can be accessed from the series as * `series.dataTable`. CRUD operations on the data table will be reflected in * the chart. * * @example * const dataTable = new Highcharts.DataTable({ * columns: { * year: [2020, 2021, 2022, 2023], * cost: [11, 13, 12, 14], * revenue: [12, 15, 14, 18] * } * }); * * @class * @name Highcharts.DataTable * * @param {Highcharts.DataTableOptionsObject} [options] * Options to initialize the new DataTable instance. */ class DataTableCore { constructor(options = {}) { this.isDataTable = true; this.autoId = !options.id; this.columns = {}; this.id = (options.id || (0,external_highcharts_src_js_default_namespaceObject.uniqueKey)()); this.rowCount = 0; this.versionTag = (0,external_highcharts_src_js_default_namespaceObject.uniqueKey)(); let rowCount = 0; (0,external_highcharts_src_js_default_namespaceObject.objectEach)(options.columns || {}, (column, columnId) => { this.columns[columnId] = column.slice(); rowCount = Math.max(rowCount, column.length); }); this.applyRowCount(rowCount); } /* * * * Functions * * */ /** * Applies a row count to the table by setting the `rowCount` property and * adjusting the length of all columns. * * @private * @param {number} rowCount The new row count. */ applyRowCount(rowCount) { this.rowCount = rowCount; (0,external_highcharts_src_js_default_namespaceObject.objectEach)(this.columns, (column, columnId) => { if (column.length !== rowCount) { this.columns[columnId] = DataTableCore_setLength(column, rowCount); } }); } /** * Delete rows. Simplified version of the full * `DataTable.deleteRows` method. * * @sample highcharts/datatable/live-chart/ * Add and delete rows in a live chart * @sample highcharts/datatable/shared-with-grid/ * Chart with data table CRUD operations * * @function Highcharts.DataTable#deleteRows * * @param {number} rowIndex * The start row index * * @param {number} [rowCount=1] * The number of rows to delete * * @return {void} * * @emits #afterDeleteRows */ deleteRows(rowIndex, rowCount = 1) { if (rowCount > 0 && rowIndex < this.rowCount) { let length = 0; (0,external_highcharts_src_js_default_namespaceObject.objectEach)(this.columns, (column, columnId) => { this.columns[columnId] = DataTableCore_splice(column, rowIndex, rowCount).array; length = column.length; }); this.rowCount = length; } (0,external_highcharts_src_js_default_namespaceObject.fireEvent)(this, 'afterDeleteRows', { rowIndex, rowCount }); this.versionTag = (0,external_highcharts_src_js_default_namespaceObject.uniqueKey)(); } /** * Fetches the given column by the canonical column ID. Simplified version * of the full `DataTable.getRow` method, always returning by reference. * * @function Highcharts.DataTable#getColumn * * @param {string} columnId * ID of the column to get. * * @return {Highcharts.DataTableColumn|undefined} * A copy of the column, or `undefined` if not found. */ getColumn(columnId, // eslint-disable-next-line @typescript-eslint/no-unused-vars asReference) { return this.columns[columnId]; } /** * Retrieves all or the given columns. Simplified version of the full * `DataTable.getColumns` method, always returning by reference. * * @function Highcharts.DataTable#getColumns * * @param {Array<string>} [columnIds] * Column ids to retrieve. * * @return {Highcharts.DataTableColumnCollection} * Collection of columns. If a requested column was not found, it is * `undefined`. */ getColumns(columnIds, // eslint-disable-next-line @typescript-eslint/no-unused-vars asReference) { return (columnIds || Object.keys(this.columns)).reduce((columns, columnId) => { columns[columnId] = this.columns[columnId]; return columns; }, {}); } /** * Retrieves the row at a given index. * * @function Highcharts.DataTable#getRowObject * * @param {number} rowIndex * Row index to retrieve. First row has index 0. * * @param {Array<string>} [columnNames] * Column names to retrieve. * * @return {Record<string, number|string|undefined>|undefined} * Returns the row values, or `undefined` if not found. */ getRowObject(rowIndex, columnNames) { const row = {}, columns = this.columns; columnNames ?? (columnNames = Object.keys(this.columns)); for (const columnName of columnNames) { row[columnName] = columns[columnName]?.[rowIndex]; } return row; } /** * Sets cell values for a column. Will insert a new column, if not found. * * @function Highcharts.DataTable#setColumn * * @param {string} columnId * Column name to set. * * @param {Highcharts.DataTableColumn} [column] * Values to set in the column. * * @param {number} [rowIndex] * Index of the first row to change. (Default: 0) * * @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail] * Custom information for pending events. * * @emits #setColumns * @emits #afterSetColumns */ setColumn(columnId, column = [], rowIndex = 0, eventDetail) { this.setColumns({ [columnId]: column }, rowIndex, eventDetail); } /** * Sets cell values for multiple columns. Will insert new columns, if not * found. Simplified version of the full `DataTable.setColumns`, limited * to full replacement of the columns (undefined `rowIndex`). * * @sample highcharts/datatable/shared-with-grid/ * Chart with data table CRUD operations * * @function Highcharts.DataTable#setColumns * * @param {Highcharts.DataTableColumnCollection} columns * Columns as a collection, where the keys are the column names. * * @param {number} [rowIndex] * Index of the first row to change. Ignored in the simplified `DataTable`, * as it always replaces the full column. * * @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail] * Custom information for pending events. * * @emits #setColumns * @emits #afterSetColumns */ setColumns(columns, rowIndex, eventDetail) { let rowCount = this.rowCount; (0,external_highcharts_src_js_default_namespaceObject.objectEach)(columns, (column, columnId) => { this.columns[columnId] = column.slice(); rowCount = column.length; }); this.applyRowCount(rowCount); if (!eventDetail?.silent) { (0,external_highcharts_src_js_default_namespaceObject.fireEvent)(this, 'afterSetColumns'); this.versionTag = (0,external_highcharts_src_js_default_namespaceObject.uniqueKey)(); } } /** * Sets cell values of a row. Will insert a new row if no index was * provided, or if the index is higher than the total number of table rows. * A simplified version of the full `DateTable.setRow`, limited to objects. * * @sample highcharts/datatable/live-chart/ * Add and delete rows in a live chart * @sample stock/datatable/live-candlestick/ * Live candlestick * @sample highcharts/datatable/shared-with-grid/ * Chart with data table CRUD operations * * @function Highcharts.DataTable#setRow * * @param {Record<string, number|string|undefined>} row * Cell values to set. * * @param {number} [rowIndex] * Index of the row to set. Leave `undefined` to add as a new row. * * @param {boolean} [insert] * Whether to insert the row at the given index, or to overwrite the row. * * @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail] * Custom information for pending events. * * @emits #afterSetRows */ setRow(row, rowIndex = this.rowCount, insert, eventDetail) { var _a; const { columns } = this, indexRowCount = insert ? this.rowCount + 1 : rowIndex + 1, rowKeys = Object.keys(row); if (eventDetail?.addColumns !== false) { for (let i = 0, iEnd = rowKeys.length; i < iEnd; i++) { columns[_a = rowKeys[i]] || (columns[_a] = new Array(this.rowCount)); } } (0,external_highcharts_src_js_default_namespaceObject.objectEach)(columns, (column, columnId) => { if (column) { if (insert) { column = DataTableCore_splice(column, rowIndex, 0, true, [row[columnId]]).array; } else { column[rowIndex] = // Preserve explicit null and undefined but fall back // to existing value if the new row does not have the // key columnId in row ? row[columnId] : column[rowIndex]; } columns[columnId] = column; } }); this.applyRowCount(Math.max(indexRowCount, this.rowCount)); if (!eventDetail?.silent) { (0,external_highcharts_src_js_default_namespaceObject.fireEvent)(this, 'afterSetRows', { rowIndex }); this.versionTag = (0,external_highcharts_src_js_default_namespaceObject.uniqueKey)(); } } /** * Returns the modified (clone) or the original data table if the modified * one does not exist. * * @return {Highcharts.DataTable} * The modified (clone) or the original data table. */ getModified() { return this.modified || this; } } /* * * * Default Export * * */ /* harmony default export */ const Data_DataTableCore = (DataTableCore); /* * * * API Declarations * * */ /** * A collection of data table columns defined by a object where the key is the * column ID and the value is an array of the column values. Typed arrays are * supported. * * @type {Highcharts.DataTableColumnCollection|undefined} * @apioption dataTable.columns */ /** * Custom ID to identify the new DataTable instance. * * @type {string|undefined} * @apioption dataTable.id */ /** * A typed array. * @typedef {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} Highcharts.TypedArray */ /** * A column of values in a data table. * @typedef {Array<boolean|null|number|string|undefined>|Highcharts.TypedArray} Highcharts.DataTableColumn */ /** * A collection of data table columns defined by a object where the key is the * column ID and the value is an array of the column values. Typed arrays are * supported. * @typedef {Record<string, Highcharts.DataTableColumn>} Highcharts.DataTableColumnCollection */ /** * Options for the `DataTable` or `DataTableCore` classes. * @interface Highcharts.DataTableOptionsObject */ /** * The column options for the data table. The columns are defined by an object * where the key is the column ID and the value is an array of the column * values. * * @name Highcharts.DataTableOptionsObject.columns * @type {Highcharts.DataTableColumnCollection|undefined} */ /** * Custom ID to identify the new DataTable instance. * * @name Highcharts.DataTableOptionsObject.id * @type {string|undefined} */ (''); // Keeps doclets above in JS file ;// external ["../highcharts.src.js","default","SeriesRegistry"] const external_highcharts_src_js_default_SeriesRegistry_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"].SeriesRegistry; var external_highcharts_src_js_default_SeriesRegistry_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_SeriesRegistry_namespaceObject); ;// ./code/es-modules/Stock/Indicators/SMA/SMAIndicator.js /* * * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ const { line: LineSeries } = (external_highcharts_src_js_default_SeriesRegistry_default()).seriesTypes; /** * * Return the parent series values in the legacy two-dimensional yData * format * @internal */ const tableToMultiYData = (series, processed) => { const yData = [], pointArrayMap = series.pointArrayMap, table = processed && series.dataTable.getModified() || series.dataTable; if (!pointArrayMap) { return series.getColumn('y', processed); } const columns = pointArrayMap.map((key) => series.getColumn(key, processed)); for (let i = 0; i < table.rowCount; i++) { const values = pointArrayMap.map((key, colIndex) => columns[colIndex]?.[i] || 0); yData.push(values); } return yData; }; /* * * * Class * * */ /** * The SMA series type. * * @internal */ class SMAIndicator extends LineSeries { /* * * * Functions * * */ /** @internal */ destroy() { this.dataEventsToUnbind.forEach(function (unbinder) { unbinder(); }); super.destroy.apply(this, arguments); } /** @internal */ getName() { const params = []; let name = this.name; if (!name) { (this.nameComponents || []).forEach(function (component, index) { params.push(this.options.params[component] + (0,external_highcharts_src_js_default_namespaceObject.pick)(this.nameSuffixes[index], '')); }, this); name = (this.nameBase || this.type.toUpperCase()) + (this.nameComponents ? ' (' + params.join(', ') + ')' : ''); } return name; } /** @internal */ getValues(series, params) { const period = params.period, xVal = series.xData || [], yVal = series.yData, yValLen = yVal.length, SMA = [], xData = [], yData = []; let i, index = -1, range = 0, SMAPoint, sum = 0; if (xVal.length < period) { return; } // Switch index for OHLC / Candlestick / Arearange if ((0,external_highcharts_src_js_default_namespaceObject.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 }; } /** @internal */ init(chart, options) { const indicator = this; super.init.call(indicator, chart, options); // Only after series are linked indicator can be processed. const linkedSeriesUnbiner = (0,external_highcharts_src_js_default_namespaceObject.addEvent)((external_highcharts_src_js_default_Chart_default()), 'afterLinkSeries', function ({ isUpdating }) { // #18643 indicator shouldn't recalculate // values while series updating. if (isUpdating) { return; } const hasEvents = !!indicator.dataEventsToUnbind.length; if (indicator.linkedParent) { if (!hasEvents) { // No matter which indicator, always recalculate after // updating the data. indicator.dataEventsToUnbind.push((0,external_highcharts_src_js_default_namespaceObject.addEvent)(indicator.linkedParent, 'updatedData', function () { indicator.recalculateValues(); })); // Some indicators (like VBP) requires an additional // event (afterSetExtremes) to properly show the data. if (indicator.calculateOn.xAxis) { indicator.dataEventsToUnbind.push((0,external_highcharts_src_js_default_namespaceObject.addEvent)(indicator.linkedParent.xAxis, indicator.calculateOn.xAxis, function () { indicator.recalculateValues(); })); } } // Most indicators are being calculated on chart's init. if (indicator.calculateOn.chart === 'init') { // When closestPointRange is set, it is an indication // that `Series.processData` has run. If it hasn't we // need to `recalculateValues`. if (!indicator.closestPointRange) { indicator.recalculateValues(); } } else if (!hasEvents) { // Some indicators (like VBP) has to recalculate their // values after other chart's events (render). const unbinder = (0,external_highcharts_src_js_default_namespaceObject.addEvent)(indicator.chart, indicator.calculateOn.chart, function () { indicator.recalculateValues(); // Call this just once. unbinder(); }); } } else { return (0,external_highcharts_src_js_default_namespaceObject.error)('Series ' + indicator.options.linkedTo + ' not found! Check `linkedTo`.', false, chart); } }, { order: 0 }); // Make sure we find series which is a base for an indicator // chart.linkSeries(); indicator.dataEventsToUnbind = []; indicator.eventsToUnbind.push(linkedSeriesUnbiner); } /** @internal */ recalculateValues() { const indicator = this, table = this.dataTable, oldDataLength = indicator.dataTable.rowCount, emptySet = { values: [], xData: [], yData: [] }; let overwriteData = true, min, max; // For the newer data table, temporarily set the parent series `yData` // to the legacy format that is documented for custom indicators, and // get the xData from the data table const yData = indicator.linkedParent.yData, processedYData = indicator.linkedParent.processedYData; indicator.linkedParent.xData = indicator.linkedParent .getColumn('x'); indicator.linkedParent.yData = tableToMultiYData(indicator.linkedParent); indicator.linkedParent.processedYData = tableToMultiYData(indicator.linkedParent, true); // Updating an indicator with redraw=false may destroy data. // If there will be a following update for the parent series, // we will try to access Series object without any properties // (except for prototyped ones). This is what happens // for example when using Axis.setDataGrouping(). See #16670 const processedData = indicator.linkedParent.options && // #18176, #18177 indicators should work with empty dataset indicator.linkedParent.dataTable.rowCount ? (indicator.getValues(indicator.linkedParent, indicator.options.params) || emptySet) : emptySet; processedData.xData.length = processedData.values.length; // Reset delete indicator.linkedParent.xData; indicator.linkedParent.yData = yData; indicator.linkedParent.processedYData = processedYData; const pointArrayMap = indicator.pointArrayMap || ['y'], valueColumns = {}; // Split legacy two-dimensional values into value columns processedData.yData .forEach((values) => { pointArrayMap.forEach((key, index) => { const column = valueColumns[key] || []; column.push((0,external_highcharts_src_js_default_namespaceObject.isArray)(values) ? values[index] : values); if (!valueColumns[key]) { valueColumns[key] = column; } }); }); // 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 available points (#9493) if (indicator.cropped) { if (indicator.xAxis) { min = indicator.xAxis.min; max = indicator.xAxis.max; } const croppedData = indicator.cropData(table, min, max); indicator.setData(croppedData.modified, false); } else if (indicator.updateAllPoints || // #18710 // Omit addPoint() and removePoint() cases processedData.xData.length !== oldDataLength - 1 && processedData.xData.length !== oldDataLength + 1) { overwriteData = false; this.setData(new Data_DataTableCore({ columns: { x: processedData.xData, ...valueColumns } }), false); } } if (overwriteData) { const columns = valueColumns; columns.x = processedData.xData; // Add the processedData.values to the data table processedData.values.reduce((columns, val, i) => { Object.keys(val).forEach((key) => { if (!columns[key]) { columns[key] = []; } columns[key][i] = val[key]; }); return columns; }, columns); table.setColumns(columns); delete indicator.xColumn; delete indicator.xColumnIsNumbers; } if (indicator.calculateOn.xAxis && indicator.getColumn('x', true).length) { indicator.isDirty = true; indicator.redraw(); } indicator.isDirtyData = !!indicator.linkedSeries.length; (0,external_highcharts_src_js_default_namespaceObject.fireEvent)(indicator, 'updatedData'); // #18689 } /** @internal */ processData() { const series = this, compareToMain = series.options.compareToMain, linkedParent = series.linkedParent; super.processData.apply(series, arguments); if (series.dataModify && linkedParent && linkedParent.dataModify && linkedParent.dataModify.compareValue && compareToMain) { series.dataModify.compareValue = linkedParent.dataModify.compareValue; } return; } } /* * * * Static Properties * * */ /** * Simple moving average indicator (SMA). This series requires `linkedTo` * option to be set. * * @sample {highstock} 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 */ SMAIndicator.defaultOptions = (0,external_highcharts_src_js_default_namespaceObject.merge)(LineSeries.defaultOptions, { /** * 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, /** * Parameters 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: 3, /** * The base period for indicator calculations. This is the number of * data points which are taken into account for the indicator * calculations. */ period: 14 } }); (0,external_highcharts_src_js_default_namespaceObject.extend)(SMAIndicator.prototype, { calculateOn: { chart: 'init' }, hasDerivedData: true, nameComponents: ['period'], nameSuffixes: [], // E.g. Zig Zag uses extra '%'' in the legend name useCommonDataGrouping: true }); external_highcharts_src_js_default_SeriesRegistry_default().registerSeriesType('sma', SMAIndicator); /* * * * Default Export * * */ /** @internal */ /* harmony default export */ const SMA_SMAIndicator = ((/* unused pure expression or super */ null && (SMAIndicator))); /* * * * API Options * * */ /** * 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 ;// ./code/es-modules/Stock/Indicators/EMA/EMAIndicator.js /* * * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ const { sma: EMAIndicator_SMAIndicator } = (external_highcharts_src_js_default_SeriesRegistry_default()).seriesTypes; /* * * * Class * * */ /** * The EMA series type. * * @internal * @class * @name Highcharts.seriesTypes.ema * * @augments Highcharts.Series */ class EMAIndicator extends EMAIndicator_SMAIndicator { /* * * * Functions * * */ accumulatePeriodPoints(period, index, yVal) { let sum = 0, i = 0, y = 0; while (i < period) { y = index < 0 ? yVal[i] : yVal[i][index]; sum = sum + y; i++; } return sum; } calculateEma(xVal, yVal, i, EMApercent, calEMA, index, SMA) { const x = xVal[i - 1], yValue = index < 0 ? yVal[i - 1] : yVal[i - 1][index], y = typeof calEMA === 'undefined' ? SMA : (0,external_highcharts_src_js_default_namespaceObject.correctFloat)((yValue * EMApercent) + (calEMA * (1 - EMApercent))); return [x, y]; } getValues(series, params) { const period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, EMApercent = 2 / (period + 1), EMA = [], xData = [], yData = []; let calEMA, EMAPoint, i, index = -1, sum = 0, SMA = 0; // Check period, if bigger than points length, skip if (yValLen < period) { return; } // Switch index for OHLC / Candlestick / Arearange if ((0,external_highcharts_src_js_default_namespaceObject.isArray)(yVal[0])) { index = params.index ? params.index : 0; } // Accumulate first N-points sum = this.accumulatePeriodPoints(period, index, yVal); // First point SMA = sum / period; // Calculate value one-by-one for each period in visible data for (i = period; i < yValLen + 1; i++) { EMAPoint = this.calculateEma(xVal, yVal, i, EMApercent, calEMA, index, SMA); EMA.push(EMAPoint); xData.push(EMAPoint[0]); yData.push(EMAPoint[1]); calEMA = EMAPoint[1]; } return { values: EMA, xData: xData, yData: yData }; } } /* * * * Static Properties * * */ /** * Exponential moving average indicator (EMA). This series requires the * `linkedTo` option to be set. * * @sample {highstock} stock/indicators/ema * Exponential moving average indicator * * @extends plotOptions.sma * @since 6.0.0 * @product highstock * @requires stock/indicators/indicators * @optionparent plotOptions.ema */ EMAIndicator.defaultOptions = (0,external_highcharts_src_js_default_namespaceObject.merge)(EMAIndicator_SMAIndicator.defaultOptions, { 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. * * By default index value used to be set to 0. Since * Highcharts Stock 7 by default index is set to 3 * which means that the ema indicator will be * calculated using Close values. */ index: 3, period: 9 // @merge 14 in v6.2 } }); external_highcharts_src_js_default_SeriesRegistry_default().registerSeriesType('ema', EMAIndicator); /* * * * Default Export * * */ /** @internal */ /* harmony default export */ const EMA_EMAIndicator = ((/* unused pure expression or super */ null && (EMAIndicator))); /* * * * API Options * * */ /** * A `EMA` series. If the [type](#series.ema.type) option is not * specified, it is inherited from [chart.type](#chart.type). * * @extends series,plotOptions.ema * @since 6.0.0 * @product highstock * @excluding dataParser, dataURL * @requires stock/indicators/indicators * @apioption series.ema */ ''; // Adds doclet above to the transpiled file ;// ./code/es-modules/Stock/Indicators/MultipleLinesComposition.js /* * * * (c) 2010-2026 Highsoft AS * Author: Wojciech Chmiel * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ const { sma: { prototype: smaProto } } = (external_highcharts_src_js_default_SeriesRegistry_default()).seriesTypes; /* * * * Composition * * */ var MultipleLinesComposition; (function (MultipleLinesComposition) { /* * * * Declarations * * */ /* * * * Constants * * */ /** * 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). * * @internal * @type {Array<string>} */ const linesApiNames = ['bottomLine']; /** * 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. * * @internal * @type {Array<string>} */ const pointArrayMap = ['top', 'bottom']; /** * Names of the lines, between which the area should be plotted. * If the drawing of the area should * be disabled for some indicators, leave this option as an empty array. * Names should be the same as the names in the pointArrayMap. * * @internal * @type {Array<string>} */ const areaLinesNames = ['top']; /** * Main line id. * * @internal * @type {string} */ const pointValKey = 'top'; /* * * * Functions * * */ /** * Composition useful for all indicators that have more than one line. * Compose it with your implementation where you will provide the * `getValues` method appropriate to your indicator and `pointArrayMap`, * `pointValKey`, `linesApiNames` properties. Notice that `pointArrayMap` * should be consistent with the amount of lines calculated in the * `getValues` method. * * @internal */ function compose(IndicatorClass) { const proto = IndicatorClass.prototype; proto.linesApiNames = (proto.linesApiNames || linesApiNames.slice()); proto.pointArrayMap = (proto.pointArrayMap || pointArrayMap.slice()); proto.pointValKey = (proto.pointValKey || pointValKey); proto.areaLinesNames = (proto.areaLinesNames || areaLinesNames.slice()); proto.drawGraph = indicatorDrawGraph; proto.getGraphPath = indicatorGetGraphPath; proto.toYData = indicatorToYData; proto.translate = indicatorTranslate; return IndicatorClass; } MultipleLinesComposition.compose = compose; /** * Generate the API name of the line * * @internal * @param {string} propertyName name of the line */ function getLineName(propertyName) { return ('plot' + propertyName.charAt(0).toUpperCase() + propertyName.slice(1)); } /** * Create translatedLines Collection based on pointArrayMap. * * @internal * @param {SMAIndicator} indicator * @param {string} [excludedValue] * Main line id * @return {Array<string>} * Returns translated lines names without excluded value. */ function getTranslatedLinesNames(indicator, excludedValue) { const translatedLines = []; (indicator.pointArrayMap || []).forEach((propertyName) => { if (propertyName !== excludedValue) { translatedLines.push(getLineName(propertyName)); } }); return translatedLines; } /** * Draw main and additional lines. * * @internal */ function indicatorDrawGraph() { const indicator = this, pointValKey = indicator.pointValKey, linesApiNames = indicator.linesApiNames, areaLinesNames = indicator.areaLinesNames, mainLinePoints = indicator.points, mainLineOptions = indicator.options, mainLinePath = indicator.graph, gappedExtend = { options: { gapSize: mainLineOptions.gapSize } }, // Additional lines point place holders: secondaryLines = [], secondaryLinesNames = getTranslatedLinesNames(indicator, pointValKey); let pointsLength = mainLinePoints.length, point; // Generate points for additional lines: secondaryLinesNames.forEach((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: !(0,external_highcharts_src_js_default_namespaceObject.defined)(point[plotLine]) }); } pointsLength = mainLinePoints.length; }); // Modify options and generate area fill: if (indicator.userOptions.fillColor && areaLinesNames.length) { const index = secondaryLinesNames.indexOf(getLineName(areaLinesNames[0])), secondLinePoints = secondaryLines[index], firstLinePoints = areaLinesNames.length === 1 ? mainLinePoints : secondaryLines[secondaryLinesNames.indexOf(getLineName(areaLinesNames[1]))], originalColor = indicator.color; indicator.points = firstLinePoints; indicator.nextPoints = secondLinePoints; indicator.color = indicator.userOptions.fillColor; indicator.options = (0,external_highcharts_src_js_default_namespaceObject.merge)(mainLinePoints, gappedExtend); indicator.graph = indicator.area; indicator.fillGraph = true; smaProto.drawGraph.call(indicator); indicator.area = indicator.graph; // Clean temporary properties: delete indicator.nextPoints; delete indicator.fillGraph; indicator.color = originalColor; } // Modify options and generate additional lines: linesApiNames.forEach((lineName, i) => { if (secondaryLines[i]) { indicator.points = secondaryLines[i]; if (mainLineOptions[lineName]) { indicator.options = (0,external_highcharts_src_js_default_namespaceObject.merge)(mainLineOptions[lineName].styles, gappedExtend); } else { (0,external_highcharts_src_js_default_namespaceObject.error)('Error: "There is no ' + lineName + ' in DOCS options declared. Check if linesApiNames' + ' are consistent with your DOCS line names."'); } indicator.graph = indicator['graph' + lineName]; smaProto.drawGraph.call(indicator); // Now save lines: indicator['graph' + lineName] = indicator.graph; } else { (0,external_highcharts_src_js_default_namespaceObject.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; smaProto.drawGraph.call(indicator); } /** * Create the path based on points provided as argument. * If indicator.nextPoints option is defined, create the areaFill. * * @internal * @param {Array<LinePoint>} points Points on which the path should be created */ function indicatorGetGraphPath(points) { let areaPath, path = [], higherAreaPath = []; points = points || this.points; // Render Span if (this.fillGraph && this.nextPoints) { areaPath = smaProto.getGraphPath.call(this, this.nextPoints); if (areaPath && areaPath.length) { areaPath[0][0] = 'L'; path = smaProto.getGraphPath.call(this, points); higherAreaPath = areaPath.slice(0, path.length); // Reverse points, so that the areaFill will start from the end: for (let i = higherAreaPath.length - 1; i >= 0; i--) { path.push(higherAreaPath[i]); } } } else { path = smaProto.getGraphPath.apply(this, arguments); } return path; } /** * @internal * @param {Highcharts.Point} point * Indicator point * @return {Array<number>} * Returns point Y value for all lines */ function indicatorToYData(point) { const pointColl = []; (this.pointArrayMap || []).forEach((propertyName) => { pointColl.push(point[propertyName]); }); return pointColl; } /** * Add lines plot pixel values. * * @internal */ function indicatorTranslate() { const pointArrayMap = this.pointArrayMap; let LinesNames = [], value; LinesNames = getTranslatedLinesNames(this); smaProto.translate.apply(this, arguments); this.points.forEach((point) => { pointArrayMap.forEach((propertyName, i) => { value = point[propertyName]; // If the modifier, like for example compare exists, // modified the original value by that method, #15867. if (this.dataModify) { value = this.dataModify.modifyValue(value); } if (value !== null) { point[LinesNames[i]] = this.yAxis.toPixels(value, true); } }); }); } })(MultipleLinesComposition || (MultipleLinesComposition = {})); /* * * * Default Export * * */ /* harmony default export */ const Indicators_MultipleLinesComposition = (MultipleLinesComposition); ;// ./code/es-modules/masters/indicators/indicators.src.js const G = (external_highcharts_src_js_default_default()); G.MultipleLinesComposition = G.MultipleLinesComposition || Indicators_MultipleLinesComposition; /* harmony default export */ const indicators_src = ((external_highcharts_src_js_default_default())); export { indicators_src as default };