highcharts
Version:
JavaScript charting framework
1,397 lines (1,366 loc) • 50 kB
JavaScript
// 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 };