chore: version inicial del codigo fuente del LMS Estafeta
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, merge = U.merge, seriesType = U.seriesType;
|
||||
import multipleLinesMixin from '../../Mixins/MultipleLines.js';
|
||||
var SMA = H.seriesTypes.sma;
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getBaseForBand(low, high, factor) {
|
||||
return (((correctFloat(high - low)) /
|
||||
((correctFloat(high + low)) / 2)) * 1000) * factor;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getPointUB(high, base) {
|
||||
return high * (correctFloat(1 + 2 * base));
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getPointLB(low, base) {
|
||||
return low * (correctFloat(1 - 2 * base));
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* The ABands series type
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.abands
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('abands', 'sma',
|
||||
/**
|
||||
* Acceleration bands (ABANDS). This series requires the `linkedTo` option
|
||||
* to be set and should be loaded after the
|
||||
* `stock/indicators/indicators.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/acceleration-bands
|
||||
* Acceleration Bands
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @mixes Highcharts.MultipleLinesMixin
|
||||
* @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/acceleration-bands
|
||||
* @optionparent plotOptions.abands
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 20,
|
||||
/**
|
||||
* The algorithms factor value used to calculate bands.
|
||||
*
|
||||
* @product highstock
|
||||
*/
|
||||
factor: 0.001,
|
||||
index: 3
|
||||
},
|
||||
lineWidth: 1,
|
||||
topLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
bottomLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
merge(multipleLinesMixin, {
|
||||
pointArrayMap: ['top', 'middle', 'bottom'],
|
||||
pointValKey: 'middle',
|
||||
nameBase: 'Acceleration Bands',
|
||||
nameComponents: ['period', 'factor'],
|
||||
linesApiNames: ['topLine', 'bottomLine'],
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, factor = params.factor, index = params.index, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0,
|
||||
// Upperbands
|
||||
UB = [],
|
||||
// Lowerbands
|
||||
LB = [],
|
||||
// ABANDS array structure:
|
||||
// 0-date, 1-top line, 2-middle line, 3-bottom line
|
||||
ABANDS = [],
|
||||
// middle line, top line and bottom line
|
||||
ML, TL, BL, date, bandBase, pointSMA, ubSMA, lbSMA, low = 2, high = 1, xData = [], yData = [], slicedX, slicedY, i;
|
||||
if (yValLen < period) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i <= yValLen; i++) {
|
||||
// Get UB and LB values of every point. This condition
|
||||
// is necessary, because there is a need to calculate current
|
||||
// UB nad LB values simultaneously with given period SMA
|
||||
// in one for loop.
|
||||
if (i < yValLen) {
|
||||
bandBase = getBaseForBand(yVal[i][low], yVal[i][high], factor);
|
||||
UB.push(getPointUB(yVal[i][high], bandBase));
|
||||
LB.push(getPointLB(yVal[i][low], bandBase));
|
||||
}
|
||||
if (i >= period) {
|
||||
slicedX = xVal.slice(i - period, i);
|
||||
slicedY = yVal.slice(i - period, i);
|
||||
ubSMA = SMA.prototype.getValues.call(this, {
|
||||
xData: slicedX,
|
||||
yData: UB.slice(i - period, i)
|
||||
}, {
|
||||
period: period
|
||||
});
|
||||
lbSMA = SMA.prototype.getValues.call(this, {
|
||||
xData: slicedX,
|
||||
yData: LB.slice(i - period, i)
|
||||
}, {
|
||||
period: period
|
||||
});
|
||||
pointSMA = SMA.prototype.getValues.call(this, {
|
||||
xData: slicedX,
|
||||
yData: slicedY
|
||||
}, {
|
||||
period: period,
|
||||
index: index
|
||||
});
|
||||
date = pointSMA.xData[0];
|
||||
TL = ubSMA.yData[0];
|
||||
BL = lbSMA.yData[0];
|
||||
ML = pointSMA.yData[0];
|
||||
ABANDS.push([date, TL, ML, BL]);
|
||||
xData.push(date);
|
||||
yData.push([TL, ML, BL]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: ABANDS,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* An Acceleration bands indicator. If the [type](#series.abands.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.abands
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, compare, compareBase, dataParser, dataURL,
|
||||
* joinBy, keys, navigatorOptions, pointInterval,
|
||||
* pointIntervalUnit, pointPlacement, pointRange, pointStart,
|
||||
* stacking, showInNavigator,
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/acceleration-bands
|
||||
* @apioption series.abands
|
||||
*/
|
||||
''; // to include the above in jsdoc
|
||||
@@ -0,0 +1,108 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var error = U.error, seriesType = U.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) {
|
||||
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
|
||||
@@ -0,0 +1,163 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, isArray = U.isArray, seriesType = U.seriesType;
|
||||
var noop = H.noop;
|
||||
/**
|
||||
* The AO series type
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.ao
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
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,
|
||||
crisp: false,
|
||||
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
|
||||
@@ -0,0 +1,130 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var error = U.error, seriesType = U.seriesType;
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
var EMA = H.seriesTypes.ema;
|
||||
/**
|
||||
* The APO series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.apo
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('apo', 'ema',
|
||||
/**
|
||||
* Absolute Price Oscillator. This series requires the `linkedTo` option to
|
||||
* be set and should be loaded after the `stock/indicators/indicators.js`
|
||||
* and `stock/indicators/ema.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/apo
|
||||
* Absolute Price Oscillator
|
||||
*
|
||||
* @extends plotOptions.ema
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, joinBy, keys, navigatorOptions,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement,
|
||||
* pointRange, pointStart, showInNavigator, stacking
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/ema
|
||||
* @requires stock/indicators/apo
|
||||
* @optionparent plotOptions.apo
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Paramters used in calculation of Absolute Price Oscillator
|
||||
* series points.
|
||||
*
|
||||
* @excluding period
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* Periods for Absolute Price Oscillator calculations.
|
||||
*
|
||||
* @type {Array<number>}
|
||||
* @default [10, 20]
|
||||
* @since 7.0.0
|
||||
*/
|
||||
periods: [10, 20]
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series.prototype
|
||||
*/
|
||||
{
|
||||
nameBase: 'APO',
|
||||
nameComponents: ['periods'],
|
||||
init: function () {
|
||||
var args = arguments, ctx = this;
|
||||
requiredIndicator.isParentLoaded(EMA, 'ema', ctx.type, function (indicator) {
|
||||
indicator.prototype.init.apply(ctx, args);
|
||||
return;
|
||||
});
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var periods = params.periods, index = params.index,
|
||||
// 0- date, 1- Absolute price oscillator
|
||||
APO = [], xData = [], yData = [], periodsOffset,
|
||||
// Shorter Period EMA
|
||||
SPE,
|
||||
// Longer Period EMA
|
||||
LPE, oscillator, i;
|
||||
// Check if periods are correct
|
||||
if (periods.length !== 2 || periods[1] <= periods[0]) {
|
||||
error('Error: "APO requires two periods. Notice, first period ' +
|
||||
'should be lower than the second one."');
|
||||
return;
|
||||
}
|
||||
SPE = EMA.prototype.getValues.call(this, series, {
|
||||
index: index,
|
||||
period: periods[0]
|
||||
});
|
||||
LPE = EMA.prototype.getValues.call(this, series, {
|
||||
index: index,
|
||||
period: periods[1]
|
||||
});
|
||||
// Check if ema is calculated properly, if not skip
|
||||
if (!SPE || !LPE) {
|
||||
return;
|
||||
}
|
||||
periodsOffset = periods[1] - periods[0];
|
||||
for (i = 0; i < LPE.yData.length; i++) {
|
||||
oscillator = (SPE.yData[i + periodsOffset] -
|
||||
LPE.yData[i]);
|
||||
APO.push([LPE.xData[i], oscillator]);
|
||||
xData.push(LPE.xData[i]);
|
||||
yData.push(oscillator);
|
||||
}
|
||||
return {
|
||||
values: APO,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* An `Absolute Price Oscillator` series. If the [type](#series.apo.type) option
|
||||
* is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.apo
|
||||
* @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/ema
|
||||
* @requires stock/indicators/apo
|
||||
* @apioption series.apo
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,118 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
var UNDEFINED;
|
||||
/* eslint-disable valid-jsdoc */
|
||||
// Utils:
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function accumulateAverage(points, xVal, yVal, i) {
|
||||
var xValue = xVal[i], yValue = yVal[i];
|
||||
points.push([xValue, yValue]);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getTR(currentPoint, prevPoint) {
|
||||
var pointY = currentPoint, prevY = prevPoint, HL = pointY[1] - pointY[2], HCp = prevY === UNDEFINED ? 0 : Math.abs(pointY[1] - prevY[3]), LCp = prevY === UNDEFINED ? 0 : Math.abs(pointY[2] - prevY[3]), TR = Math.max(HL, HCp, LCp);
|
||||
return TR;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function populateAverage(points, xVal, yVal, i, period, prevATR) {
|
||||
var x = xVal[i - 1], TR = getTR(yVal[i - 1], yVal[i - 2]), y;
|
||||
y = (((prevATR * (period - 1)) + TR) / period);
|
||||
return [x, y];
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* The ATR series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.atr
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('atr', 'sma',
|
||||
/**
|
||||
* Average true range indicator (ATR). This series requires `linkedTo`
|
||||
* option to be set.
|
||||
*
|
||||
* @sample stock/indicators/atr
|
||||
* ATR indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/atr
|
||||
* @optionparent plotOptions.atr
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 14
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, xValue = xVal[0], yValue = yVal[0], range = 1, prevATR = 0, TR = 0, ATR = [], xData = [], yData = [], point, i, points;
|
||||
points = [[xValue, yValue]];
|
||||
if ((xVal.length <= period) ||
|
||||
!isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
for (i = 1; i <= yValLen; i++) {
|
||||
accumulateAverage(points, xVal, yVal, i);
|
||||
if (period < range) {
|
||||
point = populateAverage(points, xVal, yVal, i, period, prevATR);
|
||||
prevATR = point[1];
|
||||
ATR.push(point);
|
||||
xData.push(point[0]);
|
||||
yData.push(point[1]);
|
||||
}
|
||||
else if (period === range) {
|
||||
prevATR = TR / (i - 1);
|
||||
ATR.push([xVal[i - 1], prevATR]);
|
||||
xData.push(xVal[i - 1]);
|
||||
yData.push(prevATR);
|
||||
range++;
|
||||
}
|
||||
else {
|
||||
TR += getTR(yVal[i - 1], yVal[i - 2]);
|
||||
range++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: ATR,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `ATR` series. If the [type](#series.atr.type) option is not specified, it
|
||||
* is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.atr
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/atr
|
||||
* @apioption series.atr
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,153 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var merge = U.merge, pick = U.pick, seriesType = U.seriesType;
|
||||
import multipleLinesMixin from '../../Mixins/MultipleLines.js';
|
||||
/* 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
|
||||
*/
|
||||
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#
|
||||
*/
|
||||
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
|
||||
@@ -0,0 +1,114 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import multipleLinesMixin from '../../Mixins/MultipleLines.js';
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var merge = U.merge, seriesType = U.seriesType;
|
||||
var AROON = H.seriesTypes.aroon;
|
||||
/**
|
||||
* The Aroon Oscillator series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.aroonoscillator
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
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#
|
||||
*/
|
||||
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
|
||||
* @requires stock/indicators/aroon-oscillator
|
||||
* @apioption series.aroonoscillator
|
||||
*/
|
||||
''; // adds doclet above to the transpiled file
|
||||
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, merge = U.merge, seriesType = U.seriesType;
|
||||
import multipleLinesMixin from '../../Mixins/MultipleLines.js';
|
||||
var SMA = H.seriesTypes.sma;
|
||||
/* eslint-disable valid-jsdoc */
|
||||
// Utils:
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function getStandardDeviation(arr, index, isOHLC, mean) {
|
||||
var variance = 0, arrLen = arr.length, std = 0, i = 0, value;
|
||||
for (; i < arrLen; i++) {
|
||||
value = (isOHLC ? arr[i][index] : arr[i]) - mean;
|
||||
variance += value * value;
|
||||
}
|
||||
variance = variance / (arrLen - 1);
|
||||
std = Math.sqrt(variance);
|
||||
return std;
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* Bollinger Bands series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.bb
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('bb', 'sma',
|
||||
/**
|
||||
* Bollinger bands (BB). This series requires the `linkedTo` option to be
|
||||
* set and should be loaded after the `stock/indicators/indicators.js` file.
|
||||
*
|
||||
* @sample stock/indicators/bollinger-bands
|
||||
* Bollinger bands
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/bollinger-bands
|
||||
* @optionparent plotOptions.bb
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 20,
|
||||
/**
|
||||
* Standard deviation for top and bottom bands.
|
||||
*/
|
||||
standardDeviation: 2,
|
||||
index: 3
|
||||
},
|
||||
/**
|
||||
* Bottom line options.
|
||||
*/
|
||||
bottomLine: {
|
||||
/**
|
||||
* Styles for a bottom line.
|
||||
*/
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line. If not set, it's inherited from
|
||||
* [plotOptions.bb.color](#plotOptions.bb.color).
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Top line options.
|
||||
*
|
||||
* @extends plotOptions.bb.bottomLine
|
||||
*/
|
||||
topLine: {
|
||||
styles: {
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span><b> {series.name}</b><br/>Top: {point.top}<br/>Middle: {point.middle}<br/>Bottom: {point.bottom}<br/>'
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
merge(multipleLinesMixin, {
|
||||
pointArrayMap: ['top', 'middle', 'bottom'],
|
||||
pointValKey: 'middle',
|
||||
nameComponents: ['period', 'standardDeviation'],
|
||||
linesApiNames: ['topLine', 'bottomLine'],
|
||||
init: function () {
|
||||
SMA.prototype.init.apply(this, arguments);
|
||||
// Set default color for lines:
|
||||
this.options = merge({
|
||||
topLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
},
|
||||
bottomLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
}
|
||||
}, this.options);
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, standardDeviation = params.standardDeviation, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0,
|
||||
// 0- date, 1-middle line, 2-top line, 3-bottom line
|
||||
BB = [],
|
||||
// middle line, top line and bottom line
|
||||
ML, TL, BL, date, xData = [], yData = [], slicedX, slicedY, stdDev, isOHLC, point, i;
|
||||
if (xVal.length < period) {
|
||||
return;
|
||||
}
|
||||
isOHLC = isArray(yVal[0]);
|
||||
for (i = period; i <= yValLen; i++) {
|
||||
slicedX = xVal.slice(i - period, i);
|
||||
slicedY = yVal.slice(i - period, i);
|
||||
point = SMA.prototype.getValues.call(this, {
|
||||
xData: slicedX,
|
||||
yData: slicedY
|
||||
}, params);
|
||||
date = point.xData[0];
|
||||
ML = point.yData[0];
|
||||
stdDev = getStandardDeviation(slicedY, params.index, isOHLC, ML);
|
||||
TL = ML + standardDeviation * stdDev;
|
||||
BL = ML - standardDeviation * stdDev;
|
||||
BB.push([date, TL, ML, BL]);
|
||||
xData.push(date);
|
||||
yData.push([TL, ML, BL]);
|
||||
}
|
||||
return {
|
||||
values: BB,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* A bollinger bands indicator. If the [type](#series.bb.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.bb
|
||||
* @since 6.0.0
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/bollinger-bands
|
||||
* @apioption series.bb
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,109 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
/* eslint-disable valid-jsdoc */
|
||||
// Utils:
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function sumArray(array) {
|
||||
return array.reduce(function (prev, cur) {
|
||||
return prev + cur;
|
||||
}, 0);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function meanDeviation(arr, sma) {
|
||||
var len = arr.length, sum = 0, i;
|
||||
for (i = 0; i < len; i++) {
|
||||
sum += Math.abs(sma - (arr[i]));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* The CCI series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.cci
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('cci', 'sma',
|
||||
/**
|
||||
* Commodity Channel Index (CCI). This series requires `linkedTo` option to
|
||||
* be set.
|
||||
*
|
||||
* @sample stock/indicators/cci
|
||||
* CCI indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/cci
|
||||
* @optionparent plotOptions.cci
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 14
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, TP = [], periodTP = [], range = 1, CCI = [], xData = [], yData = [], CCIPoint, p, len, smaTP, TPtemp, meanDev, i;
|
||||
// CCI requires close value
|
||||
if (xVal.length <= period ||
|
||||
!isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
// accumulate first N-points
|
||||
while (range < period) {
|
||||
p = yVal[range - 1];
|
||||
TP.push((p[1] + p[2] + p[3]) / 3);
|
||||
range++;
|
||||
}
|
||||
for (i = period; i <= yValLen; i++) {
|
||||
p = yVal[i - 1];
|
||||
TPtemp = (p[1] + p[2] + p[3]) / 3;
|
||||
len = TP.push(TPtemp);
|
||||
periodTP = TP.slice(len - period);
|
||||
smaTP = sumArray(periodTP) / period;
|
||||
meanDev = meanDeviation(periodTP, smaTP) / period;
|
||||
CCIPoint = ((TPtemp - smaTP) / (0.015 * meanDev));
|
||||
CCI.push([xVal[i - 1], CCIPoint]);
|
||||
xData.push(xVal[i - 1]);
|
||||
yData.push(CCIPoint);
|
||||
}
|
||||
return {
|
||||
values: CCI,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `CCI` series. If the [type](#series.cci.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.cci
|
||||
* @since 6.0.0
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/cci
|
||||
* @apioption series.cci
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,185 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Highsoft AS
|
||||
*
|
||||
* Author: Sebastian Domas
|
||||
*
|
||||
* Chaikin Money Flow indicator for Highstock
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
/**
|
||||
* The CMF series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.cmf
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('cmf', 'sma',
|
||||
/**
|
||||
* Chaikin Money Flow indicator (cmf).
|
||||
*
|
||||
* @sample stock/indicators/cmf/
|
||||
* Chaikin Money Flow indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @excluding animationLimit
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/cmf
|
||||
* @optionparent plotOptions.cmf
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 14,
|
||||
/**
|
||||
* The id of another series to use its data as volume data for the
|
||||
* indiator calculation.
|
||||
*/
|
||||
volumeSeriesID: 'volume'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Chaikin Money Flow',
|
||||
/**
|
||||
* Checks if the series and volumeSeries are accessible, number of
|
||||
* points.x is longer than period, is series has OHLC data
|
||||
* @private
|
||||
* @param {Highcharts.CMFIndicator} this indicator to use.
|
||||
* @return {boolean} True if series is valid and can be computed,
|
||||
* otherwise false.
|
||||
*/
|
||||
isValid: function () {
|
||||
var chart = this.chart, options = this.options, series = this.linkedParent, volumeSeries = (this.volumeSeries ||
|
||||
(this.volumeSeries =
|
||||
chart.get(options.params.volumeSeriesID))), isSeriesOHLC = (series &&
|
||||
series.yData &&
|
||||
series.yData[0].length === 4);
|
||||
/**
|
||||
* @private
|
||||
* @param {Highcharts.Series} serie to check length validity on.
|
||||
* @return {boolean|undefined} true if length is valid.
|
||||
*/
|
||||
function isLengthValid(serie) {
|
||||
return serie.xData &&
|
||||
serie.xData.length >= options.params.period;
|
||||
}
|
||||
return !!(series &&
|
||||
volumeSeries &&
|
||||
isLengthValid(series) &&
|
||||
isLengthValid(volumeSeries) && isSeriesOHLC);
|
||||
},
|
||||
/**
|
||||
* Returns indicator's data.
|
||||
* @private
|
||||
* @param {Highcharts.CMFIndicator} this indicator to use.
|
||||
* @param {Highcharts.Series} series to calculate values from
|
||||
* @param {Highcharts.CMFIndicatorParamsOptions} params to pass
|
||||
* @return {boolean|Highcharts.IndicatorNullableValuesObject} Returns false if the
|
||||
* indicator is not valid, otherwise returns Values object.
|
||||
*/
|
||||
getValues: function (series, params) {
|
||||
if (!this.isValid()) {
|
||||
return;
|
||||
}
|
||||
return this.getMoneyFlow(series.xData, series.yData, this.volumeSeries.yData, params.period);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Array<number>} xData - x timestamp values
|
||||
* @param {Array<number>} seriesYData - yData of basic series
|
||||
* @param {Array<number>} volumeSeriesYData - yData of volume series
|
||||
* @param {number} period - indicator's param
|
||||
* @return {Highcharts.IndicatorNullableValuesObject} object containing computed money
|
||||
* flow data
|
||||
*/
|
||||
getMoneyFlow: function (xData, seriesYData, volumeSeriesYData, period) {
|
||||
var len = seriesYData.length, moneyFlowVolume = [], sumVolume = 0, sumMoneyFlowVolume = 0, moneyFlowXData = [], moneyFlowYData = [], values = [], i, point, nullIndex = -1;
|
||||
/**
|
||||
* Calculates money flow volume, changes i, nullIndex vars from
|
||||
* upper scope!
|
||||
* @private
|
||||
* @param {Array<number>} ohlc - OHLC point
|
||||
* @param {number} volume - Volume point's y value
|
||||
* @return {number|null} - volume * moneyFlowMultiplier
|
||||
**/
|
||||
function getMoneyFlowVolume(ohlc, volume) {
|
||||
var high = ohlc[1], low = ohlc[2], close = ohlc[3], isValid = volume !== null &&
|
||||
high !== null &&
|
||||
low !== null &&
|
||||
close !== null &&
|
||||
high !== low;
|
||||
/**
|
||||
* @private
|
||||
* @param {number} h - High value
|
||||
* @param {number} l - Low value
|
||||
* @param {number} c - Close value
|
||||
* @return {number} calculated multiplier for the point
|
||||
**/
|
||||
function getMoneyFlowMultiplier(h, l, c) {
|
||||
return ((c - l) - (h - c)) / (h - l);
|
||||
}
|
||||
return isValid ?
|
||||
getMoneyFlowMultiplier(high, low, close) * volume :
|
||||
((nullIndex = i), null);
|
||||
}
|
||||
if (period > 0 && period <= len) {
|
||||
for (i = 0; i < period; i++) {
|
||||
moneyFlowVolume[i] = getMoneyFlowVolume(seriesYData[i], volumeSeriesYData[i]);
|
||||
sumVolume += volumeSeriesYData[i];
|
||||
sumMoneyFlowVolume += moneyFlowVolume[i];
|
||||
}
|
||||
moneyFlowXData.push(xData[i - 1]);
|
||||
moneyFlowYData.push(i - nullIndex >= period && sumVolume !== 0 ?
|
||||
sumMoneyFlowVolume / sumVolume :
|
||||
null);
|
||||
values.push([moneyFlowXData[0], moneyFlowYData[0]]);
|
||||
for (; i < len; i++) {
|
||||
moneyFlowVolume[i] = getMoneyFlowVolume(seriesYData[i], volumeSeriesYData[i]);
|
||||
sumVolume -= volumeSeriesYData[i - period];
|
||||
sumVolume += volumeSeriesYData[i];
|
||||
sumMoneyFlowVolume -= moneyFlowVolume[i - period];
|
||||
sumMoneyFlowVolume += moneyFlowVolume[i];
|
||||
point = [
|
||||
xData[i],
|
||||
i - nullIndex >= period ?
|
||||
sumMoneyFlowVolume / sumVolume :
|
||||
null
|
||||
];
|
||||
moneyFlowXData.push(point[0]);
|
||||
moneyFlowYData.push(point[1]);
|
||||
values.push([point[0], point[1]]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: values,
|
||||
xData: moneyFlowXData,
|
||||
yData: moneyFlowYData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `CMF` series. If the [type](#series.cmf.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.cmf
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/cmf
|
||||
* @apioption series.cmf
|
||||
*/
|
||||
''; // adds doclet above to the transpiled file
|
||||
@@ -0,0 +1,144 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import './ADIndicator.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, error = U.error, seriesType = U.seriesType;
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
var EMA = H.seriesTypes.ema, AD = H.seriesTypes.ad;
|
||||
/**
|
||||
* The Chaikin series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.chaikin
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('chaikin', 'ema',
|
||||
/**
|
||||
* Chaikin Oscillator. This series requires the `linkedTo` option to
|
||||
* be set and should be loaded after the `stock/indicators/indicators.js`
|
||||
* and `stock/indicators/ema.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/chaikin
|
||||
* Chaikin Oscillator
|
||||
*
|
||||
* @extends plotOptions.ema
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, joinBy, keys, navigatorOptions,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement,
|
||||
* pointRange, pointStart, showInNavigator, stacking
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/ema
|
||||
* @requires stock/indicators/chaikin
|
||||
* @optionparent plotOptions.chaikin
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Paramters used in calculation of Chaikin Oscillator
|
||||
* series points.
|
||||
*
|
||||
* @excluding index, period
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
volumeSeriesID: 'volume',
|
||||
/**
|
||||
* Periods for Chaikin Oscillator calculations.
|
||||
*
|
||||
* @type {Array<number>}
|
||||
* @default [3, 10]
|
||||
*/
|
||||
periods: [3, 10]
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Chaikin Osc',
|
||||
nameComponents: ['periods'],
|
||||
init: function () {
|
||||
var args = arguments, ctx = this;
|
||||
requiredIndicator.isParentLoaded(EMA, 'ema', ctx.type, function (indicator) {
|
||||
indicator.prototype.init.apply(ctx, args);
|
||||
return;
|
||||
});
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var periods = params.periods, period = params.period,
|
||||
// Accumulation Distribution Line data
|
||||
ADL,
|
||||
// 0- date, 1- Chaikin Oscillator
|
||||
CHA = [], xData = [], yData = [], periodsOffset,
|
||||
// Shorter Period EMA
|
||||
SPE,
|
||||
// Longer Period EMA
|
||||
LPE, oscillator, i;
|
||||
// Check if periods are correct
|
||||
if (periods.length !== 2 || periods[1] <= periods[0]) {
|
||||
error('Error: "Chaikin requires two periods. Notice, first ' +
|
||||
'period should be lower than the second one."');
|
||||
return;
|
||||
}
|
||||
ADL = AD.prototype.getValues.call(this, series, {
|
||||
volumeSeriesID: params.volumeSeriesID,
|
||||
period: period
|
||||
});
|
||||
// Check if adl is calculated properly, if not skip
|
||||
if (!ADL) {
|
||||
return;
|
||||
}
|
||||
SPE = EMA.prototype.getValues.call(this, ADL, {
|
||||
period: periods[0]
|
||||
});
|
||||
LPE = EMA.prototype.getValues.call(this, ADL, {
|
||||
period: periods[1]
|
||||
});
|
||||
// Check if ema is calculated properly, if not skip
|
||||
if (!SPE || !LPE) {
|
||||
return;
|
||||
}
|
||||
periodsOffset = periods[1] - periods[0];
|
||||
for (i = 0; i < LPE.yData.length; i++) {
|
||||
oscillator = correctFloat(SPE.yData[i + periodsOffset] -
|
||||
LPE.yData[i]);
|
||||
CHA.push([LPE.xData[i], oscillator]);
|
||||
xData.push(LPE.xData[i]);
|
||||
yData.push(oscillator);
|
||||
}
|
||||
return {
|
||||
values: CHA,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Chaikin Oscillator` series. If the [type](#series.chaikin.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.chaikin
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, dataParser, dataURL, joinBy, keys,
|
||||
* navigatorOptions, pointInterval, pointIntervalUnit,
|
||||
* pointPlacement, pointRange, pointStart, stacking, showInNavigator
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/ema
|
||||
* @requires stock/indicators/chaikin
|
||||
* @apioption series.chaikin
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,133 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, isArray = U.isArray, seriesType = U.seriesType;
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
var EMAindicator = H.seriesTypes.ema;
|
||||
/**
|
||||
* The DEMA series Type
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.dema
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('dema', 'ema',
|
||||
/**
|
||||
* Double exponential moving average (DEMA) indicator. This series requires
|
||||
* `linkedTo` option to be set and should be loaded after the
|
||||
* `stock/indicators/indicators.js` and `stock/indicators/ema.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/dema
|
||||
* DEMA indicator
|
||||
*
|
||||
* @extends plotOptions.ema
|
||||
* @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/ema
|
||||
* @requires stock/indicators/dema
|
||||
* @optionparent plotOptions.dema
|
||||
*/
|
||||
{},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
init: function () {
|
||||
var args = arguments, ctx = this;
|
||||
requiredIndicator.isParentLoaded(EMAindicator, 'ema', ctx.type, function (indicator) {
|
||||
indicator.prototype.init.apply(ctx, args);
|
||||
return;
|
||||
});
|
||||
},
|
||||
getEMA: function (yVal, prevEMA, SMA, index, i, xVal) {
|
||||
return EMAindicator.prototype.calculateEma(xVal || [], yVal, typeof i === 'undefined' ? 1 : i, this.chart.series[0].EMApercent, prevEMA, typeof index === 'undefined' ? -1 : index, SMA);
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, doubledPeriod = 2 * period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, index = -1, accumulatePeriodPoints = 0, SMA = 0, DEMA = [], xDataDema = [], yDataDema = [], EMA = 0,
|
||||
// EMA(EMA)
|
||||
EMAlevel2,
|
||||
// EMA of previous point
|
||||
prevEMA, prevEMAlevel2,
|
||||
// EMA values array
|
||||
EMAvalues = [], i, DEMAPoint;
|
||||
series.EMApercent = (2 / (period + 1));
|
||||
// Check period, if bigger than EMA points length, skip
|
||||
if (yValLen < 2 * period - 1) {
|
||||
return;
|
||||
}
|
||||
// Switch index for OHLC / Candlestick / Arearange
|
||||
if (isArray(yVal[0])) {
|
||||
index = params.index ? params.index : 0;
|
||||
}
|
||||
// Accumulate first N-points
|
||||
accumulatePeriodPoints =
|
||||
EMAindicator.prototype.accumulatePeriodPoints(period, index, yVal);
|
||||
// first point
|
||||
SMA = accumulatePeriodPoints / period;
|
||||
accumulatePeriodPoints = 0;
|
||||
// Calculate value one-by-one for each period in visible data
|
||||
for (i = period; i < yValLen + 2; i++) {
|
||||
if (i < yValLen + 1) {
|
||||
EMA = this.getEMA(yVal, prevEMA, SMA, index, i)[1];
|
||||
EMAvalues.push(EMA);
|
||||
}
|
||||
prevEMA = EMA;
|
||||
// Summing first period points for EMA(EMA)
|
||||
if (i < doubledPeriod) {
|
||||
accumulatePeriodPoints += EMA;
|
||||
}
|
||||
else {
|
||||
// Calculate DEMA
|
||||
// First DEMA point
|
||||
if (i === doubledPeriod) {
|
||||
SMA = accumulatePeriodPoints / period;
|
||||
}
|
||||
EMA = EMAvalues[i - period - 1];
|
||||
EMAlevel2 = this.getEMA([EMA], prevEMAlevel2, SMA)[1];
|
||||
DEMAPoint = [
|
||||
xVal[i - 2],
|
||||
correctFloat(2 * EMA - EMAlevel2)
|
||||
];
|
||||
DEMA.push(DEMAPoint);
|
||||
xDataDema.push(DEMAPoint[0]);
|
||||
yDataDema.push(DEMAPoint[1]);
|
||||
prevEMAlevel2 = EMAlevel2;
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: DEMA,
|
||||
xData: xDataDema,
|
||||
yData: yDataDema
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `DEMA` series. If the [type](#series.ema.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.ema
|
||||
* @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/ema
|
||||
* @requires stock/indicators/dema
|
||||
* @apioption series.dema
|
||||
*/
|
||||
''; // adds doclet above to the transpiled file
|
||||
@@ -0,0 +1,116 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, pick = U.pick, seriesType = U.seriesType;
|
||||
/* eslint-disable valid-jsdoc */
|
||||
// Utils
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function accumulatePoints(sum, yVal, i, index, subtract) {
|
||||
var price = pick(yVal[i][index], yVal[i]);
|
||||
if (subtract) {
|
||||
return correctFloat(sum - price);
|
||||
}
|
||||
return correctFloat(sum + price);
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* The DPO series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.dpo
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('dpo', 'sma',
|
||||
/**
|
||||
* Detrended Price Oscillator. This series requires the `linkedTo` option to
|
||||
* be set and should be loaded after the `stock/indicators/indicators.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/dpo
|
||||
* Detrended Price Oscillator
|
||||
*
|
||||
* @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/dpo
|
||||
* @optionparent plotOptions.dpo
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Parameters used in calculation of Detrended Price Oscillator series
|
||||
* points.
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* Period for Detrended Price Oscillator
|
||||
*/
|
||||
period: 21
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'DPO',
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, index = params.index, offset = Math.floor(period / 2 + 1), range = period + offset, xVal = series.xData || [], yVal = series.yData || [], yValLen = yVal.length,
|
||||
// 0- date, 1- Detrended Price Oscillator
|
||||
DPO = [], xData = [], yData = [], sum = 0, oscillator, periodIndex, rangeIndex, price, i, j;
|
||||
if (xVal.length <= range) {
|
||||
return;
|
||||
}
|
||||
// Accumulate first N-points for SMA
|
||||
for (i = 0; i < period - 1; i++) {
|
||||
sum = accumulatePoints(sum, yVal, i, index);
|
||||
}
|
||||
// Detrended Price Oscillator formula:
|
||||
// DPO = Price - Simple moving average [from (n / 2 + 1) days ago]
|
||||
for (j = 0; j <= yValLen - range; j++) {
|
||||
periodIndex = j + period - 1;
|
||||
rangeIndex = j + range - 1;
|
||||
// adding the last period point
|
||||
sum = accumulatePoints(sum, yVal, periodIndex, index);
|
||||
price = pick(yVal[rangeIndex][index], yVal[rangeIndex]);
|
||||
oscillator = price - sum / period;
|
||||
// substracting the first period point
|
||||
sum = accumulatePoints(sum, yVal, j, index, true);
|
||||
DPO.push([xVal[rangeIndex], oscillator]);
|
||||
xData.push(xVal[rangeIndex]);
|
||||
yData.push(oscillator);
|
||||
}
|
||||
return {
|
||||
values: DPO,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A Detrended Price Oscillator. If the [type](#series.dpo.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.dpo
|
||||
* @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/dpo
|
||||
* @apioption series.dpo
|
||||
*/
|
||||
''; // to include the above in the js output'
|
||||
@@ -0,0 +1,113 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, isArray = U.isArray, seriesType = U.seriesType;
|
||||
/**
|
||||
* The EMA series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.ema
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('ema', 'sma',
|
||||
/**
|
||||
* Exponential moving average indicator (EMA). This series requires the
|
||||
* `linkedTo` option to be set.
|
||||
*
|
||||
* @sample stock/indicators/ema
|
||||
* Exponential moving average indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/ema
|
||||
* @optionparent plotOptions.ema
|
||||
*/
|
||||
{
|
||||
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 Highstock 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
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
accumulatePeriodPoints: function (period, index, yVal) {
|
||||
var sum = 0, i = 0, y = 0;
|
||||
while (i < period) {
|
||||
y = index < 0 ? yVal[i] : yVal[i][index];
|
||||
sum = sum + y;
|
||||
i++;
|
||||
}
|
||||
return sum;
|
||||
},
|
||||
calculateEma: function (xVal, yVal, i, EMApercent, calEMA, index, SMA) {
|
||||
var x = xVal[i - 1], yValue = index < 0 ?
|
||||
yVal[i - 1] :
|
||||
yVal[i - 1][index], y;
|
||||
y = typeof calEMA === 'undefined' ?
|
||||
SMA : correctFloat((yValue * EMApercent) +
|
||||
(calEMA * (1 - EMApercent)));
|
||||
return [x, y];
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, EMApercent = 2 / (period + 1), sum = 0, EMA = [], xData = [], yData = [], index = -1, SMA = 0, calEMA, EMAPoint, i;
|
||||
// Check period, if bigger than points length, skip
|
||||
if (yValLen < period) {
|
||||
return;
|
||||
}
|
||||
// Switch index for OHLC / Candlestick / Arearange
|
||||
if (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
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* 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
|
||||
* @requires stock/indicators/ema
|
||||
* @apioption series.ema
|
||||
*/
|
||||
''; // adds doclet above to the transpiled file
|
||||
@@ -0,0 +1,640 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
/* eslint-enable @typescript-eslint/interface-name-prefix */
|
||||
import Color from '../../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import U from '../../Core/Utilities.js';
|
||||
var defined = U.defined, isArray = U.isArray, merge = U.merge, objectEach = U.objectEach, seriesType = U.seriesType;
|
||||
var UNDEFINED, SMA = H.seriesTypes.sma;
|
||||
/* eslint-disable require-jsdoc */
|
||||
// Utils:
|
||||
function maxHigh(arr) {
|
||||
return arr.reduce(function (max, res) {
|
||||
return Math.max(max, res[1]);
|
||||
}, -Infinity);
|
||||
}
|
||||
function minLow(arr) {
|
||||
return arr.reduce(function (min, res) {
|
||||
return Math.min(min, res[2]);
|
||||
}, Infinity);
|
||||
}
|
||||
function highlowLevel(arr) {
|
||||
return {
|
||||
high: maxHigh(arr),
|
||||
low: minLow(arr)
|
||||
};
|
||||
}
|
||||
function getClosestPointRange(axis) {
|
||||
var closestDataRange, loopLength, distance, xData, i;
|
||||
axis.series.forEach(function (series) {
|
||||
if (series.xData) {
|
||||
xData = series.xData;
|
||||
loopLength = series.xIncrement ? 1 : xData.length - 1;
|
||||
for (i = loopLength; i > 0; i--) {
|
||||
distance = xData[i] - xData[i - 1];
|
||||
if (closestDataRange === UNDEFINED ||
|
||||
distance < closestDataRange) {
|
||||
closestDataRange = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return closestDataRange;
|
||||
}
|
||||
// Check two lines intersection (line a1-a2 and b1-b2)
|
||||
// Source: https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
|
||||
function checkLineIntersection(a1, a2, b1, b2) {
|
||||
if (a1 && a2 && b1 && b2) {
|
||||
var saX = a2.plotX - a1.plotX, // Auxiliary section a2-a1 X
|
||||
saY = a2.plotY - a1.plotY, // Auxiliary section a2-a1 Y
|
||||
sbX = b2.plotX - b1.plotX, // Auxiliary section b2-b1 X
|
||||
sbY = b2.plotY - b1.plotY, // Auxiliary section b2-b1 Y
|
||||
sabX = a1.plotX - b1.plotX, // Auxiliary section a1-b1 X
|
||||
sabY = a1.plotY - b1.plotY, // Auxiliary section a1-b1 Y
|
||||
// First degree Bézier parameters
|
||||
u, t;
|
||||
u = (-saY * sabX + saX * sabY) / (-sbX * saY + saX * sbY);
|
||||
t = (sbX * sabY - sbY * sabX) / (-sbX * saY + saX * sbY);
|
||||
if (u >= 0 && u <= 1 && t >= 0 && t <= 1) {
|
||||
return {
|
||||
plotX: a1.plotX + (t * saX),
|
||||
plotY: a1.plotY + (t * saY)
|
||||
};
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Parameter opt (indicator options object) include indicator, points,
|
||||
// nextPoints, color, options, gappedExtend and graph properties
|
||||
function drawSenkouSpan(opt) {
|
||||
var indicator = opt.indicator;
|
||||
indicator.points = opt.points;
|
||||
indicator.nextPoints = opt.nextPoints;
|
||||
indicator.color = opt.color;
|
||||
indicator.options = merge(opt.options.senkouSpan.styles, opt.gap);
|
||||
indicator.graph = opt.graph;
|
||||
indicator.fillGraph = true;
|
||||
SMA.prototype.drawGraph.call(indicator);
|
||||
}
|
||||
// Data integrity in Ichimoku is different than default "averages":
|
||||
// Point: [undefined, value, value, ...] is correct
|
||||
// Point: [undefined, undefined, undefined, ...] is incorrect
|
||||
H.approximations['ichimoku-averages'] = function () {
|
||||
var ret = [], isEmptyRange;
|
||||
[].forEach.call(arguments, function (arr, i) {
|
||||
ret.push(H.approximations.average(arr));
|
||||
isEmptyRange = !isEmptyRange && typeof ret[i] === 'undefined';
|
||||
});
|
||||
// Return undefined when first elem. is undefined and let
|
||||
// sum method handle null (#7377)
|
||||
return isEmptyRange ? void 0 : ret;
|
||||
};
|
||||
/* eslint-enable require-jsdoc */
|
||||
/**
|
||||
* The IKH series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.ikh
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('ikh', 'sma',
|
||||
/**
|
||||
* Ichimoku Kinko Hyo (IKH). This series requires `linkedTo` option to be
|
||||
* set.
|
||||
*
|
||||
* @sample stock/indicators/ichimoku-kinko-hyo
|
||||
* Ichimoku Kinko Hyo indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @excluding allAreas, colorAxis, compare, compareBase, joinBy, keys,
|
||||
* navigatorOptions, pointInterval, pointIntervalUnit,
|
||||
* pointPlacement, pointRange, pointStart, showInNavigator,
|
||||
* stacking
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/ichimoku-kinko-hyo
|
||||
* @optionparent plotOptions.ikh
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 26,
|
||||
/**
|
||||
* The base period for Tenkan calculations.
|
||||
*/
|
||||
periodTenkan: 9,
|
||||
/**
|
||||
* The base period for Senkou Span B calculations
|
||||
*/
|
||||
periodSenkouSpanB: 52
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> <b> {series.name}</b><br/>' +
|
||||
'TENKAN SEN: {point.tenkanSen:.3f}<br/>' +
|
||||
'KIJUN SEN: {point.kijunSen:.3f}<br/>' +
|
||||
'CHIKOU SPAN: {point.chikouSpan:.3f}<br/>' +
|
||||
'SENKOU SPAN A: {point.senkouSpanA:.3f}<br/>' +
|
||||
'SENKOU SPAN B: {point.senkouSpanB:.3f}<br/>'
|
||||
},
|
||||
/**
|
||||
* The styles for Tenkan line
|
||||
*/
|
||||
tenkanLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The styles for Kijun line
|
||||
*/
|
||||
kijunLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The styles for Chikou line
|
||||
*/
|
||||
chikouLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The styles for Senkou Span A line
|
||||
*/
|
||||
senkouSpanA: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The styles for Senkou Span B line
|
||||
*/
|
||||
senkouSpanB: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The styles for area between Senkou Span A and B.
|
||||
*/
|
||||
senkouSpan: {
|
||||
/**
|
||||
* Color of the area between Senkou Span A and B,
|
||||
* when Senkou Span A is above Senkou Span B. Note that if
|
||||
* a `style.fill` is defined, the `color` takes precedence and
|
||||
* the `style.fill` is ignored.
|
||||
*
|
||||
* @see [senkouSpan.styles.fill](#series.ikh.senkouSpan.styles.fill)
|
||||
*
|
||||
* @sample stock/indicators/ichimoku-kinko-hyo
|
||||
* Ichimoku Kinko Hyo color
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 7.0.0
|
||||
* @apioption plotOptions.ikh.senkouSpan.color
|
||||
*/
|
||||
/**
|
||||
* Color of the area between Senkou Span A and B,
|
||||
* when Senkou Span A is under Senkou Span B.
|
||||
*
|
||||
* @sample stock/indicators/ikh-negative-color
|
||||
* Ichimoku Kinko Hyo negativeColor
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 7.0.0
|
||||
* @apioption plotOptions.ikh.senkouSpan.negativeColor
|
||||
*/
|
||||
styles: {
|
||||
/**
|
||||
* Color of the area between Senkou Span A and B.
|
||||
*
|
||||
* @deprecated
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
fill: 'rgba(255, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'ichimoku-averages'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
pointArrayMap: [
|
||||
'tenkanSen',
|
||||
'kijunSen',
|
||||
'chikouSpan',
|
||||
'senkouSpanA',
|
||||
'senkouSpanB'
|
||||
],
|
||||
pointValKey: 'tenkanSen',
|
||||
nameComponents: ['periodSenkouSpanB', 'period', 'periodTenkan'],
|
||||
init: function () {
|
||||
SMA.prototype.init.apply(this, arguments);
|
||||
// Set default color for lines:
|
||||
this.options = merge({
|
||||
tenkanLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
},
|
||||
kijunLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
},
|
||||
chikouLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
},
|
||||
senkouSpanA: {
|
||||
styles: {
|
||||
lineColor: this.color,
|
||||
fill: color(this.color).setOpacity(0.5).get()
|
||||
}
|
||||
},
|
||||
senkouSpanB: {
|
||||
styles: {
|
||||
lineColor: this.color,
|
||||
fill: color(this.color).setOpacity(0.5).get()
|
||||
}
|
||||
},
|
||||
senkouSpan: {
|
||||
styles: {
|
||||
fill: color(this.color).setOpacity(0.2).get()
|
||||
}
|
||||
}
|
||||
}, this.options);
|
||||
},
|
||||
toYData: function (point) {
|
||||
return [
|
||||
point.tenkanSen,
|
||||
point.kijunSen,
|
||||
point.chikouSpan,
|
||||
point.senkouSpanA,
|
||||
point.senkouSpanB
|
||||
];
|
||||
},
|
||||
translate: function () {
|
||||
var indicator = this;
|
||||
SMA.prototype.translate.apply(indicator);
|
||||
indicator.points.forEach(function (point) {
|
||||
indicator.pointArrayMap.forEach(function (value) {
|
||||
if (defined(point[value])) {
|
||||
point['plot' + value] =
|
||||
indicator.yAxis.toPixels(point[value], true);
|
||||
// Add extra parameters for support tooltip in moved
|
||||
// lines
|
||||
point.plotY = point['plot' + value];
|
||||
point.tooltipPos = [
|
||||
point.plotX,
|
||||
point['plot' + value]
|
||||
];
|
||||
point.isNull = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// One does not simply
|
||||
// Render five linesZ
|
||||
// And an arearange
|
||||
// In just one series..
|
||||
drawGraph: function () {
|
||||
var indicator = this, mainLinePoints = (indicator.points), pointsLength = mainLinePoints.length, mainLineOptions = (indicator.options), mainLinePath = (indicator.graph), mainColor = indicator.color, gappedExtend = {
|
||||
options: {
|
||||
gapSize: mainLineOptions.gapSize
|
||||
}
|
||||
}, pointArrayMapLength = indicator.pointArrayMap.length, allIchimokuPoints = [[], [], [], [], [], []], ikhMap = {
|
||||
tenkanLine: allIchimokuPoints[0],
|
||||
kijunLine: allIchimokuPoints[1],
|
||||
chikouLine: allIchimokuPoints[2],
|
||||
senkouSpanA: allIchimokuPoints[3],
|
||||
senkouSpanB: allIchimokuPoints[4],
|
||||
senkouSpan: allIchimokuPoints[5]
|
||||
}, intersectIndexColl = [], senkouSpanOptions = indicator.options.senkouSpan, color = (senkouSpanOptions.color ||
|
||||
senkouSpanOptions.styles.fill), negativeColor = (senkouSpanOptions.negativeColor),
|
||||
// Points to create color and negativeColor senkouSpan
|
||||
points = [
|
||||
[],
|
||||
[] // Points negative color
|
||||
],
|
||||
// For span, we need an access to the next points, used in
|
||||
// getGraphPath()
|
||||
nextPoints = [
|
||||
[],
|
||||
[] // NextPoints negative color
|
||||
], lineIndex = 0, position, point, i, startIntersect, endIntersect, sectionPoints, sectionNextPoints, pointsPlotYSum, nextPointsPlotYSum, senkouSpanTempColor, concatArrIndex, j, k;
|
||||
indicator.ikhMap = ikhMap;
|
||||
// Generate points for all lines and spans lines:
|
||||
while (pointsLength--) {
|
||||
point = mainLinePoints[pointsLength];
|
||||
for (i = 0; i < pointArrayMapLength; i++) {
|
||||
position = indicator.pointArrayMap[i];
|
||||
if (defined(point[position])) {
|
||||
allIchimokuPoints[i].push({
|
||||
plotX: point.plotX,
|
||||
plotY: point['plot' + position],
|
||||
isNull: false
|
||||
});
|
||||
}
|
||||
}
|
||||
if (negativeColor &&
|
||||
pointsLength !== mainLinePoints.length - 1) {
|
||||
// Check if lines intersect
|
||||
var index = ikhMap.senkouSpanB.length - 1, intersect = checkLineIntersection(ikhMap.senkouSpanA[index - 1], ikhMap.senkouSpanA[index], ikhMap.senkouSpanB[index - 1], ikhMap.senkouSpanB[index]), intersectPointObj = {
|
||||
plotX: intersect.plotX,
|
||||
plotY: intersect.plotY,
|
||||
isNull: false,
|
||||
intersectPoint: true
|
||||
};
|
||||
if (intersect) {
|
||||
// Add intersect point to ichimoku points collection
|
||||
// Create senkouSpan sections
|
||||
ikhMap.senkouSpanA.splice(index, 0, intersectPointObj);
|
||||
ikhMap.senkouSpanB.splice(index, 0, intersectPointObj);
|
||||
intersectIndexColl.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Modify options and generate lines:
|
||||
objectEach(ikhMap, function (values, lineName) {
|
||||
if (mainLineOptions[lineName] &&
|
||||
lineName !== 'senkouSpan') {
|
||||
// First line is rendered by default option
|
||||
indicator.points = allIchimokuPoints[lineIndex];
|
||||
indicator.options = merge(mainLineOptions[lineName].styles, gappedExtend);
|
||||
indicator.graph = indicator['graph' + lineName];
|
||||
indicator.fillGraph = false;
|
||||
indicator.color = mainColor;
|
||||
SMA.prototype.drawGraph.call(indicator);
|
||||
// Now save line
|
||||
indicator['graph' + lineName] = indicator.graph;
|
||||
}
|
||||
lineIndex++;
|
||||
});
|
||||
// Generate senkouSpan area:
|
||||
// If graphColection exist then remove svg
|
||||
// element and indicator property
|
||||
if (indicator.graphCollection) {
|
||||
indicator.graphCollection.forEach(function (graphName) {
|
||||
indicator[graphName].destroy();
|
||||
delete indicator[graphName];
|
||||
});
|
||||
}
|
||||
// Clean grapCollection or initialize it
|
||||
indicator.graphCollection = [];
|
||||
// When user set negativeColor property
|
||||
if (negativeColor &&
|
||||
ikhMap.senkouSpanA[0] &&
|
||||
ikhMap.senkouSpanB[0]) {
|
||||
// Add first and last point to senkouSpan area sections
|
||||
intersectIndexColl.unshift(0);
|
||||
intersectIndexColl.push(ikhMap.senkouSpanA.length - 1);
|
||||
// Populate points and nextPoints arrays
|
||||
for (j = 0; j < intersectIndexColl.length - 1; j++) {
|
||||
startIntersect = intersectIndexColl[j];
|
||||
endIntersect = intersectIndexColl[j + 1];
|
||||
sectionPoints = ikhMap.senkouSpanB.slice(startIntersect, endIntersect + 1);
|
||||
sectionNextPoints = ikhMap.senkouSpanA.slice(startIntersect, endIntersect + 1);
|
||||
// Add points to color or negativeColor arrays
|
||||
// Check the middle point (if exist)
|
||||
if (Math.floor(sectionPoints.length / 2) >= 1) {
|
||||
var x = Math.floor(sectionPoints.length / 2);
|
||||
// When middle points has equal values
|
||||
// Compare all ponints plotY value sum
|
||||
if (sectionPoints[x].plotY ===
|
||||
sectionNextPoints[x].plotY) {
|
||||
pointsPlotYSum = 0;
|
||||
nextPointsPlotYSum = 0;
|
||||
for (k = 0; k < sectionPoints.length; k++) {
|
||||
pointsPlotYSum +=
|
||||
sectionPoints[k].plotY;
|
||||
nextPointsPlotYSum +=
|
||||
sectionNextPoints[k].plotY;
|
||||
}
|
||||
concatArrIndex = (pointsPlotYSum > nextPointsPlotYSum ? 0 : 1);
|
||||
points[concatArrIndex] = (points[concatArrIndex].concat(sectionPoints));
|
||||
nextPoints[concatArrIndex] = (nextPoints[concatArrIndex].concat(sectionNextPoints));
|
||||
}
|
||||
else {
|
||||
// Compare middle point of the section
|
||||
concatArrIndex = (sectionPoints[x].plotY >
|
||||
sectionNextPoints[x].plotY ?
|
||||
0 : 1);
|
||||
points[concatArrIndex] = (points[concatArrIndex].concat(sectionPoints));
|
||||
nextPoints[concatArrIndex] = (nextPoints[concatArrIndex].concat(sectionNextPoints));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Compare first point of the section
|
||||
concatArrIndex = (sectionPoints[0].plotY >
|
||||
sectionNextPoints[0].plotY ?
|
||||
0 : 1);
|
||||
points[concatArrIndex] = (points[concatArrIndex].concat(sectionPoints));
|
||||
nextPoints[concatArrIndex] = (nextPoints[concatArrIndex].concat(sectionNextPoints));
|
||||
}
|
||||
}
|
||||
// Render color and negativeColor paths
|
||||
[
|
||||
'graphsenkouSpanColor', 'graphsenkouSpanNegativeColor'
|
||||
].forEach(function (areaName, i) {
|
||||
if (points[i].length && nextPoints[i].length) {
|
||||
senkouSpanTempColor = (i === 0) ?
|
||||
color : negativeColor;
|
||||
drawSenkouSpan({
|
||||
indicator: indicator,
|
||||
points: points[i],
|
||||
nextPoints: nextPoints[i],
|
||||
color: senkouSpanTempColor,
|
||||
options: mainLineOptions,
|
||||
gap: gappedExtend,
|
||||
graph: indicator[areaName]
|
||||
});
|
||||
// Now save line
|
||||
indicator[areaName] = indicator.graph;
|
||||
indicator.graphCollection.push(areaName);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// When user set only senkouSpan style.fill property
|
||||
drawSenkouSpan({
|
||||
indicator: indicator,
|
||||
points: ikhMap.senkouSpanB,
|
||||
nextPoints: ikhMap.senkouSpanA,
|
||||
color: color,
|
||||
options: mainLineOptions,
|
||||
gap: gappedExtend,
|
||||
graph: indicator.graphsenkouSpan
|
||||
});
|
||||
// Now save line
|
||||
indicator.graphsenkouSpan = indicator.graph;
|
||||
}
|
||||
// Clean temporary properties:
|
||||
delete indicator.nextPoints;
|
||||
delete indicator.fillGraph;
|
||||
// Restore options and draw the Tenkan line:
|
||||
indicator.points = mainLinePoints;
|
||||
indicator.options = mainLineOptions;
|
||||
indicator.graph = mainLinePath;
|
||||
},
|
||||
getGraphPath: function (points) {
|
||||
var indicator = this, path = [], spanA, spanAarr = [];
|
||||
points = points || this.points;
|
||||
// Render Senkou Span
|
||||
if (indicator.fillGraph && indicator.nextPoints) {
|
||||
spanA = SMA.prototype.getGraphPath.call(indicator,
|
||||
// Reverse points, so Senkou Span A will start from the end:
|
||||
indicator.nextPoints);
|
||||
spanA[0][0] = 'L';
|
||||
path = SMA.prototype.getGraphPath.call(indicator, points);
|
||||
spanAarr = spanA.slice(0, path.length);
|
||||
for (var i = spanAarr.length - 1; i >= 0; i--) {
|
||||
path.push(spanAarr[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
path = SMA.prototype.getGraphPath.apply(indicator, arguments);
|
||||
}
|
||||
return path;
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, periodTenkan = params.periodTenkan, periodSenkouSpanB = params.periodSenkouSpanB, xVal = series.xData, yVal = series.yData, xAxis = series.xAxis, yValLen = (yVal && yVal.length) || 0, closestPointRange = getClosestPointRange(xAxis), IKH = [], xData = [], dateStart, date, slicedTSY, slicedKSY, slicedSSBY, pointTS, pointKS, pointSSB, i, TS, KS, CS, SSA, SSB;
|
||||
// Ikh requires close value
|
||||
if (xVal.length <= period ||
|
||||
!isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
// Add timestamps at the beginning
|
||||
dateStart = xVal[0] - (period * closestPointRange);
|
||||
for (i = 0; i < period; i++) {
|
||||
xData.push(dateStart + i * closestPointRange);
|
||||
}
|
||||
for (i = 0; i < yValLen; i++) {
|
||||
// Tenkan Sen
|
||||
if (i >= periodTenkan) {
|
||||
slicedTSY = yVal.slice(i - periodTenkan, i);
|
||||
pointTS = highlowLevel(slicedTSY);
|
||||
TS = (pointTS.high + pointTS.low) / 2;
|
||||
}
|
||||
if (i >= period) {
|
||||
slicedKSY = yVal.slice(i - period, i);
|
||||
pointKS = highlowLevel(slicedKSY);
|
||||
KS = (pointKS.high + pointKS.low) / 2;
|
||||
SSA = (TS + KS) / 2;
|
||||
}
|
||||
if (i >= periodSenkouSpanB) {
|
||||
slicedSSBY = yVal.slice(i - periodSenkouSpanB, i);
|
||||
pointSSB = highlowLevel(slicedSSBY);
|
||||
SSB = (pointSSB.high + pointSSB.low) / 2;
|
||||
}
|
||||
CS = yVal[i][3];
|
||||
date = xVal[i];
|
||||
if (IKH[i] === UNDEFINED) {
|
||||
IKH[i] = [];
|
||||
}
|
||||
if (IKH[i + period] === UNDEFINED) {
|
||||
IKH[i + period] = [];
|
||||
}
|
||||
IKH[i + period][0] = TS;
|
||||
IKH[i + period][1] = KS;
|
||||
IKH[i + period][2] = UNDEFINED;
|
||||
IKH[i][2] = CS;
|
||||
if (i <= period) {
|
||||
IKH[i + period][3] = UNDEFINED;
|
||||
IKH[i + period][4] = UNDEFINED;
|
||||
}
|
||||
if (IKH[i + 2 * period] === UNDEFINED) {
|
||||
IKH[i + 2 * period] = [];
|
||||
}
|
||||
IKH[i + 2 * period][3] = SSA;
|
||||
IKH[i + 2 * period][4] = SSB;
|
||||
xData.push(date);
|
||||
}
|
||||
// Add timestamps for further points
|
||||
for (i = 1; i <= period; i++) {
|
||||
xData.push(date + i * closestPointRange);
|
||||
}
|
||||
return {
|
||||
values: IKH,
|
||||
xData: xData,
|
||||
yData: IKH
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `IKH` series. If the [type](#series.ikh.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.ikh
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/ichimoku-kinko-hyo
|
||||
* @apioption series.ikh
|
||||
*/
|
||||
''; // add doclet above to transpiled file
|
||||
@@ -0,0 +1,318 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, error = U.error, extend = U.extend, isArray = U.isArray, pick = U.pick, seriesType = U.seriesType, splat = U.splat;
|
||||
import '../../Core/Series/Series.js';
|
||||
var Series = H.Series, seriesTypes = H.seriesTypes, ohlcProto = H.seriesTypes.ohlc.prototype, generateMessage = requiredIndicator.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
|
||||
@@ -0,0 +1,172 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, merge = U.merge, seriesType = U.seriesType;
|
||||
import multipleLinesMixin from '../../Mixins/MultipleLines.js';
|
||||
var SMA = H.seriesTypes.sma, EMA = H.seriesTypes.ema, ATR = H.seriesTypes.atr;
|
||||
/**
|
||||
* The Keltner Channels series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.keltnerchannels
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('keltnerchannels', 'sma',
|
||||
/**
|
||||
* Keltner Channels. This series requires the `linkedTo` option to be set
|
||||
* and should be loaded after the `stock/indicators/indicators.js`,
|
||||
* `stock/indicators/atr.js`, and `stock/ema/.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/keltner-channels
|
||||
* Keltner Channels
|
||||
*
|
||||
* @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/keltner-channels
|
||||
* @optionparent plotOptions.keltnerchannels
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 20,
|
||||
/**
|
||||
* The ATR period.
|
||||
*/
|
||||
periodATR: 10,
|
||||
/**
|
||||
* The ATR multiplier.
|
||||
*/
|
||||
multiplierATR: 2
|
||||
},
|
||||
/**
|
||||
* Bottom line options.
|
||||
*
|
||||
*/
|
||||
bottomLine: {
|
||||
/**
|
||||
* Styles for a bottom line.
|
||||
*
|
||||
*/
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line. If not set, it's inherited from
|
||||
* `plotOptions.keltnerchannels.color`
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Top line options.
|
||||
*
|
||||
* @extends plotOptions.keltnerchannels.bottomLine
|
||||
*/
|
||||
topLine: {
|
||||
styles: {
|
||||
lineWidth: 1,
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span><b> {series.name}</b><br/>Upper Channel: {point.top}<br/>EMA({series.options.params.period}): {point.middle}<br/>Lower Channel: {point.bottom}<br/>'
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
},
|
||||
lineWidth: 1
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
merge(multipleLinesMixin, {
|
||||
pointArrayMap: ['top', 'middle', 'bottom'],
|
||||
pointValKey: 'middle',
|
||||
nameBase: 'Keltner Channels',
|
||||
nameComponents: ['period', 'periodATR', 'multiplierATR'],
|
||||
linesApiNames: ['topLine', 'bottomLine'],
|
||||
requiredIndicators: ['ema', 'atr'],
|
||||
init: function () {
|
||||
SMA.prototype.init.apply(this, arguments);
|
||||
// Set default color for lines:
|
||||
this.options = merge({
|
||||
topLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
},
|
||||
bottomLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
}
|
||||
}, this.options);
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, periodATR = params.periodATR, multiplierATR = params.multiplierATR, index = params.index, yVal = series.yData, yValLen = yVal ? yVal.length : 0,
|
||||
// Keltner Channels array structure:
|
||||
// 0-date, 1-top line, 2-middle line, 3-bottom line
|
||||
KC = [],
|
||||
// middle line, top line and bottom lineI
|
||||
ML, TL, BL, date, seriesEMA = EMA.prototype.getValues(series, {
|
||||
period: period,
|
||||
index: index
|
||||
}), seriesATR = ATR.prototype.getValues(series, {
|
||||
period: periodATR
|
||||
}), pointEMA, pointATR, xData = [], yData = [], i;
|
||||
if (yValLen < period) {
|
||||
return;
|
||||
}
|
||||
for (i = period; i <= yValLen; i++) {
|
||||
pointEMA = seriesEMA.values[i - period];
|
||||
pointATR = seriesATR.values[i - periodATR];
|
||||
date = pointEMA[0];
|
||||
TL = correctFloat(pointEMA[1] + (multiplierATR * pointATR[1]));
|
||||
BL = correctFloat(pointEMA[1] - (multiplierATR * pointATR[1]));
|
||||
ML = pointEMA[1];
|
||||
KC.push([date, TL, ML, BL]);
|
||||
xData.push(date);
|
||||
yData.push([TL, ML, BL]);
|
||||
}
|
||||
return {
|
||||
values: KC,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* A Keltner Channels indicator. If the [type](#series.keltnerchannels.type)
|
||||
* option is not specified, it is inherited from[chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.keltnerchannels
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, compare, compareBase, dataParser, dataURL,
|
||||
* joinBy, keys, navigatorOptions, pointInterval,
|
||||
* pointIntervalUnit, pointPlacement, pointRange, pointStart,
|
||||
* stacking, showInNavigator
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/keltner-channels
|
||||
* @apioption series.keltnerchannels
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,349 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, defined = U.defined, merge = U.merge, seriesType = U.seriesType;
|
||||
var noop = H.noop, SMA = H.seriesTypes.sma, EMA = H.seriesTypes.ema;
|
||||
/**
|
||||
* The MACD series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.macd
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('macd', 'sma',
|
||||
/**
|
||||
* Moving Average Convergence Divergence (MACD). This series requires
|
||||
* `linkedTo` option to be set and should be loaded after the
|
||||
* `stock/indicators/indicators.js` and `stock/indicators/ema.js`.
|
||||
*
|
||||
* @sample stock/indicators/macd
|
||||
* MACD indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/macd
|
||||
* @optionparent plotOptions.macd
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
/**
|
||||
* The short period for indicator calculations.
|
||||
*/
|
||||
shortPeriod: 12,
|
||||
/**
|
||||
* The long period for indicator calculations.
|
||||
*/
|
||||
longPeriod: 26,
|
||||
/**
|
||||
* The base period for signal calculations.
|
||||
*/
|
||||
signalPeriod: 9,
|
||||
period: 26
|
||||
},
|
||||
/**
|
||||
* The styles for signal line
|
||||
*/
|
||||
signalLine: {
|
||||
/**
|
||||
* @sample stock/indicators/macd-zones
|
||||
* Zones in MACD
|
||||
*
|
||||
* @extends plotOptions.macd.zones
|
||||
*/
|
||||
zones: [],
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The styles for macd line
|
||||
*/
|
||||
macdLine: {
|
||||
/**
|
||||
* @sample stock/indicators/macd-zones
|
||||
* Zones in MACD
|
||||
*
|
||||
* @extends plotOptions.macd.zones
|
||||
*/
|
||||
zones: [],
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
threshold: 0,
|
||||
groupPadding: 0.1,
|
||||
pointPadding: 0.1,
|
||||
crisp: false,
|
||||
states: {
|
||||
hover: {
|
||||
halo: {
|
||||
size: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> <b> {series.name}</b><br/>' +
|
||||
'Value: {point.MACD}<br/>' +
|
||||
'Signal: {point.signal}<br/>' +
|
||||
'Histogram: {point.y}<br/>'
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
},
|
||||
minPointLength: 0
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameComponents: ['longPeriod', 'shortPeriod', 'signalPeriod'],
|
||||
requiredIndicators: ['ema'],
|
||||
// "y" value is treated as Histogram data
|
||||
pointArrayMap: ['y', 'signal', 'MACD'],
|
||||
parallelArrays: ['x', 'y', 'signal', 'MACD'],
|
||||
pointValKey: 'y',
|
||||
// Columns support:
|
||||
markerAttribs: noop,
|
||||
getColumnMetrics: H.seriesTypes.column.prototype.getColumnMetrics,
|
||||
crispCol: H.seriesTypes.column.prototype.crispCol,
|
||||
// Colors and lines:
|
||||
init: function () {
|
||||
SMA.prototype.init.apply(this, arguments);
|
||||
// Check whether series is initialized. It may be not initialized,
|
||||
// when any of required indicators is missing.
|
||||
if (this.options) {
|
||||
// Set default color for a signal line and the histogram:
|
||||
this.options = merge({
|
||||
signalLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
},
|
||||
macdLine: {
|
||||
styles: {
|
||||
color: this.color
|
||||
}
|
||||
}
|
||||
}, this.options);
|
||||
// Zones have indexes automatically calculated, we need to
|
||||
// translate them to support multiple lines within one indicator
|
||||
this.macdZones = {
|
||||
zones: this.options.macdLine.zones,
|
||||
startIndex: 0
|
||||
};
|
||||
this.signalZones = {
|
||||
zones: this.macdZones.zones.concat(this.options.signalLine.zones),
|
||||
startIndex: this.macdZones.zones.length
|
||||
};
|
||||
this.resetZones = true;
|
||||
}
|
||||
},
|
||||
toYData: function (point) {
|
||||
return [point.y, point.signal, point.MACD];
|
||||
},
|
||||
translate: function () {
|
||||
var indicator = this, plotNames = ['plotSignal', 'plotMACD'];
|
||||
H.seriesTypes.column.prototype.translate.apply(indicator);
|
||||
indicator.points.forEach(function (point) {
|
||||
[point.signal, point.MACD].forEach(function (value, i) {
|
||||
if (value !== null) {
|
||||
point[plotNames[i]] =
|
||||
indicator.yAxis.toPixels(value, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
// this.graph is null due to removing two times the same SVG element
|
||||
this.graph = null;
|
||||
this.graphmacd = this.graphmacd && this.graphmacd.destroy();
|
||||
this.graphsignal = this.graphsignal && this.graphsignal.destroy();
|
||||
SMA.prototype.destroy.apply(this, arguments);
|
||||
},
|
||||
drawPoints: H.seriesTypes.column.prototype.drawPoints,
|
||||
drawGraph: function () {
|
||||
var indicator = this, mainLinePoints = indicator.points, pointsLength = mainLinePoints.length, mainLineOptions = indicator.options, histogramZones = indicator.zones, gappedExtend = {
|
||||
options: {
|
||||
gapSize: mainLineOptions.gapSize
|
||||
}
|
||||
}, otherSignals = [[], []], point;
|
||||
// Generate points for top and bottom lines:
|
||||
while (pointsLength--) {
|
||||
point = mainLinePoints[pointsLength];
|
||||
if (defined(point.plotMACD)) {
|
||||
otherSignals[0].push({
|
||||
plotX: point.plotX,
|
||||
plotY: point.plotMACD,
|
||||
isNull: !defined(point.plotMACD)
|
||||
});
|
||||
}
|
||||
if (defined(point.plotSignal)) {
|
||||
otherSignals[1].push({
|
||||
plotX: point.plotX,
|
||||
plotY: point.plotSignal,
|
||||
isNull: !defined(point.plotMACD)
|
||||
});
|
||||
}
|
||||
}
|
||||
// Modify options and generate smoothing line:
|
||||
['macd', 'signal'].forEach(function (lineName, i) {
|
||||
indicator.points = otherSignals[i];
|
||||
indicator.options = merge(mainLineOptions[lineName + 'Line'].styles, gappedExtend);
|
||||
indicator.graph = indicator['graph' + lineName];
|
||||
// Zones extension:
|
||||
indicator.currentLineZone = lineName + 'Zones';
|
||||
indicator.zones =
|
||||
indicator[indicator.currentLineZone].zones;
|
||||
SMA.prototype.drawGraph.call(indicator);
|
||||
indicator['graph' + lineName] = indicator.graph;
|
||||
});
|
||||
// Restore options:
|
||||
indicator.points = mainLinePoints;
|
||||
indicator.options = mainLineOptions;
|
||||
indicator.zones = histogramZones;
|
||||
indicator.currentLineZone = null;
|
||||
// indicator.graph = null;
|
||||
},
|
||||
getZonesGraphs: function (props) {
|
||||
var allZones = SMA.prototype.getZonesGraphs.call(this, props), currentZones = allZones;
|
||||
if (this.currentLineZone) {
|
||||
currentZones = allZones.splice(this[this.currentLineZone].startIndex + 1);
|
||||
if (!currentZones.length) {
|
||||
// Line has no zones, return basic graph "zone"
|
||||
currentZones = [props[0]];
|
||||
}
|
||||
else {
|
||||
// Add back basic prop:
|
||||
currentZones.splice(0, 0, props[0]);
|
||||
}
|
||||
}
|
||||
return currentZones;
|
||||
},
|
||||
applyZones: function () {
|
||||
// Histogram zones are handled by drawPoints method
|
||||
// Here we need to apply zones for all lines
|
||||
var histogramZones = this.zones;
|
||||
// signalZones.zones contains all zones:
|
||||
this.zones = this.signalZones.zones;
|
||||
SMA.prototype.applyZones.call(this);
|
||||
// applyZones hides only main series.graph, hide macd line manually
|
||||
if (this.graphmacd && this.options.macdLine.zones.length) {
|
||||
this.graphmacd.hide();
|
||||
}
|
||||
this.zones = histogramZones;
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var j = 0, MACD = [], xMACD = [], yMACD = [], signalLine = [], shortEMA, longEMA, i;
|
||||
if (series.xData.length <
|
||||
params.longPeriod + params.signalPeriod) {
|
||||
return;
|
||||
}
|
||||
// Calculating the short and long EMA used when calculating the MACD
|
||||
shortEMA = EMA.prototype.getValues(series, {
|
||||
period: params.shortPeriod
|
||||
});
|
||||
longEMA = EMA.prototype.getValues(series, {
|
||||
period: params.longPeriod
|
||||
});
|
||||
shortEMA = shortEMA.values;
|
||||
longEMA = longEMA.values;
|
||||
// Subtract each Y value from the EMA's and create the new dataset
|
||||
// (MACD)
|
||||
for (i = 1; i <= shortEMA.length; i++) {
|
||||
if (defined(longEMA[i - 1]) &&
|
||||
defined(longEMA[i - 1][1]) &&
|
||||
defined(shortEMA[i + params.shortPeriod + 1]) &&
|
||||
defined(shortEMA[i + params.shortPeriod + 1][0])) {
|
||||
MACD.push([
|
||||
shortEMA[i + params.shortPeriod + 1][0],
|
||||
0,
|
||||
null,
|
||||
shortEMA[i + params.shortPeriod + 1][1] -
|
||||
longEMA[i - 1][1]
|
||||
]);
|
||||
}
|
||||
}
|
||||
// Set the Y and X data of the MACD. This is used in calculating the
|
||||
// signal line.
|
||||
for (i = 0; i < MACD.length; i++) {
|
||||
xMACD.push(MACD[i][0]);
|
||||
yMACD.push([0, null, MACD[i][3]]);
|
||||
}
|
||||
// Setting the signalline (Signal Line: X-day EMA of MACD line).
|
||||
signalLine = EMA.prototype.getValues({
|
||||
xData: xMACD,
|
||||
yData: yMACD
|
||||
}, {
|
||||
period: params.signalPeriod,
|
||||
index: 2
|
||||
});
|
||||
signalLine = signalLine.values;
|
||||
// Setting the MACD Histogram. In comparison to the loop with pure
|
||||
// MACD this loop uses MACD x value not xData.
|
||||
for (i = 0; i < MACD.length; i++) {
|
||||
// detect the first point
|
||||
if (MACD[i][0] >= signalLine[0][0]) {
|
||||
MACD[i][2] = signalLine[j][1];
|
||||
yMACD[i] = [0, signalLine[j][1], MACD[i][3]];
|
||||
if (MACD[i][3] === null) {
|
||||
MACD[i][1] = 0;
|
||||
yMACD[i][0] = 0;
|
||||
}
|
||||
else {
|
||||
MACD[i][1] = correctFloat(MACD[i][3] -
|
||||
signalLine[j][1]);
|
||||
yMACD[i][0] = correctFloat(MACD[i][3] -
|
||||
signalLine[j][1]);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: MACD,
|
||||
xData: xMACD,
|
||||
yData: yMACD
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `MACD` series. If the [type](#series.macd.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.macd
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/macd
|
||||
* @apioption series.macd
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,154 @@
|
||||
/* *
|
||||
*
|
||||
* Money Flow Index indicator for Highstock
|
||||
*
|
||||
* (c) 2010-2020 Grzegorz Blachliński
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var error = U.error, isArray = U.isArray, seriesType = U.seriesType;
|
||||
/* eslint-disable require-jsdoc */
|
||||
// Utils:
|
||||
function sumArray(array) {
|
||||
return array.reduce(function (prev, cur) {
|
||||
return prev + cur;
|
||||
});
|
||||
}
|
||||
function toFixed(a, n) {
|
||||
return parseFloat(a.toFixed(n));
|
||||
}
|
||||
function calculateTypicalPrice(point) {
|
||||
return (point[1] + point[2] + point[3]) / 3;
|
||||
}
|
||||
function calculateRawMoneyFlow(typicalPrice, volume) {
|
||||
return typicalPrice * volume;
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
/**
|
||||
* The MFI series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.mfi
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('mfi', 'sma',
|
||||
/**
|
||||
* Money Flow Index. This series requires `linkedTo` option to be set and
|
||||
* should be loaded after the `stock/indicators/indicators.js` file.
|
||||
*
|
||||
* @sample stock/indicators/mfi
|
||||
* Money Flow Index Indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/mfi
|
||||
* @optionparent plotOptions.mfi
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index
|
||||
*/
|
||||
params: {
|
||||
period: 14,
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
volumeSeriesID: 'volume',
|
||||
/**
|
||||
* Number of maximum decimals that are used in MFI calculations.
|
||||
*/
|
||||
decimals: 4
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Money Flow Index',
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, decimals = params.decimals,
|
||||
// MFI starts calculations from the second point
|
||||
// Cause we need to calculate change between two points
|
||||
range = 1, volumeSeries = series.chart.get(params.volumeSeriesID), yValVolume = (volumeSeries && volumeSeries.yData), MFI = [], isUp = false, xData = [], yData = [], positiveMoneyFlow = [], negativeMoneyFlow = [], newTypicalPrice, oldTypicalPrice, rawMoneyFlow, negativeMoneyFlowSum, positiveMoneyFlowSum, moneyFlowRatio, MFIPoint, i;
|
||||
if (!volumeSeries) {
|
||||
error('Series ' +
|
||||
params.volumeSeriesID +
|
||||
' not found! Check `volumeSeriesID`.', true, series.chart);
|
||||
return;
|
||||
}
|
||||
// MFI requires high low and close values
|
||||
if ((xVal.length <= period) || !isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4 ||
|
||||
!yValVolume) {
|
||||
return;
|
||||
}
|
||||
// Calculate first typical price
|
||||
newTypicalPrice = calculateTypicalPrice(yVal[range]);
|
||||
// Accumulate first N-points
|
||||
while (range < period + 1) {
|
||||
// Calculate if up or down
|
||||
oldTypicalPrice = newTypicalPrice;
|
||||
newTypicalPrice = calculateTypicalPrice(yVal[range]);
|
||||
isUp = newTypicalPrice >= oldTypicalPrice;
|
||||
// Calculate raw money flow
|
||||
rawMoneyFlow = calculateRawMoneyFlow(newTypicalPrice, yValVolume[range]);
|
||||
// Add to array
|
||||
positiveMoneyFlow.push(isUp ? rawMoneyFlow : 0);
|
||||
negativeMoneyFlow.push(isUp ? 0 : rawMoneyFlow);
|
||||
range++;
|
||||
}
|
||||
for (i = range - 1; i < yValLen; i++) {
|
||||
if (i > range - 1) {
|
||||
// Remove first point from array
|
||||
positiveMoneyFlow.shift();
|
||||
negativeMoneyFlow.shift();
|
||||
// Calculate if up or down
|
||||
oldTypicalPrice = newTypicalPrice;
|
||||
newTypicalPrice = calculateTypicalPrice(yVal[i]);
|
||||
isUp = newTypicalPrice > oldTypicalPrice;
|
||||
// Calculate raw money flow
|
||||
rawMoneyFlow = calculateRawMoneyFlow(newTypicalPrice, yValVolume[i]);
|
||||
// Add to array
|
||||
positiveMoneyFlow.push(isUp ? rawMoneyFlow : 0);
|
||||
negativeMoneyFlow.push(isUp ? 0 : rawMoneyFlow);
|
||||
}
|
||||
// Calculate sum of negative and positive money flow:
|
||||
negativeMoneyFlowSum = sumArray(negativeMoneyFlow);
|
||||
positiveMoneyFlowSum = sumArray(positiveMoneyFlow);
|
||||
moneyFlowRatio = positiveMoneyFlowSum / negativeMoneyFlowSum;
|
||||
MFIPoint = toFixed(100 - (100 / (1 + moneyFlowRatio)), decimals);
|
||||
MFI.push([xVal[i], MFIPoint]);
|
||||
xData.push(xVal[i]);
|
||||
yData.push(MFIPoint);
|
||||
}
|
||||
return {
|
||||
values: MFI,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `MFI` series. If the [type](#series.mfi.type) option is not specified, it
|
||||
* is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.mfi
|
||||
* @since 6.0.0
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/mfi
|
||||
* @apioption series.mfi
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,97 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
/* eslint-disable require-jsdoc */
|
||||
function populateAverage(points, xVal, yVal, i, period) {
|
||||
var mmY = yVal[i - 1][3] - yVal[i - period - 1][3], mmX = xVal[i - 1];
|
||||
points.shift(); // remove point until range < period
|
||||
return [mmX, mmY];
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
/**
|
||||
* The Momentum series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.momentum
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('momentum', 'sma',
|
||||
/**
|
||||
* Momentum. This series requires `linkedTo` option to be set.
|
||||
*
|
||||
* @sample stock/indicators/momentum
|
||||
* Momentum indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/momentum
|
||||
* @optionparent plotOptions.momentum
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
period: 14
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Momentum',
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, xValue = xVal[0], yValue = yVal[0], MM = [], xData = [], yData = [], index, i, points, MMPoint;
|
||||
if (xVal.length <= period) {
|
||||
return;
|
||||
}
|
||||
// Switch index for OHLC / Candlestick / Arearange
|
||||
if (isArray(yVal[0])) {
|
||||
yValue = yVal[0][3];
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
// Starting point
|
||||
points = [
|
||||
[xValue, yValue]
|
||||
];
|
||||
// Calculate value one-by-one for each period in visible data
|
||||
for (i = (period + 1); i < yValLen; i++) {
|
||||
MMPoint = populateAverage(points, xVal, yVal, i, period, index);
|
||||
MM.push(MMPoint);
|
||||
xData.push(MMPoint[0]);
|
||||
yData.push(MMPoint[1]);
|
||||
}
|
||||
MMPoint = populateAverage(points, xVal, yVal, i, period, index);
|
||||
MM.push(MMPoint);
|
||||
xData.push(MMPoint[0]);
|
||||
yData.push(MMPoint[1]);
|
||||
return {
|
||||
values: MM,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Momentum` series. If the [type](#series.momentum.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.momentum
|
||||
* @since 6.0.0
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/momentum
|
||||
* @apioption series.momentum
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,73 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
var ATR = H.seriesTypes.atr;
|
||||
/**
|
||||
* The NATR series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.natr
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('natr', 'sma',
|
||||
/**
|
||||
* Normalized average true range indicator (NATR). This series requires
|
||||
* `linkedTo` option to be set and should be loaded after the
|
||||
* `stock/indicators/indicators.js` and `stock/indicators/atr.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/natr
|
||||
* NATR indicator
|
||||
*
|
||||
* @extends plotOptions.atr
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/natr
|
||||
* @optionparent plotOptions.natr
|
||||
*/
|
||||
{
|
||||
tooltip: {
|
||||
valueSuffix: '%'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
requiredIndicators: ['atr'],
|
||||
getValues: function (series, params) {
|
||||
var atrData = (ATR.prototype.getValues.apply(this, arguments)), atrLength = atrData.values.length, period = params.period - 1, yVal = series.yData, i = 0;
|
||||
if (!atrData) {
|
||||
return;
|
||||
}
|
||||
for (; i < atrLength; i++) {
|
||||
atrData.yData[i] = (atrData.values[i][1] / yVal[period][3] * 100);
|
||||
atrData.values[i][1] = atrData.yData[i];
|
||||
period++;
|
||||
}
|
||||
return atrData;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `NATR` series. If the [type](#series.natr.type) option is not specified, it
|
||||
* is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.natr
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/natr
|
||||
* @apioption series.natr
|
||||
*/
|
||||
''; // to include the above in the js output'
|
||||
@@ -0,0 +1,135 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var merge = U.merge, seriesType = U.seriesType;
|
||||
import reduceArrayMixin from '../../Mixins/ReduceArray.js';
|
||||
import multipleLinesMixin from '../../Mixins/MultipleLines.js';
|
||||
var getArrayExtremes = reduceArrayMixin.getArrayExtremes;
|
||||
/**
|
||||
* The Price Channel series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.pc
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('pc', 'sma',
|
||||
/**
|
||||
* Price channel (PC). This series requires the `linkedTo` option to be
|
||||
* set and should be loaded after the `stock/indicators/indicators.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/price-channel
|
||||
* Price Channel
|
||||
*
|
||||
* @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/price-channel
|
||||
* @optionparent plotOptions.pc
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index
|
||||
*/
|
||||
params: {
|
||||
period: 20
|
||||
},
|
||||
lineWidth: 1,
|
||||
topLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Color of the top line. If not set, it's inherited from
|
||||
* [plotOptions.pc.color](#plotOptions.pc.color).
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: '#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1'.split(' ')[2],
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
bottomLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Color of the bottom line. If not set, it's inherited from
|
||||
* [plotOptions.pc.color](#plotOptions.pc.color).
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: '#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1'.split(' ')[8],
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
merge(multipleLinesMixin, {
|
||||
pointArrayMap: ['top', 'middle', 'bottom'],
|
||||
pointValKey: 'middle',
|
||||
nameBase: 'Price Channel',
|
||||
nameComponents: ['period'],
|
||||
linesApiNames: ['topLine', 'bottomLine'],
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0,
|
||||
// 0- date, 1-top line, 2-middle line, 3-bottom line
|
||||
PC = [],
|
||||
// middle line, top line and bottom line
|
||||
ML, TL, BL, date, low = 2, high = 1, xData = [], yData = [], slicedY, extremes, i;
|
||||
if (yValLen < period) {
|
||||
return;
|
||||
}
|
||||
for (i = period; i <= yValLen; i++) {
|
||||
date = xVal[i - 1];
|
||||
slicedY = yVal.slice(i - period, i);
|
||||
extremes = getArrayExtremes(slicedY, low, high);
|
||||
TL = extremes[1];
|
||||
BL = extremes[0];
|
||||
ML = (TL + BL) / 2;
|
||||
PC.push([date, TL, ML, BL]);
|
||||
xData.push(date);
|
||||
yData.push([TL, ML, BL]);
|
||||
}
|
||||
return {
|
||||
values: PC,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* A Price channel indicator. If the [type](#series.pc.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.pc
|
||||
* @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/price-channel
|
||||
* @apioption series.pc
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,131 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, error = U.error, seriesType = U.seriesType;
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
var EMA = H.seriesTypes.ema;
|
||||
/**
|
||||
* The PPO series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.ppo
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('ppo', 'ema',
|
||||
/**
|
||||
* Percentage Price Oscillator. This series requires the
|
||||
* `linkedTo` option to be set and should be loaded after the
|
||||
* `stock/indicators/indicators.js` and `stock/indicators/ema.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/ppo
|
||||
* Percentage Price Oscillator
|
||||
*
|
||||
* @extends plotOptions.ema
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, joinBy, keys, navigatorOptions,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement,
|
||||
* pointRange, pointStart, showInNavigator, stacking
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/ema
|
||||
* @requires stock/indicators/ppo
|
||||
* @optionparent plotOptions.ppo
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Paramters used in calculation of Percentage Price Oscillator series
|
||||
* points.
|
||||
*
|
||||
* @excluding period
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* Periods for Percentage Price Oscillator calculations.
|
||||
*
|
||||
* @type {Array<number>}
|
||||
* @default [12, 26]
|
||||
*/
|
||||
periods: [12, 26]
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series.prototype
|
||||
*/
|
||||
{
|
||||
nameBase: 'PPO',
|
||||
nameComponents: ['periods'],
|
||||
init: function () {
|
||||
var args = arguments, ctx = this;
|
||||
requiredIndicator.isParentLoaded(EMA, 'ema', ctx.type, function (indicator) {
|
||||
indicator.prototype.init.apply(ctx, args);
|
||||
return;
|
||||
});
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var periods = params.periods, index = params.index,
|
||||
// 0- date, 1- Percentage Price Oscillator
|
||||
PPO = [], xData = [], yData = [], periodsOffset,
|
||||
// Shorter Period EMA
|
||||
SPE,
|
||||
// Longer Period EMA
|
||||
LPE, oscillator, i;
|
||||
// Check if periods are correct
|
||||
if (periods.length !== 2 || periods[1] <= periods[0]) {
|
||||
error('Error: "PPO requires two periods. Notice, first period ' +
|
||||
'should be lower than the second one."');
|
||||
return;
|
||||
}
|
||||
SPE = EMA.prototype.getValues.call(this, series, {
|
||||
index: index,
|
||||
period: periods[0]
|
||||
});
|
||||
LPE = EMA.prototype.getValues.call(this, series, {
|
||||
index: index,
|
||||
period: periods[1]
|
||||
});
|
||||
// Check if ema is calculated properly, if not skip
|
||||
if (!SPE || !LPE) {
|
||||
return;
|
||||
}
|
||||
periodsOffset = periods[1] - periods[0];
|
||||
for (i = 0; i < LPE.yData.length; i++) {
|
||||
oscillator = correctFloat((SPE.yData[i + periodsOffset] -
|
||||
LPE.yData[i]) /
|
||||
LPE.yData[i] *
|
||||
100);
|
||||
PPO.push([LPE.xData[i], oscillator]);
|
||||
xData.push(LPE.xData[i]);
|
||||
yData.push(oscillator);
|
||||
}
|
||||
return {
|
||||
values: PPO,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Percentage Price Oscillator` series. If the [type](#series.ppo.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.ppo
|
||||
* @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/ema
|
||||
* @requires stock/indicators/ppo
|
||||
* @apioption series.ppo
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,231 @@
|
||||
/* *
|
||||
*
|
||||
* Parabolic SAR indicator for Highstock
|
||||
*
|
||||
* (c) 2010-2020 Grzegorz Blachliński
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
/* eslint-disable require-jsdoc */
|
||||
// Utils:
|
||||
function toFixed(a, n) {
|
||||
return parseFloat(a.toFixed(n));
|
||||
}
|
||||
function calculateDirection(previousDirection, low, high, PSAR) {
|
||||
if ((previousDirection === 1 && low > PSAR) ||
|
||||
(previousDirection === -1 && high > PSAR)) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/* *
|
||||
* Method for calculating acceleration factor
|
||||
* dir - direction
|
||||
* pDir - previous Direction
|
||||
* eP - extreme point
|
||||
* pEP - previous extreme point
|
||||
* inc - increment for acceleration factor
|
||||
* maxAcc - maximum acceleration factor
|
||||
* initAcc - initial acceleration factor
|
||||
*/
|
||||
function getAccelerationFactor(dir, pDir, eP, pEP, pAcc, inc, maxAcc, initAcc) {
|
||||
if (dir === pDir) {
|
||||
if (dir === 1 && (eP > pEP)) {
|
||||
return (pAcc === maxAcc) ? maxAcc : toFixed(pAcc + inc, 2);
|
||||
}
|
||||
if (dir === -1 && (eP < pEP)) {
|
||||
return (pAcc === maxAcc) ? maxAcc : toFixed(pAcc + inc, 2);
|
||||
}
|
||||
return pAcc;
|
||||
}
|
||||
return initAcc;
|
||||
}
|
||||
function getExtremePoint(high, low, previousDirection, previousExtremePoint) {
|
||||
if (previousDirection === 1) {
|
||||
return (high > previousExtremePoint) ? high : previousExtremePoint;
|
||||
}
|
||||
return (low < previousExtremePoint) ? low : previousExtremePoint;
|
||||
}
|
||||
function getEPMinusPSAR(EP, PSAR) {
|
||||
return EP - PSAR;
|
||||
}
|
||||
function getAccelerationFactorMultiply(accelerationFactor, EPMinusSAR) {
|
||||
return accelerationFactor * EPMinusSAR;
|
||||
}
|
||||
/* *
|
||||
* Method for calculating PSAR
|
||||
* pdir - previous direction
|
||||
* sDir - second previous Direction
|
||||
* PSAR - previous PSAR
|
||||
* pACCMultiply - previous acceleration factor multiply
|
||||
* sLow - second previous low
|
||||
* pLow - previous low
|
||||
* sHigh - second previous high
|
||||
* pHigh - previous high
|
||||
* pEP - previous extreme point
|
||||
*/
|
||||
function getPSAR(pdir, sDir, PSAR, pACCMulti, sLow, pLow, pHigh, sHigh, pEP) {
|
||||
if (pdir === sDir) {
|
||||
if (pdir === 1) {
|
||||
return (PSAR + pACCMulti < Math.min(sLow, pLow)) ?
|
||||
PSAR + pACCMulti :
|
||||
Math.min(sLow, pLow);
|
||||
}
|
||||
return (PSAR + pACCMulti > Math.max(sHigh, pHigh)) ?
|
||||
PSAR + pACCMulti :
|
||||
Math.max(sHigh, pHigh);
|
||||
}
|
||||
return pEP;
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
/**
|
||||
* The Parabolic SAR series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.psar
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('psar', 'sma',
|
||||
/**
|
||||
* Parabolic SAR. This series requires `linkedTo`
|
||||
* option to be set and should be loaded
|
||||
* after `stock/indicators/indicators.js` file.
|
||||
*
|
||||
* @sample stock/indicators/psar
|
||||
* Parabolic SAR Indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/psar
|
||||
* @optionparent plotOptions.psar
|
||||
*/
|
||||
{
|
||||
lineWidth: 0,
|
||||
marker: {
|
||||
enabled: true
|
||||
},
|
||||
states: {
|
||||
hover: {
|
||||
lineWidthPlus: 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @excluding period
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* The initial value for acceleration factor.
|
||||
* Acceleration factor is starting with this value
|
||||
* and increases by specified increment each time
|
||||
* the extreme point makes a new high.
|
||||
* AF can reach a maximum of maxAccelerationFactor,
|
||||
* no matter how long the uptrend extends.
|
||||
*/
|
||||
initialAccelerationFactor: 0.02,
|
||||
/**
|
||||
* The Maximum value for acceleration factor.
|
||||
* AF can reach a maximum of maxAccelerationFactor,
|
||||
* no matter how long the uptrend extends.
|
||||
*/
|
||||
maxAccelerationFactor: 0.2,
|
||||
/**
|
||||
* Acceleration factor increases by increment each time
|
||||
* the extreme point makes a new high.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
increment: 0.02,
|
||||
/**
|
||||
* Index from which PSAR is starting calculation
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
index: 2,
|
||||
/**
|
||||
* Number of maximum decimals that are used in PSAR calculations.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
decimals: 4
|
||||
}
|
||||
}, {
|
||||
nameComponents: false,
|
||||
getValues: function (series, params) {
|
||||
var xVal = series.xData, yVal = series.yData,
|
||||
// Extreme point is the lowest low for falling and highest high
|
||||
// for rising psar - and we are starting with falling
|
||||
extremePoint = yVal[0][1], accelerationFactor = params.initialAccelerationFactor, maxAccelerationFactor = params.maxAccelerationFactor, increment = params.increment,
|
||||
// Set initial acc factor (for every new trend!)
|
||||
initialAccelerationFactor = params.initialAccelerationFactor, PSAR = yVal[0][2], decimals = params.decimals, index = params.index, PSARArr = [], xData = [], yData = [], previousDirection = 1, direction, EPMinusPSAR, accelerationFactorMultiply, newDirection, prevLow, prevPrevLow, prevHigh, prevPrevHigh, newExtremePoint, high, low, ind;
|
||||
if (index >= yVal.length) {
|
||||
return;
|
||||
}
|
||||
for (ind = 0; ind < index; ind++) {
|
||||
extremePoint = Math.max(yVal[ind][1], extremePoint);
|
||||
PSAR = Math.min(yVal[ind][2], toFixed(PSAR, decimals));
|
||||
}
|
||||
direction = (yVal[ind][1] > PSAR) ? 1 : -1;
|
||||
EPMinusPSAR = getEPMinusPSAR(extremePoint, PSAR);
|
||||
accelerationFactor = params.initialAccelerationFactor;
|
||||
accelerationFactorMultiply = getAccelerationFactorMultiply(accelerationFactor, EPMinusPSAR);
|
||||
PSARArr.push([xVal[index], PSAR]);
|
||||
xData.push(xVal[index]);
|
||||
yData.push(toFixed(PSAR, decimals));
|
||||
for (ind = index + 1; ind < yVal.length; ind++) {
|
||||
prevLow = yVal[ind - 1][2];
|
||||
prevPrevLow = yVal[ind - 2][2];
|
||||
prevHigh = yVal[ind - 1][1];
|
||||
prevPrevHigh = yVal[ind - 2][1];
|
||||
high = yVal[ind][1];
|
||||
low = yVal[ind][2];
|
||||
// Null points break PSAR
|
||||
if (prevPrevLow !== null &&
|
||||
prevPrevHigh !== null &&
|
||||
prevLow !== null &&
|
||||
prevHigh !== null &&
|
||||
high !== null &&
|
||||
low !== null) {
|
||||
PSAR = getPSAR(direction, previousDirection, PSAR, accelerationFactorMultiply, prevPrevLow, prevLow, prevHigh, prevPrevHigh, extremePoint);
|
||||
newExtremePoint = getExtremePoint(high, low, direction, extremePoint);
|
||||
newDirection = calculateDirection(previousDirection, low, high, PSAR);
|
||||
accelerationFactor = getAccelerationFactor(newDirection, direction, newExtremePoint, extremePoint, accelerationFactor, increment, maxAccelerationFactor, initialAccelerationFactor);
|
||||
EPMinusPSAR = getEPMinusPSAR(newExtremePoint, PSAR);
|
||||
accelerationFactorMultiply = getAccelerationFactorMultiply(accelerationFactor, EPMinusPSAR);
|
||||
PSARArr.push([xVal[ind], toFixed(PSAR, decimals)]);
|
||||
xData.push(xVal[ind]);
|
||||
yData.push(toFixed(PSAR, decimals));
|
||||
previousDirection = direction;
|
||||
direction = newDirection;
|
||||
extremePoint = newExtremePoint;
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: PSARArr,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `PSAR` series. If the [type](#series.psar.type) option is not specified, it
|
||||
* is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.psar
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/psar
|
||||
* @apioption series.psar
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,288 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var defined = U.defined, isArray = U.isArray, seriesType = U.seriesType;
|
||||
var SMA = H.seriesTypes.sma;
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function destroyExtraLabels(point, functionName) {
|
||||
var props = point.series.pointArrayMap, prop, i = props.length;
|
||||
SMA.prototype.pointClass.prototype[functionName].call(point);
|
||||
while (i--) {
|
||||
prop = 'dataLabel' + props[i];
|
||||
// S4 dataLabel could be removed by parent method:
|
||||
if (point[prop] && point[prop].element) {
|
||||
point[prop].destroy();
|
||||
}
|
||||
point[prop] = null;
|
||||
}
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* The Pivot Points series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.pivotpoints
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('pivotpoints', 'sma',
|
||||
/**
|
||||
* Pivot points indicator. This series requires the `linkedTo` option to be
|
||||
* set and should be loaded after `stock/indicators/indicators.js` file.
|
||||
*
|
||||
* @sample stock/indicators/pivot-points
|
||||
* Pivot points
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/pivotpoints
|
||||
* @optionparent plotOptions.pivotpoints
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index
|
||||
*/
|
||||
params: {
|
||||
period: 28,
|
||||
/**
|
||||
* Algorithm used to calculate ressistance and support lines based
|
||||
* on pivot points. Implemented algorithms: `'standard'`,
|
||||
* `'fibonacci'` and `'camarilla'`
|
||||
*/
|
||||
algorithm: 'standard'
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
enableMouseTracking: false,
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
format: '{point.pivotLine}'
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Pivot Points',
|
||||
pointArrayMap: ['R4', 'R3', 'R2', 'R1', 'P', 'S1', 'S2', 'S3', 'S4'],
|
||||
pointValKey: 'P',
|
||||
toYData: function (point) {
|
||||
return [point.P]; // The rest should not affect extremes
|
||||
},
|
||||
translate: function () {
|
||||
var indicator = this;
|
||||
SMA.prototype.translate.apply(indicator);
|
||||
indicator.points.forEach(function (point) {
|
||||
indicator.pointArrayMap.forEach(function (value) {
|
||||
if (defined(point[value])) {
|
||||
point['plot' + value] = (indicator.yAxis.toPixels(point[value], true));
|
||||
}
|
||||
});
|
||||
});
|
||||
// Pivot points are rendered as horizontal lines
|
||||
// And last point start not from the next one (as it's the last one)
|
||||
// But from the approximated last position in a given range
|
||||
indicator.plotEndPoint = indicator.xAxis.toPixels(indicator.endPoint, true);
|
||||
},
|
||||
getGraphPath: function (points) {
|
||||
var indicator = this, pointsLength = points.length, allPivotPoints = ([[], [], [], [], [], [], [], [], []]), path = [], endPoint = indicator.plotEndPoint, pointArrayMapLength = indicator.pointArrayMap.length, position, point, i;
|
||||
while (pointsLength--) {
|
||||
point = points[pointsLength];
|
||||
for (i = 0; i < pointArrayMapLength; i++) {
|
||||
position = indicator.pointArrayMap[i];
|
||||
if (defined(point[position])) {
|
||||
allPivotPoints[i].push({
|
||||
// Start left:
|
||||
plotX: point.plotX,
|
||||
plotY: point['plot' + position],
|
||||
isNull: false
|
||||
}, {
|
||||
// Go to right:
|
||||
plotX: endPoint,
|
||||
plotY: point['plot' + position],
|
||||
isNull: false
|
||||
}, {
|
||||
// And add null points in path to generate breaks:
|
||||
plotX: endPoint,
|
||||
plotY: null,
|
||||
isNull: true
|
||||
});
|
||||
}
|
||||
}
|
||||
endPoint = point.plotX;
|
||||
}
|
||||
allPivotPoints.forEach(function (pivotPoints) {
|
||||
path = path.concat(SMA.prototype.getGraphPath.call(indicator, pivotPoints));
|
||||
});
|
||||
return path;
|
||||
},
|
||||
// TODO: Rewrite this logic to use multiple datalabels
|
||||
drawDataLabels: function () {
|
||||
var indicator = this, pointMapping = indicator.pointArrayMap, currentLabel, pointsLength, point, i;
|
||||
if (indicator.options.dataLabels.enabled) {
|
||||
pointsLength = indicator.points.length;
|
||||
// For every Ressitance/Support group we need to render labels.
|
||||
// Add one more item, which will just store dataLabels from
|
||||
// previous iteration
|
||||
pointMapping.concat([false]).forEach(function (position, k) {
|
||||
i = pointsLength;
|
||||
while (i--) {
|
||||
point = indicator.points[i];
|
||||
if (!position) {
|
||||
// Store S4 dataLabel too:
|
||||
point['dataLabel' + pointMapping[k - 1]] =
|
||||
point.dataLabel;
|
||||
}
|
||||
else {
|
||||
point.y = point[position];
|
||||
point.pivotLine = position;
|
||||
point.plotY = point['plot' + position];
|
||||
currentLabel = point['dataLabel' + position];
|
||||
// Store previous label
|
||||
if (k) {
|
||||
point['dataLabel' + pointMapping[k - 1]] = point.dataLabel;
|
||||
}
|
||||
if (!point.dataLabels) {
|
||||
point.dataLabels = [];
|
||||
}
|
||||
point.dataLabels[0] = point.dataLabel =
|
||||
currentLabel =
|
||||
currentLabel && currentLabel.element ?
|
||||
currentLabel :
|
||||
null;
|
||||
}
|
||||
}
|
||||
SMA.prototype.drawDataLabels.apply(indicator, arguments);
|
||||
});
|
||||
}
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, placement = this[params.algorithm + 'Placement'],
|
||||
// 0- from, 1- to, 2- R1, 3- R2, 4- pivot, 5- S1 etc.
|
||||
PP = [], endTimestamp, xData = [], yData = [], slicedXLen, slicedX, slicedY, lastPP, pivot, avg, i;
|
||||
// Pivot Points requires high, low and close values
|
||||
if (xVal.length < period ||
|
||||
!isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
for (i = period + 1; i <= yValLen + period; i += period) {
|
||||
slicedX = xVal.slice(i - period - 1, i);
|
||||
slicedY = yVal.slice(i - period - 1, i);
|
||||
slicedXLen = slicedX.length;
|
||||
endTimestamp = slicedX[slicedXLen - 1];
|
||||
pivot = this.getPivotAndHLC(slicedY);
|
||||
avg = placement(pivot);
|
||||
lastPP = PP.push([endTimestamp]
|
||||
.concat(avg));
|
||||
xData.push(endTimestamp);
|
||||
yData.push(PP[lastPP - 1].slice(1));
|
||||
}
|
||||
// We don't know exact position in ordinal axis
|
||||
// So we use simple logic:
|
||||
// Get first point in last range, calculate visible average range
|
||||
// and multiply by period
|
||||
this.endPoint = slicedX[0] + ((endTimestamp - slicedX[0]) /
|
||||
slicedXLen) * period;
|
||||
return {
|
||||
values: PP,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
},
|
||||
getPivotAndHLC: function (values) {
|
||||
var high = -Infinity, low = Infinity, close = values[values.length - 1][3], pivot;
|
||||
values.forEach(function (p) {
|
||||
high = Math.max(high, p[1]);
|
||||
low = Math.min(low, p[2]);
|
||||
});
|
||||
pivot = (high + low + close) / 3;
|
||||
return [pivot, high, low, close];
|
||||
},
|
||||
standardPlacement: function (values) {
|
||||
var diff = values[1] - values[2], avg = [
|
||||
null,
|
||||
null,
|
||||
values[0] + diff,
|
||||
values[0] * 2 - values[2],
|
||||
values[0],
|
||||
values[0] * 2 - values[1],
|
||||
values[0] - diff,
|
||||
null,
|
||||
null
|
||||
];
|
||||
return avg;
|
||||
},
|
||||
camarillaPlacement: function (values) {
|
||||
var diff = values[1] - values[2], avg = [
|
||||
values[3] + diff * 1.5,
|
||||
values[3] + diff * 1.25,
|
||||
values[3] + diff * 1.1666,
|
||||
values[3] + diff * 1.0833,
|
||||
values[0],
|
||||
values[3] - diff * 1.0833,
|
||||
values[3] - diff * 1.1666,
|
||||
values[3] - diff * 1.25,
|
||||
values[3] - diff * 1.5
|
||||
];
|
||||
return avg;
|
||||
},
|
||||
fibonacciPlacement: function (values) {
|
||||
var diff = values[1] - values[2], avg = [
|
||||
null,
|
||||
values[0] + diff,
|
||||
values[0] + diff * 0.618,
|
||||
values[0] + diff * 0.382,
|
||||
values[0],
|
||||
values[0] - diff * 0.382,
|
||||
values[0] - diff * 0.618,
|
||||
values[0] - diff,
|
||||
null
|
||||
];
|
||||
return avg;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Point#
|
||||
*/
|
||||
{
|
||||
// Destroy labels:
|
||||
// This method is called when cropping data:
|
||||
destroyElements: function () {
|
||||
destroyExtraLabels(this, 'destroyElements');
|
||||
},
|
||||
// This method is called when removing points, e.g. series.update()
|
||||
destroy: function () {
|
||||
destroyExtraLabels(this, 'destroyElements');
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A pivot points indicator. If the [type](#series.pivotpoints.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.pivotpoints
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/pivotpoints
|
||||
* @apioption series.pivotpoints
|
||||
*/
|
||||
''; // to include the above in the js output'
|
||||
@@ -0,0 +1,213 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, merge = U.merge, seriesType = U.seriesType;
|
||||
var SMA = H.seriesTypes.sma;
|
||||
/**
|
||||
* The Price Envelopes series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.priceenvelopes
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('priceenvelopes', 'sma',
|
||||
/**
|
||||
* Price envelopes indicator based on [SMA](#plotOptions.sma) calculations.
|
||||
* This series requires the `linkedTo` option to be set and should be loaded
|
||||
* after the `stock/indicators/indicators.js` file.
|
||||
*
|
||||
* @sample stock/indicators/price-envelopes
|
||||
* Price envelopes
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/price-envelopes
|
||||
* @optionparent plotOptions.priceenvelopes
|
||||
*/
|
||||
{
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span><b> {series.name}</b><br/>Top: {point.top}<br/>Middle: {point.middle}<br/>Bottom: {point.bottom}<br/>'
|
||||
},
|
||||
params: {
|
||||
period: 20,
|
||||
/**
|
||||
* Percentage above the moving average that should be displayed.
|
||||
* 0.1 means 110%. Relative to the calculated value.
|
||||
*/
|
||||
topBand: 0.1,
|
||||
/**
|
||||
* Percentage below the moving average that should be displayed.
|
||||
* 0.1 means 90%. Relative to the calculated value.
|
||||
*/
|
||||
bottomBand: 0.1
|
||||
},
|
||||
/**
|
||||
* Bottom line options.
|
||||
*/
|
||||
bottomLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line. If not set, it's inherited from
|
||||
* [plotOptions.priceenvelopes.color](
|
||||
* #plotOptions.priceenvelopes.color).
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Top line options.
|
||||
*
|
||||
* @extends plotOptions.priceenvelopes.bottomLine
|
||||
*/
|
||||
topLine: {
|
||||
styles: {
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameComponents: ['period', 'topBand', 'bottomBand'],
|
||||
nameBase: 'Price envelopes',
|
||||
pointArrayMap: ['top', 'middle', 'bottom'],
|
||||
parallelArrays: ['x', 'y', 'top', 'bottom'],
|
||||
pointValKey: 'middle',
|
||||
init: function () {
|
||||
SMA.prototype.init.apply(this, arguments);
|
||||
// Set default color for lines:
|
||||
this.options = merge({
|
||||
topLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
},
|
||||
bottomLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
}
|
||||
}, this.options);
|
||||
},
|
||||
toYData: function (point) {
|
||||
return [point.top, point.middle, point.bottom];
|
||||
},
|
||||
translate: function () {
|
||||
var indicator = this, translatedEnvelopes = ['plotTop', 'plotMiddle', 'plotBottom'];
|
||||
SMA.prototype.translate.apply(indicator);
|
||||
indicator.points.forEach(function (point) {
|
||||
[point.top, point.middle, point.bottom].forEach(function (value, i) {
|
||||
if (value !== null) {
|
||||
point[translatedEnvelopes[i]] =
|
||||
indicator.yAxis.toPixels(value, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
drawGraph: function () {
|
||||
var indicator = this, middleLinePoints = indicator.points, pointsLength = middleLinePoints.length, middleLineOptions = (indicator.options), middleLinePath = indicator.graph, gappedExtend = {
|
||||
options: {
|
||||
gapSize: middleLineOptions.gapSize
|
||||
}
|
||||
}, deviations = [[], []], // top and bottom point place holders
|
||||
point;
|
||||
// Generate points for top and bottom lines:
|
||||
while (pointsLength--) {
|
||||
point = middleLinePoints[pointsLength];
|
||||
deviations[0].push({
|
||||
plotX: point.plotX,
|
||||
plotY: point.plotTop,
|
||||
isNull: point.isNull
|
||||
});
|
||||
deviations[1].push({
|
||||
plotX: point.plotX,
|
||||
plotY: point.plotBottom,
|
||||
isNull: point.isNull
|
||||
});
|
||||
}
|
||||
// Modify options and generate lines:
|
||||
['topLine', 'bottomLine'].forEach(function (lineName, i) {
|
||||
indicator.points = deviations[i];
|
||||
indicator.options = merge(middleLineOptions[lineName].styles, gappedExtend);
|
||||
indicator.graph = indicator['graph' + lineName];
|
||||
SMA.prototype.drawGraph.call(indicator);
|
||||
// Now save lines:
|
||||
indicator['graph' + lineName] = indicator.graph;
|
||||
});
|
||||
// Restore options and draw a middle line:
|
||||
indicator.points = middleLinePoints;
|
||||
indicator.options = middleLineOptions;
|
||||
indicator.graph = middleLinePath;
|
||||
SMA.prototype.drawGraph.call(indicator);
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, topPercent = params.topBand, botPercent = params.bottomBand, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0,
|
||||
// 0- date, 1-top line, 2-middle line, 3-bottom line
|
||||
PE = [],
|
||||
// middle line, top line and bottom line
|
||||
ML, TL, BL, date, xData = [], yData = [], slicedX, slicedY, point, i;
|
||||
// Price envelopes requires close value
|
||||
if (xVal.length < period ||
|
||||
!isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
for (i = period; i <= yValLen; i++) {
|
||||
slicedX = xVal.slice(i - period, i);
|
||||
slicedY = yVal.slice(i - period, i);
|
||||
point = SMA.prototype.getValues.call(this, {
|
||||
xData: slicedX,
|
||||
yData: slicedY
|
||||
}, params);
|
||||
date = point.xData[0];
|
||||
ML = point.yData[0];
|
||||
TL = ML * (1 + topPercent);
|
||||
BL = ML * (1 - botPercent);
|
||||
PE.push([date, TL, ML, BL]);
|
||||
xData.push(date);
|
||||
yData.push([TL, ML, BL]);
|
||||
}
|
||||
return {
|
||||
values: PE,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A price envelopes indicator. If the [type](#series.priceenvelopes.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.priceenvelopes
|
||||
* @since 6.0.0
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/price-envelopes
|
||||
* @apioption series.priceenvelopes
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,132 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Kacper Madej
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
/* eslint-disable require-jsdoc */
|
||||
// Utils:
|
||||
function populateAverage(xVal, yVal, i, period, index) {
|
||||
/* Calculated as:
|
||||
|
||||
(Closing Price [today] - Closing Price [n days ago]) /
|
||||
Closing Price [n days ago] * 100
|
||||
|
||||
Return y as null when avoiding division by zero */
|
||||
var nDaysAgoY, rocY;
|
||||
if (index < 0) {
|
||||
// y data given as an array of values
|
||||
nDaysAgoY = yVal[i - period];
|
||||
rocY = nDaysAgoY ?
|
||||
(yVal[i] - nDaysAgoY) / nDaysAgoY * 100 :
|
||||
null;
|
||||
}
|
||||
else {
|
||||
// y data given as an array of arrays and the index should be used
|
||||
nDaysAgoY = yVal[i - period][index];
|
||||
rocY = nDaysAgoY ?
|
||||
(yVal[i][index] - nDaysAgoY) / nDaysAgoY * 100 :
|
||||
null;
|
||||
}
|
||||
return [xVal[i], rocY];
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
/**
|
||||
* The ROC series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.roc
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('roc', 'sma',
|
||||
/**
|
||||
* Rate of change indicator (ROC). The indicator value for each point
|
||||
* is defined as:
|
||||
*
|
||||
* `(C - Cn) / Cn * 100`
|
||||
*
|
||||
* where: `C` is the close value of the point of the same x in the
|
||||
* linked series and `Cn` is the close value of the point `n` periods
|
||||
* ago. `n` is set through [period](#plotOptions.roc.params.period).
|
||||
*
|
||||
* This series requires `linkedTo` option to be set.
|
||||
*
|
||||
* @sample stock/indicators/roc
|
||||
* Rate of change indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/roc
|
||||
* @optionparent plotOptions.roc
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
index: 3,
|
||||
period: 9
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Rate of Change',
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, ROC = [], xData = [], yData = [], i, index = -1, ROCPoint;
|
||||
// Period is used as a number of time periods ago, so we need more
|
||||
// (at least 1 more) data than the period value
|
||||
if (xVal.length <= period) {
|
||||
return;
|
||||
}
|
||||
// Switch index for OHLC / Candlestick / Arearange
|
||||
if (isArray(yVal[0])) {
|
||||
index = params.index;
|
||||
}
|
||||
// i = period <-- skip first N-points
|
||||
// Calculate value one-by-one for each period in visible data
|
||||
for (i = period; i < yValLen; i++) {
|
||||
ROCPoint = populateAverage(xVal, yVal, i, period, index);
|
||||
ROC.push(ROCPoint);
|
||||
xData.push(ROCPoint[0]);
|
||||
yData.push(ROCPoint[1]);
|
||||
}
|
||||
return {
|
||||
values: ROC,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `ROC` series. If the [type](#series.wma.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* Rate of change indicator (ROC). The indicator value for each point
|
||||
* is defined as:
|
||||
*
|
||||
* `(C - Cn) / Cn * 100`
|
||||
*
|
||||
* where: `C` is the close value of the point of the same x in the
|
||||
* linked series and `Cn` is the close value of the point `n` periods
|
||||
* ago. `n` is set through [period](#series.roc.params.period).
|
||||
*
|
||||
* This series requires `linkedTo` option to be set.
|
||||
*
|
||||
* @extends series,plotOptions.roc
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/roc
|
||||
* @apioption series.roc
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,131 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
/* eslint-disable require-jsdoc */
|
||||
// Utils:
|
||||
function toFixed(a, n) {
|
||||
return parseFloat(a.toFixed(n));
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
/**
|
||||
* The RSI series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.rsi
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('rsi', 'sma',
|
||||
/**
|
||||
* Relative strength index (RSI) technical indicator. This series
|
||||
* requires the `linkedTo` option to be set and should be loaded after
|
||||
* the `stock/indicators/indicators.js` file.
|
||||
*
|
||||
* @sample stock/indicators/rsi
|
||||
* RSI indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/rsi
|
||||
* @optionparent plotOptions.rsi
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index
|
||||
*/
|
||||
params: {
|
||||
period: 14,
|
||||
/**
|
||||
* Number of maximum decimals that are used in RSI calculations.
|
||||
*/
|
||||
decimals: 4
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, decimals = params.decimals,
|
||||
// RSI starts calculations from the second point
|
||||
// Cause we need to calculate change between two points
|
||||
range = 1, RSI = [], xData = [], yData = [], index = 3, gain = 0, loss = 0, RSIPoint, change, avgGain, avgLoss, i;
|
||||
// RSI requires close value
|
||||
if ((xVal.length < period) || !isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
// Calculate changes for first N points
|
||||
while (range < period) {
|
||||
change = toFixed(yVal[range][index] - yVal[range - 1][index], decimals);
|
||||
if (change > 0) {
|
||||
gain += change;
|
||||
}
|
||||
else {
|
||||
loss += Math.abs(change);
|
||||
}
|
||||
range++;
|
||||
}
|
||||
// Average for first n-1 points:
|
||||
avgGain = toFixed(gain / (period - 1), decimals);
|
||||
avgLoss = toFixed(loss / (period - 1), decimals);
|
||||
for (i = range; i < yValLen; i++) {
|
||||
change = toFixed(yVal[i][index] - yVal[i - 1][index], decimals);
|
||||
if (change > 0) {
|
||||
gain = change;
|
||||
loss = 0;
|
||||
}
|
||||
else {
|
||||
gain = 0;
|
||||
loss = Math.abs(change);
|
||||
}
|
||||
// Calculate smoothed averages, RS, RSI values:
|
||||
avgGain = toFixed((avgGain * (period - 1) + gain) / period, decimals);
|
||||
avgLoss = toFixed((avgLoss * (period - 1) + loss) / period, decimals);
|
||||
// If average-loss is equal zero, then by definition RSI is set
|
||||
// to 100:
|
||||
if (avgLoss === 0) {
|
||||
RSIPoint = 100;
|
||||
// If average-gain is equal zero, then by definition RSI is set
|
||||
// to 0:
|
||||
}
|
||||
else if (avgGain === 0) {
|
||||
RSIPoint = 0;
|
||||
}
|
||||
else {
|
||||
RSIPoint = toFixed(100 - (100 / (1 + (avgGain / avgLoss))), decimals);
|
||||
}
|
||||
RSI.push([xVal[i], RSIPoint]);
|
||||
xData.push(xVal[i]);
|
||||
yData.push(RSIPoint);
|
||||
}
|
||||
return {
|
||||
values: RSI,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `RSI` series. If the [type](#series.rsi.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.rsi
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/rsi
|
||||
* @apioption series.rsi
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,375 @@
|
||||
/**
|
||||
*
|
||||
* (c) 2010-2020 Kamil Kulig
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
/**
|
||||
* Linear regression series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.linearregression
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('linearRegression', 'sma',
|
||||
/**
|
||||
* Linear regression indicator. This series requires `linkedTo` option to be
|
||||
* set.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/linear-regression
|
||||
* Linear regression indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @optionparent plotOptions.linearregression
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
/**
|
||||
* Unit (in milliseconds) for the x axis distances used to compute
|
||||
* the regression line paramters (slope & intercept) for every
|
||||
* range. In Highstock the x axis values are always represented in
|
||||
* milliseconds which may cause that distances between points are
|
||||
* "big" integer numbers.
|
||||
*
|
||||
* Highstock's linear regression algorithm (least squares method)
|
||||
* will utilize these "big" integers for finding the slope and the
|
||||
* intercept of the regression line for each period. In consequence,
|
||||
* this value may be a very "small" decimal number that's hard to
|
||||
* interpret by a human.
|
||||
*
|
||||
* For instance: `xAxisUnit` equealed to `86400000` ms (1 day)
|
||||
* forces the algorithm to treat `86400000` as `1` while computing
|
||||
* the slope and the intercept. This may enchance the legiblitity of
|
||||
* the indicator's values.
|
||||
*
|
||||
* Default value is the closest distance between two data points.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/linear-regression-xaxisunit
|
||||
* xAxisUnit set to 1 minute
|
||||
*
|
||||
* @example
|
||||
* // In Liniear Regression Slope Indicator series `xAxisUnit` is
|
||||
* // `86400000` (1 day) and period is `3`. There're 3 points in the
|
||||
* // base series:
|
||||
*
|
||||
* data: [
|
||||
* [Date.UTC(2020, 0, 1), 1],
|
||||
* [Date.UTC(2020, 0, 2), 3],
|
||||
* [Date.UTC(2020, 0, 3), 5]
|
||||
* ]
|
||||
*
|
||||
* // This will produce one point in the indicator series that has a
|
||||
* // `y` value of `2` (slope of the regression line). If we change
|
||||
* // the `xAxisUnit` to `1` (ms) the value of the indicator's point
|
||||
* // will be `2.3148148148148148e-8` which is harder to interpert
|
||||
* // for a human.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highstock
|
||||
*/
|
||||
xAxisUnit: void 0
|
||||
},
|
||||
tooltip: {
|
||||
valueDecimals: 4
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Linear Regression Indicator',
|
||||
/**
|
||||
* Return the slope and intercept of a straight line function.
|
||||
* @private
|
||||
* @param {Highcharts.LinearRegressionIndicator} this indicator to use
|
||||
* @param {Array<number>} xData - list of all x coordinates in a period
|
||||
* @param {Array<number>} yData - list of all y coordinates in a period
|
||||
* @return {Highcharts.RegressionLineParametersObject}
|
||||
* object that contains the slope and the intercept
|
||||
* of a straight line function
|
||||
*/
|
||||
getRegressionLineParameters: function (xData, yData) {
|
||||
// least squares method
|
||||
var yIndex = this.options.params.index, getSingleYValue = function (yValue, yIndex) {
|
||||
return isArray(yValue) ? yValue[yIndex] : yValue;
|
||||
}, xSum = xData.reduce(function (accX, val) {
|
||||
return val + accX;
|
||||
}, 0), ySum = yData.reduce(function (accY, val) {
|
||||
return getSingleYValue(val, yIndex) + accY;
|
||||
}, 0), xMean = xSum / xData.length, yMean = ySum / yData.length, xError, yError, formulaNumerator = 0, formulaDenominator = 0, i, slope;
|
||||
for (i = 0; i < xData.length; i++) {
|
||||
xError = xData[i] - xMean;
|
||||
yError = getSingleYValue(yData[i], yIndex) - yMean;
|
||||
formulaNumerator += xError * yError;
|
||||
formulaDenominator += Math.pow(xError, 2);
|
||||
}
|
||||
slope = formulaDenominator ?
|
||||
formulaNumerator / formulaDenominator : 0; // don't divide by 0
|
||||
return {
|
||||
slope: slope,
|
||||
intercept: yMean - slope * xMean
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Return the y value on a straight line.
|
||||
* @private
|
||||
* @param {Highcharts.RegressionLineParametersObject} lineParameters
|
||||
* object that contains the slope and the intercept
|
||||
* of a straight line function
|
||||
* @param {number} endPointX - x coordinate of the point
|
||||
* @return {number} - y value of the point that lies on the line
|
||||
*/
|
||||
getEndPointY: function (lineParameters, endPointX) {
|
||||
return lineParameters.slope * endPointX + lineParameters.intercept;
|
||||
},
|
||||
/**
|
||||
* Transform the coordinate system so that x values start at 0 and
|
||||
* apply xAxisUnit.
|
||||
* @private
|
||||
* @param {Array<number>} xData - list of all x coordinates in a period
|
||||
* @param {number} xAxisUnit - option (see the API)
|
||||
* @return {Array<number>} - array of transformed x data
|
||||
*/
|
||||
transformXData: function (xData, xAxisUnit) {
|
||||
var xOffset = xData[0];
|
||||
return xData.map(function (xValue) {
|
||||
return (xValue - xOffset) / xAxisUnit;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Find the closest distance between points in the base series.
|
||||
* @private
|
||||
* @param {Array<number>} xData
|
||||
list of all x coordinates in the base series
|
||||
* @return {number} - closest distance between points in the base series
|
||||
*/
|
||||
findClosestDistance: function (xData) {
|
||||
var distance, closestDistance, i;
|
||||
for (i = 1; i < xData.length - 1; i++) {
|
||||
distance = xData[i] - xData[i - 1];
|
||||
if (distance > 0 &&
|
||||
(typeof closestDistance === 'undefined' ||
|
||||
distance < closestDistance)) {
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
return closestDistance;
|
||||
},
|
||||
// Required to be implemented - starting point for indicator's logic
|
||||
getValues: function (baseSeries, regressionSeriesParams) {
|
||||
var xData = baseSeries.xData, yData = baseSeries.yData, period = regressionSeriesParams.period, lineParameters, i, periodStart, periodEnd,
|
||||
// format required to be returned
|
||||
indicatorData = {
|
||||
xData: [],
|
||||
yData: [],
|
||||
values: []
|
||||
}, endPointX, endPointY, periodXData, periodYData, periodTransformedXData, xAxisUnit = this.options.params.xAxisUnit ||
|
||||
this.findClosestDistance(xData);
|
||||
// Iteration logic: x value of the last point within the period
|
||||
// (end point) is used to represent the y value (regression)
|
||||
// of the entire period.
|
||||
for (i = period - 1; i <= xData.length - 1; i++) {
|
||||
periodStart = i - period + 1; // adjusted for slice() function
|
||||
periodEnd = i + 1; // (as above)
|
||||
endPointX = xData[i];
|
||||
periodXData = xData.slice(periodStart, periodEnd);
|
||||
periodYData = yData.slice(periodStart, periodEnd);
|
||||
periodTransformedXData = this.transformXData(periodXData, xAxisUnit);
|
||||
lineParameters = this.getRegressionLineParameters(periodTransformedXData, periodYData);
|
||||
endPointY = this.getEndPointY(lineParameters, periodTransformedXData[periodTransformedXData.length - 1]);
|
||||
// @todo this is probably not used anywhere
|
||||
indicatorData.values.push({
|
||||
regressionLineParameters: lineParameters,
|
||||
x: endPointX,
|
||||
y: endPointY
|
||||
});
|
||||
indicatorData.xData.push(endPointX);
|
||||
indicatorData.yData.push(endPointY);
|
||||
}
|
||||
return indicatorData;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A linear regression series. If the [type](#series.linearregression.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.linearregression
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser,dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @apioption series.linearregression
|
||||
*/
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* The Linear Regression Slope series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.linearRegressionSlope
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('linearRegressionSlope', 'linearRegression',
|
||||
/**
|
||||
* Linear regression slope indicator. This series requires `linkedTo`
|
||||
* option to be set.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/linear-regression-slope
|
||||
* Linear regression slope indicator
|
||||
*
|
||||
* @extends plotOptions.linearregression
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @optionparent plotOptions.linearregressionslope
|
||||
*/
|
||||
{},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Linear Regression Slope Indicator',
|
||||
getEndPointY: function (lineParameters) {
|
||||
return lineParameters.slope;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A linear regression slope series. If the
|
||||
* [type](#series.linearregressionslope.type) option is not specified, it is
|
||||
* inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.linearregressionslope
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser,dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @apioption series.linearregressionslope
|
||||
*/
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* The Linear Regression Intercept series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.linearRegressionIntercept
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('linearRegressionIntercept', 'linearRegression',
|
||||
/**
|
||||
* Linear regression intercept indicator. This series requires `linkedTo`
|
||||
* option to be set.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/linear-regression-intercept
|
||||
* Linear intercept slope indicator
|
||||
*
|
||||
* @extends plotOptions.linearregression
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @optionparent plotOptions.linearregressionintercept
|
||||
*/
|
||||
{},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Linear Regression Intercept Indicator',
|
||||
getEndPointY: function (lineParameters) {
|
||||
return lineParameters.intercept;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A linear regression intercept series. If the
|
||||
* [type](#series.linearregressionintercept.type) option is not specified, it is
|
||||
* inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.linearregressionintercept
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser,dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @apioption series.linearregressionintercept
|
||||
*/
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* The Linear Regression Angle series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.linearRegressionAngle
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('linearRegressionAngle', 'linearRegression',
|
||||
/**
|
||||
* Linear regression angle indicator. This series requires `linkedTo`
|
||||
* option to be set.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/linear-regression-angle
|
||||
* Linear intercept angle indicator
|
||||
*
|
||||
* @extends plotOptions.linearregression
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @optionparent plotOptions.linearregressionangle
|
||||
*/
|
||||
{
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span>' +
|
||||
'{series.name}: <b>{point.y}°</b><br/>'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Linear Regression Angle Indicator',
|
||||
/**
|
||||
* Convert a slope of a line to angle (in degrees) between
|
||||
* the line and x axis
|
||||
* @private
|
||||
* @param {number} slope of the straight line function
|
||||
* @return {number} angle in degrees
|
||||
*/
|
||||
slopeToAngle: function (slope) {
|
||||
return Math.atan(slope) * (180 / Math.PI); // rad to deg
|
||||
},
|
||||
getEndPointY: function (lineParameters) {
|
||||
return this.slopeToAngle(lineParameters.slope);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A linear regression intercept series. If the
|
||||
* [type](#series.linearregressionangle.type) option is not specified, it is
|
||||
* inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.linearregressionangle
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser,dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/regressions
|
||||
* @apioption series.linearregressionangle
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,114 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
var seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* The Slow Stochastic series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.slowstochastic
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('slowstochastic', 'stochastic',
|
||||
/**
|
||||
* Slow Stochastic oscillator. This series requires the `linkedTo` option
|
||||
* to be set and should be loaded after `stock/indicators/indicators.js`
|
||||
* and `stock/indicators/stochastic.js` files.
|
||||
*
|
||||
* @sample stock/indicators/slow-stochastic
|
||||
* Slow Stochastic oscillator
|
||||
*
|
||||
* @extends plotOptions.stochastic
|
||||
* @since 8.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/stochastic
|
||||
* @requires stock/indicators/slowstochastic
|
||||
* @optionparent plotOptions.slowstochastic
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
/**
|
||||
* Periods for Slow Stochastic oscillator: [%K, %D, SMA(%D)].
|
||||
*
|
||||
* @type {Array<number,number,number>}
|
||||
* @default [14, 3, 3]
|
||||
*/
|
||||
periods: [14, 3, 3]
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Slow Stochastic',
|
||||
init: function () {
|
||||
var args = arguments, ctx = this;
|
||||
requiredIndicator.isParentLoaded(H.seriesTypes.stochastic, 'stochastic', ctx.type, function (indicator) {
|
||||
indicator.prototype.init.apply(ctx, args);
|
||||
return;
|
||||
});
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var periods = params.periods, fastValues = seriesTypes.stochastic.prototype.getValues.call(this, series, params), slowValues = {
|
||||
values: [],
|
||||
xData: [],
|
||||
yData: []
|
||||
};
|
||||
var i = 0;
|
||||
if (!fastValues) {
|
||||
return;
|
||||
}
|
||||
slowValues.xData = fastValues.xData.slice(periods[1] - 1);
|
||||
var fastYData = fastValues.yData.slice(periods[1] - 1);
|
||||
// Get SMA(%D)
|
||||
var smoothedValues = seriesTypes.sma.prototype.getValues.call(this, {
|
||||
xData: slowValues.xData,
|
||||
yData: fastYData
|
||||
}, {
|
||||
index: 1,
|
||||
period: periods[2]
|
||||
});
|
||||
if (!smoothedValues) {
|
||||
return;
|
||||
}
|
||||
var xDataLen = slowValues.xData.length;
|
||||
// Format data
|
||||
for (; i < xDataLen; i++) {
|
||||
slowValues.yData[i] = [
|
||||
fastYData[i][1],
|
||||
smoothedValues.yData[i - periods[2] + 1] || null
|
||||
];
|
||||
slowValues.values[i] = [
|
||||
slowValues.xData[i],
|
||||
fastYData[i][1],
|
||||
smoothedValues.yData[i - periods[2] + 1] || null
|
||||
];
|
||||
}
|
||||
return slowValues;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A Slow Stochastic indicator. If the [type](#series.slowstochastic.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.slowstochastic
|
||||
* @since 8.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/stochastic
|
||||
* @requires stock/indicators/slowstochastic
|
||||
* @apioption series.slowstochastic
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,165 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, merge = U.merge, seriesType = U.seriesType;
|
||||
import reduceArrayMixin from '../../Mixins/ReduceArray.js';
|
||||
import multipleLinesMixin from '../../Mixins/MultipleLines.js';
|
||||
var SMA = H.seriesTypes.sma, getArrayExtremes = reduceArrayMixin.getArrayExtremes;
|
||||
/**
|
||||
* The Stochastic series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.stochastic
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('stochastic', 'sma',
|
||||
/**
|
||||
* Stochastic oscillator. This series requires the `linkedTo` option to be
|
||||
* set and should be loaded after the `stock/indicators/indicators.js` file.
|
||||
*
|
||||
* @sample stock/indicators/stochastic
|
||||
* Stochastic oscillator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, joinBy, keys, navigatorOptions,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement,
|
||||
* pointRange, pointStart, showInNavigator, stacking
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/stochastic
|
||||
* @optionparent plotOptions.stochastic
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index, period
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* Periods for Stochastic oscillator: [%K, %D].
|
||||
*
|
||||
* @type {Array<number,number>}
|
||||
* @default [14, 3]
|
||||
*/
|
||||
periods: [14, 3]
|
||||
},
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span><b> {series.name}</b><br/>%K: {point.y}<br/>%D: {point.smoothed}<br/>'
|
||||
},
|
||||
/**
|
||||
* Smoothed line options.
|
||||
*/
|
||||
smoothedLine: {
|
||||
/**
|
||||
* Styles for a smoothed line.
|
||||
*/
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line. If not set, it's inherited from
|
||||
* [plotOptions.stochastic.color
|
||||
* ](#plotOptions.stochastic.color).
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: void 0
|
||||
}
|
||||
},
|
||||
dataGrouping: {
|
||||
approximation: 'averages'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
merge(multipleLinesMixin, {
|
||||
nameComponents: ['periods'],
|
||||
nameBase: 'Stochastic',
|
||||
pointArrayMap: ['y', 'smoothed'],
|
||||
parallelArrays: ['x', 'y', 'smoothed'],
|
||||
pointValKey: 'y',
|
||||
linesApiNames: ['smoothedLine'],
|
||||
init: function () {
|
||||
SMA.prototype.init.apply(this, arguments);
|
||||
// Set default color for lines:
|
||||
this.options = merge({
|
||||
smoothedLine: {
|
||||
styles: {
|
||||
lineColor: this.color
|
||||
}
|
||||
}
|
||||
}, this.options);
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var periodK = params.periods[0], periodD = params.periods[1], xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0,
|
||||
// 0- date, 1-%K, 2-%D
|
||||
SO = [], xData = [], yData = [], slicedY, close = 3, low = 2, high = 1, CL, HL, LL, K, D = null, points, extremes, i;
|
||||
// Stochastic requires close value
|
||||
if (yValLen < periodK ||
|
||||
!isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
// 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 = periodK - 1; i < yValLen; i++) {
|
||||
slicedY = yVal.slice(i - periodK + 1, i + 1);
|
||||
// Calculate %K
|
||||
extremes = getArrayExtremes(slicedY, low, high);
|
||||
LL = extremes[0]; // Lowest low in %K periods
|
||||
CL = yVal[i][close] - LL;
|
||||
HL = extremes[1] - LL;
|
||||
K = CL / HL * 100;
|
||||
xData.push(xVal[i]);
|
||||
yData.push([K, null]);
|
||||
// Calculate smoothed %D, which is SMA of %K
|
||||
if (i >= (periodK - 1) + (periodD - 1)) {
|
||||
points = SMA.prototype.getValues.call(this, {
|
||||
xData: xData.slice(-periodD),
|
||||
yData: yData.slice(-periodD)
|
||||
}, {
|
||||
period: periodD
|
||||
});
|
||||
D = points.yData[0];
|
||||
}
|
||||
SO.push([xVal[i], K, D]);
|
||||
yData[yData.length - 1][1] = D;
|
||||
}
|
||||
return {
|
||||
values: SO,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* A Stochastic indicator. If the [type](#series.stochastic.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.stochastic
|
||||
* @since 6.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/stochastic
|
||||
* @apioption series.stochastic
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,431 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, merge = U.merge, seriesType = U.seriesType;
|
||||
var isArray = U.isArray, objectEach = U.objectEach;
|
||||
var ATR = H.seriesTypes.atr, SMA = H.seriesTypes.sma;
|
||||
/* eslint-disable require-jsdoc */
|
||||
// Utils:
|
||||
function createPointObj(mainSeries, index, close) {
|
||||
return {
|
||||
index: index,
|
||||
close: mainSeries.yData[index][close],
|
||||
x: mainSeries.xData[index]
|
||||
};
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
/**
|
||||
* The Supertrend series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.supertrend
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('supertrend', 'sma',
|
||||
/**
|
||||
* Supertrend indicator. This series requires the `linkedTo` option to be
|
||||
* set and should be loaded after the `stock/indicators/indicators.js` and
|
||||
* `stock/indicators/sma.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/supertrend
|
||||
* Supertrend indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, cropThreshold, negativeColor, colorAxis, joinBy,
|
||||
* keys, navigatorOptions, pointInterval, pointIntervalUnit,
|
||||
* pointPlacement, pointRange, pointStart, showInNavigator,
|
||||
* stacking, threshold
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/supertrend
|
||||
* @optionparent plotOptions.supertrend
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Paramters used in calculation of Supertrend indicator series points.
|
||||
*
|
||||
* @excluding index
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* Multiplier for Supertrend Indicator.
|
||||
*/
|
||||
multiplier: 3,
|
||||
/**
|
||||
* The base period for indicator Supertrend Indicator calculations.
|
||||
* This is the number of data points which are taken into account
|
||||
* for the indicator calculations.
|
||||
*/
|
||||
period: 10
|
||||
},
|
||||
/**
|
||||
* Color of the Supertrend series line that is beneath the main series.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/supertrend/
|
||||
* Example with risingTrendColor
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
risingTrendColor: '#06B535',
|
||||
/**
|
||||
* Color of the Supertrend series line that is above the main series.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/supertrend/
|
||||
* Example with fallingTrendColor
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
fallingTrendColor: '#F21313',
|
||||
/**
|
||||
* The styles for the Supertrend line that intersect main series.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/supertrend/
|
||||
* Example with changeTrendLine
|
||||
*/
|
||||
changeTrendLine: {
|
||||
styles: {
|
||||
/**
|
||||
* Pixel width of the line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Color of the line.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
lineColor: '#333333',
|
||||
/**
|
||||
* The dash or dot style of the grid lines. For possible
|
||||
* values, see
|
||||
* [this demonstration](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/series-dashstyle-all/).
|
||||
*
|
||||
* @sample {highcharts} highcharts/yaxis/gridlinedashstyle/
|
||||
* Long dashes
|
||||
* @sample {highstock} stock/xaxis/gridlinedashstyle/
|
||||
* Long dashes
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @since 7.0.0
|
||||
*/
|
||||
dashStyle: 'LongDash'
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series.prototype
|
||||
*/
|
||||
{
|
||||
nameBase: 'Supertrend',
|
||||
nameComponents: ['multiplier', 'period'],
|
||||
requiredIndicators: ['atr'],
|
||||
init: function () {
|
||||
var options, parentOptions;
|
||||
SMA.prototype.init.apply(this, arguments);
|
||||
options = this.options;
|
||||
parentOptions = this.linkedParent.options;
|
||||
// Indicator cropThreshold has to be equal linked series one
|
||||
// reduced by period due to points comparison in drawGraph method
|
||||
// (#9787)
|
||||
options.cropThreshold = (parentOptions.cropThreshold -
|
||||
(options.params.period - 1));
|
||||
},
|
||||
drawGraph: function () {
|
||||
var indicator = this, indicOptions = indicator.options,
|
||||
// Series that indicator is linked to
|
||||
mainSeries = indicator.linkedParent, mainLinePoints = (mainSeries ? mainSeries.points : []), indicPoints = indicator.points, indicPath = indicator.graph, indicPointsLen = indicPoints.length,
|
||||
// Points offset between lines
|
||||
tempOffset = mainLinePoints.length - indicPointsLen, offset = tempOffset > 0 ? tempOffset : 0,
|
||||
// @todo: fix when ichi-moku indicator is merged to master.
|
||||
gappedExtend = {
|
||||
options: {
|
||||
gapSize: indicOptions.gapSize
|
||||
}
|
||||
},
|
||||
// Sorted supertrend points array
|
||||
groupedPoitns = {
|
||||
top: [],
|
||||
bottom: [],
|
||||
intersect: [] // Change trend line points
|
||||
},
|
||||
// Options for trend lines
|
||||
supertrendLineOptions = {
|
||||
top: {
|
||||
styles: {
|
||||
lineWidth: indicOptions.lineWidth,
|
||||
lineColor: (indicOptions.fallingTrendColor ||
|
||||
indicOptions.color),
|
||||
dashStyle: indicOptions.dashStyle
|
||||
}
|
||||
},
|
||||
bottom: {
|
||||
styles: {
|
||||
lineWidth: indicOptions.lineWidth,
|
||||
lineColor: (indicOptions.risingTrendColor ||
|
||||
indicOptions.color),
|
||||
dashStyle: indicOptions.dashStyle
|
||||
}
|
||||
},
|
||||
intersect: indicOptions.changeTrendLine
|
||||
}, close = 3,
|
||||
// Supertrend line point
|
||||
point,
|
||||
// Supertrend line next point (has smaller x pos than point)
|
||||
nextPoint,
|
||||
// Main series points
|
||||
mainPoint, nextMainPoint,
|
||||
// Used when supertrend and main points are shifted
|
||||
// relative to each other
|
||||
prevMainPoint, prevPrevMainPoint,
|
||||
// Used when particular point color is set
|
||||
pointColor,
|
||||
// Temporary points that fill groupedPoitns array
|
||||
newPoint, newNextPoint;
|
||||
// Loop which sort supertrend points
|
||||
while (indicPointsLen--) {
|
||||
point = indicPoints[indicPointsLen];
|
||||
nextPoint = indicPoints[indicPointsLen - 1];
|
||||
mainPoint = mainLinePoints[indicPointsLen - 1 + offset];
|
||||
nextMainPoint = mainLinePoints[indicPointsLen - 2 + offset];
|
||||
prevMainPoint = mainLinePoints[indicPointsLen + offset];
|
||||
prevPrevMainPoint = mainLinePoints[indicPointsLen + offset + 1];
|
||||
pointColor = point.options.color;
|
||||
newPoint = {
|
||||
x: point.x,
|
||||
plotX: point.plotX,
|
||||
plotY: point.plotY,
|
||||
isNull: false
|
||||
};
|
||||
// When mainPoint is the last one (left plot area edge)
|
||||
// but supertrend has additional one
|
||||
if (!nextMainPoint &&
|
||||
mainPoint && mainSeries.yData[mainPoint.index - 1]) {
|
||||
nextMainPoint = createPointObj(mainSeries, mainPoint.index - 1, close);
|
||||
}
|
||||
// When prevMainPoint is the last one (right plot area edge)
|
||||
// but supertrend has additional one (and points are shifted)
|
||||
if (!prevPrevMainPoint &&
|
||||
prevMainPoint && mainSeries.yData[prevMainPoint.index + 1]) {
|
||||
prevPrevMainPoint = createPointObj(mainSeries, prevMainPoint.index + 1, close);
|
||||
}
|
||||
// When points are shifted (right or left plot area edge)
|
||||
if (!mainPoint &&
|
||||
nextMainPoint && mainSeries.yData[nextMainPoint.index + 1]) {
|
||||
mainPoint = createPointObj(mainSeries, nextMainPoint.index + 1, close);
|
||||
}
|
||||
else if (!mainPoint &&
|
||||
prevMainPoint && mainSeries.yData[prevMainPoint.index - 1]) {
|
||||
mainPoint = createPointObj(mainSeries, prevMainPoint.index - 1, close);
|
||||
}
|
||||
// Check if points are shifted relative to each other
|
||||
if (point &&
|
||||
mainPoint &&
|
||||
prevMainPoint &&
|
||||
nextMainPoint &&
|
||||
point.x !== mainPoint.x) {
|
||||
if (point.x === prevMainPoint.x) {
|
||||
nextMainPoint = mainPoint;
|
||||
mainPoint = prevMainPoint;
|
||||
}
|
||||
else if (point.x === nextMainPoint.x) {
|
||||
mainPoint = nextMainPoint;
|
||||
nextMainPoint = {
|
||||
close: mainSeries.yData[mainPoint.index - 1][close],
|
||||
x: mainSeries.xData[mainPoint.index - 1]
|
||||
};
|
||||
}
|
||||
else if (prevPrevMainPoint && point.x === prevPrevMainPoint.x) {
|
||||
mainPoint = prevPrevMainPoint;
|
||||
nextMainPoint = prevMainPoint;
|
||||
}
|
||||
}
|
||||
if (nextPoint && nextMainPoint && mainPoint) {
|
||||
newNextPoint = {
|
||||
x: nextPoint.x,
|
||||
plotX: nextPoint.plotX,
|
||||
plotY: nextPoint.plotY,
|
||||
isNull: false
|
||||
};
|
||||
if (point.y >= mainPoint.close &&
|
||||
nextPoint.y >= nextMainPoint.close) {
|
||||
point.color = (pointColor || indicOptions.fallingTrendColor ||
|
||||
indicOptions.color);
|
||||
groupedPoitns.top.push(newPoint);
|
||||
}
|
||||
else if (point.y < mainPoint.close &&
|
||||
nextPoint.y < nextMainPoint.close) {
|
||||
point.color = (pointColor || indicOptions.risingTrendColor ||
|
||||
indicOptions.color);
|
||||
groupedPoitns.bottom.push(newPoint);
|
||||
}
|
||||
else {
|
||||
groupedPoitns.intersect.push(newPoint);
|
||||
groupedPoitns.intersect.push(newNextPoint);
|
||||
// Additional null point to make a gap in line
|
||||
groupedPoitns.intersect.push(merge(newNextPoint, {
|
||||
isNull: true
|
||||
}));
|
||||
if (point.y >= mainPoint.close &&
|
||||
nextPoint.y < nextMainPoint.close) {
|
||||
point.color = (pointColor || indicOptions.fallingTrendColor ||
|
||||
indicOptions.color);
|
||||
nextPoint.color = (pointColor || indicOptions.risingTrendColor ||
|
||||
indicOptions.color);
|
||||
groupedPoitns.top.push(newPoint);
|
||||
groupedPoitns.top.push(merge(newNextPoint, {
|
||||
isNull: true
|
||||
}));
|
||||
}
|
||||
else if (point.y < mainPoint.close &&
|
||||
nextPoint.y >= nextMainPoint.close) {
|
||||
point.color = (pointColor || indicOptions.risingTrendColor ||
|
||||
indicOptions.color);
|
||||
nextPoint.color = (pointColor || indicOptions.fallingTrendColor ||
|
||||
indicOptions.color);
|
||||
groupedPoitns.bottom.push(newPoint);
|
||||
groupedPoitns.bottom.push(merge(newNextPoint, {
|
||||
isNull: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mainPoint) {
|
||||
if (point.y >= mainPoint.close) {
|
||||
point.color = (pointColor || indicOptions.fallingTrendColor ||
|
||||
indicOptions.color);
|
||||
groupedPoitns.top.push(newPoint);
|
||||
}
|
||||
else {
|
||||
point.color = (pointColor || indicOptions.risingTrendColor ||
|
||||
indicOptions.color);
|
||||
groupedPoitns.bottom.push(newPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Generate lines:
|
||||
objectEach(groupedPoitns, function (values, lineName) {
|
||||
indicator.points = values;
|
||||
indicator.options = merge(supertrendLineOptions[lineName].styles, gappedExtend);
|
||||
indicator.graph = indicator['graph' + lineName + 'Line'];
|
||||
SMA.prototype.drawGraph.call(indicator);
|
||||
// Now save line
|
||||
indicator['graph' + lineName + 'Line'] = indicator.graph;
|
||||
});
|
||||
// Restore options:
|
||||
indicator.points = indicPoints;
|
||||
indicator.options = indicOptions;
|
||||
indicator.graph = indicPath;
|
||||
},
|
||||
// Supertrend (Multiplier, Period) Formula:
|
||||
// BASIC UPPERBAND = (HIGH + LOW) / 2 + Multiplier * ATR(Period)
|
||||
// BASIC LOWERBAND = (HIGH + LOW) / 2 - Multiplier * ATR(Period)
|
||||
// FINAL UPPERBAND =
|
||||
// IF(
|
||||
// Current BASICUPPERBAND < Previous FINAL UPPERBAND AND
|
||||
// Previous Close > Previous FINAL UPPERBAND
|
||||
// ) THEN (Current BASIC UPPERBAND)
|
||||
// ELSE (Previous FINALUPPERBAND)
|
||||
// FINAL LOWERBAND =
|
||||
// IF(
|
||||
// Current BASIC LOWERBAND > Previous FINAL LOWERBAND AND
|
||||
// Previous Close < Previous FINAL LOWERBAND
|
||||
// ) THEN (Current BASIC LOWERBAND)
|
||||
// ELSE (Previous FINAL LOWERBAND)
|
||||
// SUPERTREND =
|
||||
// IF(
|
||||
// Previous Supertrend == Previous FINAL UPPERBAND AND
|
||||
// Current Close < Current FINAL UPPERBAND
|
||||
// ) THAN Current FINAL UPPERBAND
|
||||
// ELSE IF(
|
||||
// Previous Supertrend == Previous FINAL LOWERBAND AND
|
||||
// Current Close < Current FINAL LOWERBAND
|
||||
// ) THAN Current FINAL UPPERBAND
|
||||
// ELSE IF(
|
||||
// Previous Supertrend == Previous FINAL UPPERBAND AND
|
||||
// Current Close > Current FINAL UPPERBAND
|
||||
// ) THAN Current FINAL LOWERBAND
|
||||
// ELSE IF(
|
||||
// Previous Supertrend == Previous FINAL LOWERBAND AND
|
||||
// Current Close > Current FINAL LOWERBAND
|
||||
// ) THAN Current FINAL LOWERBAND
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, multiplier = params.multiplier, xVal = series.xData, yVal = series.yData, ATRData = [],
|
||||
// 0- date, 1- Supertrend indicator
|
||||
ST = [], xData = [], yData = [], close = 3, low = 2, high = 1, periodsOffset = (period === 0) ? 0 : period - 1, basicUp, basicDown, finalUp = [], finalDown = [], supertrend, prevFinalUp, prevFinalDown, prevST, // previous Supertrend
|
||||
prevY, y, i;
|
||||
if ((xVal.length <= period) || !isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4 || period < 0) {
|
||||
return;
|
||||
}
|
||||
ATRData = ATR.prototype.getValues.call(this, series, {
|
||||
period: period
|
||||
}).yData;
|
||||
for (i = 0; i < ATRData.length; i++) {
|
||||
y = yVal[periodsOffset + i];
|
||||
prevY = yVal[periodsOffset + i - 1] || [];
|
||||
prevFinalUp = finalUp[i - 1];
|
||||
prevFinalDown = finalDown[i - 1];
|
||||
prevST = yData[i - 1];
|
||||
if (i === 0) {
|
||||
prevFinalUp = prevFinalDown = prevST = 0;
|
||||
}
|
||||
basicUp = correctFloat((y[high] + y[low]) / 2 + multiplier * ATRData[i]);
|
||||
basicDown = correctFloat((y[high] + y[low]) / 2 - multiplier * ATRData[i]);
|
||||
if ((basicUp < prevFinalUp) ||
|
||||
(prevY[close] > prevFinalUp)) {
|
||||
finalUp[i] = basicUp;
|
||||
}
|
||||
else {
|
||||
finalUp[i] = prevFinalUp;
|
||||
}
|
||||
if ((basicDown > prevFinalDown) ||
|
||||
(prevY[close] < prevFinalDown)) {
|
||||
finalDown[i] = basicDown;
|
||||
}
|
||||
else {
|
||||
finalDown[i] = prevFinalDown;
|
||||
}
|
||||
if (prevST === prevFinalUp && y[close] < finalUp[i] ||
|
||||
prevST === prevFinalDown && y[close] < finalDown[i]) {
|
||||
supertrend = finalUp[i];
|
||||
}
|
||||
else if (prevST === prevFinalUp && y[close] > finalUp[i] ||
|
||||
prevST === prevFinalDown && y[close] > finalDown[i]) {
|
||||
supertrend = finalDown[i];
|
||||
}
|
||||
ST.push([xVal[periodsOffset + i], supertrend]);
|
||||
xData.push(xVal[periodsOffset + i]);
|
||||
yData.push(supertrend);
|
||||
}
|
||||
return {
|
||||
values: ST,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Supertrend indicator` series. If the [type](#series.supertrend.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.supertrend
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, cropThreshold, data, dataParser, dataURL,
|
||||
* joinBy, keys, navigatorOptions, negativeColor, pointInterval,
|
||||
* pointIntervalUnit, pointPlacement, pointRange, pointStart,
|
||||
* showInNavigator, stacking, threshold
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/supertrend
|
||||
* @apioption series.supertrend
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,167 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, isArray = U.isArray, seriesType = U.seriesType;
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
var EMAindicator = H.seriesTypes.ema;
|
||||
/**
|
||||
* The TEMA series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.tema
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('tema', 'ema',
|
||||
/**
|
||||
* Triple exponential moving average (TEMA) indicator. This series requires
|
||||
* `linkedTo` option to be set and should be loaded after the
|
||||
* `stock/indicators/indicators.js` and `stock/indicators/ema.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/tema
|
||||
* TEMA indicator
|
||||
*
|
||||
* @extends plotOptions.ema
|
||||
* @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/ema
|
||||
* @requires stock/indicators/tema
|
||||
* @optionparent plotOptions.tema
|
||||
*/
|
||||
{},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
init: function () {
|
||||
var args = arguments, ctx = this;
|
||||
requiredIndicator.isParentLoaded(EMAindicator, 'ema', ctx.type, function (indicator) {
|
||||
indicator.prototype.init.apply(ctx, args);
|
||||
return;
|
||||
});
|
||||
},
|
||||
getEMA: function (yVal, prevEMA, SMA, index, i, xVal) {
|
||||
return EMAindicator.prototype.calculateEma(xVal || [], yVal, typeof i === 'undefined' ? 1 : i, this.chart.series[0].EMApercent, prevEMA, typeof index === 'undefined' ? -1 : index, SMA);
|
||||
},
|
||||
getTemaPoint: function (xVal, tripledPeriod, EMAlevels, i) {
|
||||
var TEMAPoint = [
|
||||
xVal[i - 3],
|
||||
correctFloat(3 * EMAlevels.level1 -
|
||||
3 * EMAlevels.level2 + EMAlevels.level3)
|
||||
];
|
||||
return TEMAPoint;
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, doubledPeriod = 2 * period, tripledPeriod = 3 * period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, index = -1, accumulatePeriodPoints = 0, SMA = 0, TEMA = [], xDataTema = [], yDataTema = [],
|
||||
// EMA of previous point
|
||||
prevEMA, prevEMAlevel2,
|
||||
// EMA values array
|
||||
EMAvalues = [], EMAlevel2values = [], i, TEMAPoint,
|
||||
// This object contains all EMA EMAlevels calculated like below
|
||||
// EMA = level1
|
||||
// EMA(EMA) = level2,
|
||||
// EMA(EMA(EMA)) = level3,
|
||||
EMAlevels = {};
|
||||
series.EMApercent = (2 / (period + 1));
|
||||
// Check period, if bigger than EMA points length, skip
|
||||
if (yValLen < 3 * period - 2) {
|
||||
return;
|
||||
}
|
||||
// Switch index for OHLC / Candlestick / Arearange
|
||||
if (isArray(yVal[0])) {
|
||||
index = params.index ? params.index : 0;
|
||||
}
|
||||
// Accumulate first N-points
|
||||
accumulatePeriodPoints =
|
||||
EMAindicator.prototype.accumulatePeriodPoints(period, index, yVal);
|
||||
// first point
|
||||
SMA = accumulatePeriodPoints / period;
|
||||
accumulatePeriodPoints = 0;
|
||||
// Calculate value one-by-one for each period in visible data
|
||||
for (i = period; i < yValLen + 3; i++) {
|
||||
if (i < yValLen + 1) {
|
||||
EMAlevels.level1 = this.getEMA(yVal, prevEMA, SMA, index, i)[1];
|
||||
EMAvalues.push(EMAlevels.level1);
|
||||
}
|
||||
prevEMA = EMAlevels.level1;
|
||||
// Summing first period points for ema(ema)
|
||||
if (i < doubledPeriod) {
|
||||
accumulatePeriodPoints += EMAlevels.level1;
|
||||
}
|
||||
else {
|
||||
// Calculate dema
|
||||
// First dema point
|
||||
if (i === doubledPeriod) {
|
||||
SMA = accumulatePeriodPoints / period;
|
||||
accumulatePeriodPoints = 0;
|
||||
}
|
||||
EMAlevels.level1 = EMAvalues[i - period - 1];
|
||||
EMAlevels.level2 = this.getEMA([EMAlevels.level1], prevEMAlevel2, SMA)[1];
|
||||
EMAlevel2values.push(EMAlevels.level2);
|
||||
prevEMAlevel2 = EMAlevels.level2;
|
||||
// Summing first period points for ema(ema(ema))
|
||||
if (i < tripledPeriod) {
|
||||
accumulatePeriodPoints += EMAlevels.level2;
|
||||
}
|
||||
else {
|
||||
// Calculate tema
|
||||
// First tema point
|
||||
if (i === tripledPeriod) {
|
||||
SMA = accumulatePeriodPoints / period;
|
||||
}
|
||||
if (i === yValLen + 1) {
|
||||
// Calculate the last ema and emaEMA points
|
||||
EMAlevels.level1 = EMAvalues[i - period - 1];
|
||||
EMAlevels.level2 = this.getEMA([EMAlevels.level1], prevEMAlevel2, SMA)[1];
|
||||
EMAlevel2values.push(EMAlevels.level2);
|
||||
}
|
||||
EMAlevels.level1 = EMAvalues[i - period - 2];
|
||||
EMAlevels.level2 = EMAlevel2values[i - 2 * period - 1];
|
||||
EMAlevels.level3 = this.getEMA([EMAlevels.level2], EMAlevels.prevLevel3, SMA)[1];
|
||||
TEMAPoint = this.getTemaPoint(xVal, tripledPeriod, EMAlevels, i);
|
||||
// Make sure that point exists (for TRIX oscillator)
|
||||
if (TEMAPoint) {
|
||||
TEMA.push(TEMAPoint);
|
||||
xDataTema.push(TEMAPoint[0]);
|
||||
yDataTema.push(TEMAPoint[1]);
|
||||
}
|
||||
EMAlevels.prevLevel3 = EMAlevels.level3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: TEMA,
|
||||
xData: xDataTema,
|
||||
yData: yDataTema
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `TEMA` series. If the [type](#series.ema.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.ema
|
||||
* @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/ema
|
||||
* @requires stock/indicators/tema
|
||||
* @apioption series.tema
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,80 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, seriesType = U.seriesType;
|
||||
import requiredIndicator from '../../Mixins/IndicatorRequired.js';
|
||||
var TEMA = H.seriesTypes.tema;
|
||||
/**
|
||||
* The TRIX series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.trix
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('trix', 'tema',
|
||||
/**
|
||||
* Triple exponential average (TRIX) oscillator. This series requires
|
||||
* `linkedTo` option to be set.
|
||||
*
|
||||
* Requires https://code.highcharts.com/stock/indicators/ema.js
|
||||
* and https://code.highcharts.com/stock/indicators/tema.js.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/trix
|
||||
* TRIX indicator
|
||||
*
|
||||
* @extends plotOptions.tema
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, compare, compareBase, joinBy, keys,
|
||||
* navigatorOptions, pointInterval, pointIntervalUnit,
|
||||
* pointPlacement, pointRange, pointStart, showInNavigator,
|
||||
* stacking
|
||||
* @optionparent plotOptions.trix
|
||||
*/
|
||||
{},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
init: function () {
|
||||
var args = arguments, ctx = this;
|
||||
requiredIndicator.isParentLoaded(TEMA, 'tema', ctx.type, function (indicator) {
|
||||
indicator.prototype.init.apply(ctx, args);
|
||||
return;
|
||||
});
|
||||
},
|
||||
// TRIX is calculated using TEMA so we just extend getTemaPoint method.
|
||||
getTemaPoint: function (xVal, tripledPeriod, EMAlevels, i) {
|
||||
if (i > tripledPeriod) {
|
||||
var TRIXPoint = [
|
||||
xVal[i - 3],
|
||||
EMAlevels.prevLevel3 !== 0 ?
|
||||
correctFloat(EMAlevels.level3 - EMAlevels.prevLevel3) /
|
||||
EMAlevels.prevLevel3 * 100 : null
|
||||
];
|
||||
}
|
||||
return TRIXPoint;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `TRIX` series. If the [type](#series.tema.type) option is not specified, it
|
||||
* is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.tema
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, compare, compareBase, dataParser, dataURL,
|
||||
* joinBy, keys, navigatorOptions, pointInterval, pointIntervalUnit,
|
||||
* pointPlacement, pointRange, pointStart, showInNavigator, stacking
|
||||
* @apioption series.trix
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,103 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
/**
|
||||
* The Trend line series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.trendline
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('trendline', 'sma',
|
||||
/**
|
||||
* Trendline (linear regression) fits a straight line to the selected data
|
||||
* using a method called the Sum Of Least Squares. This series requires the
|
||||
* `linkedTo` option to be set.
|
||||
*
|
||||
* @sample stock/indicators/trendline
|
||||
* Trendline indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 7.1.3
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/trendline
|
||||
* @optionparent plotOptions.trendline
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding period
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @default 3
|
||||
*/
|
||||
index: 3
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Trendline',
|
||||
nameComponents: false,
|
||||
getValues: function (series, params) {
|
||||
var xVal = series.xData, yVal = series.yData, LR = [], xData = [], yData = [], sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, xValLength = xVal.length, index = params.index, alpha, beta, i, x, y;
|
||||
// Get sums:
|
||||
for (i = 0; i < xValLength; i++) {
|
||||
x = xVal[i];
|
||||
y = isArray(yVal[i]) ? yVal[i][index] : yVal[i];
|
||||
sumX += x;
|
||||
sumY += y;
|
||||
sumXY += x * y;
|
||||
sumX2 += x * x;
|
||||
}
|
||||
// Get slope and offset:
|
||||
alpha = (xValLength * sumXY - sumX * sumY) /
|
||||
(xValLength * sumX2 - sumX * sumX);
|
||||
if (isNaN(alpha)) {
|
||||
alpha = 0;
|
||||
}
|
||||
beta = (sumY - alpha * sumX) / xValLength;
|
||||
// Calculate linear regression:
|
||||
for (i = 0; i < xValLength; i++) {
|
||||
x = xVal[i];
|
||||
y = alpha * x + beta;
|
||||
// Prepare arrays required for getValues() method
|
||||
LR[i] = [x, y];
|
||||
xData[i] = x;
|
||||
yData[i] = y;
|
||||
}
|
||||
return {
|
||||
xData: xData,
|
||||
yData: yData,
|
||||
values: LR
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `TrendLine` series. If the [type](#series.trendline.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.trendline
|
||||
* @since 7.1.3
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/trendline
|
||||
* @apioption series.trendline
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,492 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Paweł Dalek
|
||||
*
|
||||
* Volume By Price (VBP) indicator for Highstock
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import Point from '../../Core/Series/Point.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, animObject = U.animObject, arrayMax = U.arrayMax, arrayMin = U.arrayMin, correctFloat = U.correctFloat, error = U.error, extend = U.extend, isArray = U.isArray, seriesType = U.seriesType;
|
||||
/* eslint-disable require-jsdoc */
|
||||
// Utils
|
||||
function arrayExtremesOHLC(data) {
|
||||
var dataLength = data.length, min = data[0][3], max = min, i = 1, currentPoint;
|
||||
for (; i < dataLength; i++) {
|
||||
currentPoint = data[i][3];
|
||||
if (currentPoint < min) {
|
||||
min = currentPoint;
|
||||
}
|
||||
if (currentPoint > max) {
|
||||
max = currentPoint;
|
||||
}
|
||||
}
|
||||
return {
|
||||
min: min,
|
||||
max: max
|
||||
};
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
var abs = Math.abs, noop = H.noop, columnPrototype = H.seriesTypes.column.prototype;
|
||||
/**
|
||||
* The Volume By Price (VBP) series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.vbp
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('vbp', 'sma',
|
||||
/**
|
||||
* Volume By Price indicator.
|
||||
*
|
||||
* This series requires `linkedTo` option to be set.
|
||||
*
|
||||
* @sample stock/indicators/volume-by-price
|
||||
* Volume By Price indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/volume-by-price
|
||||
* @optionparent plotOptions.vbp
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index, period
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* The number of price zones.
|
||||
*/
|
||||
ranges: 12,
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
volumeSeriesID: 'volume'
|
||||
},
|
||||
/**
|
||||
* The styles for lines which determine price zones.
|
||||
*/
|
||||
zoneLines: {
|
||||
/**
|
||||
* Enable/disable zone lines.
|
||||
*/
|
||||
enabled: true,
|
||||
/**
|
||||
* Specify the style of zone lines.
|
||||
*
|
||||
* @type {Highcharts.CSSObject}
|
||||
* @default {"color": "#0A9AC9", "dashStyle": "LongDash", "lineWidth": 1}
|
||||
*/
|
||||
styles: {
|
||||
/** @ignore-options */
|
||||
color: '#0A9AC9',
|
||||
/** @ignore-options */
|
||||
dashStyle: 'LongDash',
|
||||
/** @ignore-options */
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The styles for bars when volume is divided into positive/negative.
|
||||
*/
|
||||
volumeDivision: {
|
||||
/**
|
||||
* Option to control if volume is divided.
|
||||
*/
|
||||
enabled: true,
|
||||
styles: {
|
||||
/**
|
||||
* Color of positive volume bars.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
positiveColor: 'rgba(144, 237, 125, 0.8)',
|
||||
/**
|
||||
* Color of negative volume bars.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
negativeColor: 'rgba(244, 91, 91, 0.8)'
|
||||
}
|
||||
},
|
||||
// To enable series animation; must be animationLimit > pointCount
|
||||
animationLimit: 1000,
|
||||
enableMouseTracking: false,
|
||||
pointPadding: 0,
|
||||
zIndex: -1,
|
||||
crisp: true,
|
||||
dataGrouping: {
|
||||
enabled: false
|
||||
},
|
||||
dataLabels: {
|
||||
allowOverlap: true,
|
||||
enabled: true,
|
||||
format: 'P: {point.volumePos:.2f} | N: {point.volumeNeg:.2f}',
|
||||
padding: 0,
|
||||
style: {
|
||||
/** @internal */
|
||||
fontSize: '7px'
|
||||
},
|
||||
verticalAlign: 'top'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Volume by Price',
|
||||
bindTo: {
|
||||
series: false,
|
||||
eventName: 'afterSetExtremes'
|
||||
},
|
||||
calculateOn: 'render',
|
||||
markerAttribs: noop,
|
||||
drawGraph: noop,
|
||||
getColumnMetrics: columnPrototype.getColumnMetrics,
|
||||
crispCol: columnPrototype.crispCol,
|
||||
init: function (chart) {
|
||||
var indicator = this, params, baseSeries, volumeSeries;
|
||||
H.seriesTypes.sma.prototype.init.apply(indicator, arguments);
|
||||
params = indicator.options.params;
|
||||
baseSeries = indicator.linkedParent;
|
||||
volumeSeries = chart.get(params.volumeSeriesID);
|
||||
indicator.addCustomEvents(baseSeries, volumeSeries);
|
||||
return indicator;
|
||||
},
|
||||
// Adds events related with removing series
|
||||
addCustomEvents: function (baseSeries, volumeSeries) {
|
||||
var indicator = this;
|
||||
/* eslint-disable require-jsdoc */
|
||||
function toEmptyIndicator() {
|
||||
indicator.chart.redraw();
|
||||
indicator.setData([]);
|
||||
indicator.zoneStarts = [];
|
||||
if (indicator.zoneLinesSVG) {
|
||||
indicator.zoneLinesSVG.destroy();
|
||||
delete indicator.zoneLinesSVG;
|
||||
}
|
||||
}
|
||||
/* eslint-enable require-jsdoc */
|
||||
// If base series is deleted, indicator series data is filled with
|
||||
// an empty array
|
||||
indicator.dataEventsToUnbind.push(addEvent(baseSeries, 'remove', function () {
|
||||
toEmptyIndicator();
|
||||
}));
|
||||
// If volume series is deleted, indicator series data is filled with
|
||||
// an empty array
|
||||
if (volumeSeries) {
|
||||
indicator.dataEventsToUnbind.push(addEvent(volumeSeries, 'remove', function () {
|
||||
toEmptyIndicator();
|
||||
}));
|
||||
}
|
||||
return indicator;
|
||||
},
|
||||
// Initial animation
|
||||
animate: function (init) {
|
||||
var series = this, inverted = series.chart.inverted, group = series.group, attr = {}, translate, position;
|
||||
if (!init && group) {
|
||||
translate = inverted ? 'translateY' : 'translateX';
|
||||
position = inverted ? series.yAxis.top : series.xAxis.left;
|
||||
group['forceAnimate:' + translate] = true;
|
||||
attr[translate] = position;
|
||||
group.animate(attr, extend(animObject(series.options.animation), {
|
||||
step: function (val, fx) {
|
||||
series.group.attr({
|
||||
scaleX: Math.max(0.001, fx.pos)
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
},
|
||||
drawPoints: function () {
|
||||
var indicator = this;
|
||||
if (indicator.options.volumeDivision.enabled) {
|
||||
indicator.posNegVolume(true, true);
|
||||
columnPrototype.drawPoints.apply(indicator, arguments);
|
||||
indicator.posNegVolume(false, false);
|
||||
}
|
||||
columnPrototype.drawPoints.apply(indicator, arguments);
|
||||
},
|
||||
// Function responsible for dividing volume into positive and negative
|
||||
posNegVolume: function (initVol, pos) {
|
||||
var indicator = this, signOrder = pos ?
|
||||
['positive', 'negative'] :
|
||||
['negative', 'positive'], volumeDivision = indicator.options.volumeDivision, pointLength = indicator.points.length, posWidths = [], negWidths = [], i = 0, pointWidth, priceZone, wholeVol, point;
|
||||
if (initVol) {
|
||||
indicator.posWidths = posWidths;
|
||||
indicator.negWidths = negWidths;
|
||||
}
|
||||
else {
|
||||
posWidths = indicator.posWidths;
|
||||
negWidths = indicator.negWidths;
|
||||
}
|
||||
for (; i < pointLength; i++) {
|
||||
point = indicator.points[i];
|
||||
point[signOrder[0] + 'Graphic'] = point.graphic;
|
||||
point.graphic = point[signOrder[1] + 'Graphic'];
|
||||
if (initVol) {
|
||||
pointWidth = point.shapeArgs.width;
|
||||
priceZone = indicator.priceZones[i];
|
||||
wholeVol = priceZone.wholeVolumeData;
|
||||
if (wholeVol) {
|
||||
posWidths.push(pointWidth / wholeVol * priceZone.positiveVolumeData);
|
||||
negWidths.push(pointWidth / wholeVol * priceZone.negativeVolumeData);
|
||||
}
|
||||
else {
|
||||
posWidths.push(0);
|
||||
negWidths.push(0);
|
||||
}
|
||||
}
|
||||
point.color = pos ?
|
||||
volumeDivision.styles.positiveColor :
|
||||
volumeDivision.styles.negativeColor;
|
||||
point.shapeArgs.width = pos ?
|
||||
indicator.posWidths[i] :
|
||||
indicator.negWidths[i];
|
||||
point.shapeArgs.x = pos ?
|
||||
point.shapeArgs.x :
|
||||
indicator.posWidths[i];
|
||||
}
|
||||
},
|
||||
translate: function () {
|
||||
var indicator = this, options = indicator.options, chart = indicator.chart, yAxis = indicator.yAxis, yAxisMin = yAxis.min, zoneLinesOptions = indicator.options.zoneLines, priceZones = (indicator.priceZones), yBarOffset = 0, indicatorPoints, volumeDataArray, maxVolume, primalBarWidth, barHeight, barHeightP, oldBarHeight, barWidth, pointPadding, chartPlotTop, barX, barY;
|
||||
columnPrototype.translate.apply(indicator);
|
||||
indicatorPoints = indicator.points;
|
||||
// Do translate operation when points exist
|
||||
if (indicatorPoints.length) {
|
||||
pointPadding = options.pointPadding < 0.5 ?
|
||||
options.pointPadding :
|
||||
0.1;
|
||||
volumeDataArray = indicator.volumeDataArray;
|
||||
maxVolume = arrayMax(volumeDataArray);
|
||||
primalBarWidth = chart.plotWidth / 2;
|
||||
chartPlotTop = chart.plotTop;
|
||||
barHeight = abs(yAxis.toPixels(yAxisMin) -
|
||||
yAxis.toPixels(yAxisMin + indicator.rangeStep));
|
||||
oldBarHeight = abs(yAxis.toPixels(yAxisMin) -
|
||||
yAxis.toPixels(yAxisMin + indicator.rangeStep));
|
||||
if (pointPadding) {
|
||||
barHeightP = abs(barHeight * (1 - 2 * pointPadding));
|
||||
yBarOffset = abs((barHeight - barHeightP) / 2);
|
||||
barHeight = abs(barHeightP);
|
||||
}
|
||||
indicatorPoints.forEach(function (point, index) {
|
||||
barX = point.barX = point.plotX = 0;
|
||||
barY = point.plotY = (yAxis.toPixels(priceZones[index].start) -
|
||||
chartPlotTop -
|
||||
(yAxis.reversed ?
|
||||
(barHeight - oldBarHeight) :
|
||||
barHeight) -
|
||||
yBarOffset);
|
||||
barWidth = correctFloat(primalBarWidth *
|
||||
priceZones[index].wholeVolumeData / maxVolume);
|
||||
point.pointWidth = barWidth;
|
||||
point.shapeArgs = indicator.crispCol.apply(// eslint-disable-line no-useless-call
|
||||
indicator, [barX, barY, barWidth, barHeight]);
|
||||
point.volumeNeg = priceZones[index].negativeVolumeData;
|
||||
point.volumePos = priceZones[index].positiveVolumeData;
|
||||
point.volumeAll = priceZones[index].wholeVolumeData;
|
||||
});
|
||||
if (zoneLinesOptions.enabled) {
|
||||
indicator.drawZones(chart, yAxis, indicator.zoneStarts, zoneLinesOptions.styles);
|
||||
}
|
||||
}
|
||||
},
|
||||
getValues: function (series, params) {
|
||||
var indicator = this, xValues = series.processedXData, yValues = series.processedYData, chart = indicator.chart, ranges = params.ranges, VBP = [], xData = [], yData = [], isOHLC, volumeSeries, priceZones;
|
||||
// Checks if base series exists
|
||||
if (!series.chart) {
|
||||
error('Base series not found! In case it has been removed, add ' +
|
||||
'a new one.', true, chart);
|
||||
return;
|
||||
}
|
||||
// Checks if volume series exists
|
||||
if (!(volumeSeries = (chart.get(params.volumeSeriesID)))) {
|
||||
error('Series ' +
|
||||
params.volumeSeriesID +
|
||||
' not found! Check `volumeSeriesID`.', true, chart);
|
||||
return;
|
||||
}
|
||||
// Checks if series data fits the OHLC format
|
||||
isOHLC = isArray(yValues[0]);
|
||||
if (isOHLC && yValues[0].length !== 4) {
|
||||
error('Type of ' +
|
||||
series.name +
|
||||
' series is different than line, OHLC or candlestick.', true, chart);
|
||||
return;
|
||||
}
|
||||
// Price zones contains all the information about the zones (index,
|
||||
// start, end, volumes, etc.)
|
||||
priceZones = indicator.priceZones = indicator.specifyZones(isOHLC, xValues, yValues, ranges, volumeSeries);
|
||||
priceZones.forEach(function (zone, index) {
|
||||
VBP.push([zone.x, zone.end]);
|
||||
xData.push(VBP[index][0]);
|
||||
yData.push(VBP[index][1]);
|
||||
});
|
||||
return {
|
||||
values: VBP,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
},
|
||||
// Specifing where each zone should start ans end
|
||||
specifyZones: function (isOHLC, xValues, yValues, ranges, volumeSeries) {
|
||||
var indicator = this, rangeExtremes = (isOHLC ? arrayExtremesOHLC(yValues) : false), lowRange = rangeExtremes ?
|
||||
rangeExtremes.min :
|
||||
arrayMin(yValues), highRange = rangeExtremes ?
|
||||
rangeExtremes.max :
|
||||
arrayMax(yValues), zoneStarts = indicator.zoneStarts = [], priceZones = [], i = 0, j = 1, rangeStep, zoneStartsLength;
|
||||
if (!lowRange || !highRange) {
|
||||
if (this.points.length) {
|
||||
this.setData([]);
|
||||
this.zoneStarts = [];
|
||||
this.zoneLinesSVG.destroy();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
rangeStep = indicator.rangeStep =
|
||||
correctFloat(highRange - lowRange) / ranges;
|
||||
zoneStarts.push(lowRange);
|
||||
for (; i < ranges - 1; i++) {
|
||||
zoneStarts.push(correctFloat(zoneStarts[i] + rangeStep));
|
||||
}
|
||||
zoneStarts.push(highRange);
|
||||
zoneStartsLength = zoneStarts.length;
|
||||
// Creating zones
|
||||
for (; j < zoneStartsLength; j++) {
|
||||
priceZones.push({
|
||||
index: j - 1,
|
||||
x: xValues[0],
|
||||
start: zoneStarts[j - 1],
|
||||
end: zoneStarts[j]
|
||||
});
|
||||
}
|
||||
return indicator.volumePerZone(isOHLC, priceZones, volumeSeries, xValues, yValues);
|
||||
},
|
||||
// Calculating sum of volume values for a specific zone
|
||||
volumePerZone: function (isOHLC, priceZones, volumeSeries, xValues, yValues) {
|
||||
var indicator = this, volumeXData = volumeSeries.processedXData, volumeYData = volumeSeries.processedYData, lastZoneIndex = priceZones.length - 1, baseSeriesLength = yValues.length, volumeSeriesLength = volumeYData.length, previousValue, startFlag, endFlag, value, i;
|
||||
// Checks if each point has a corresponding volume value
|
||||
if (abs(baseSeriesLength - volumeSeriesLength)) {
|
||||
// If the first point don't have volume, add 0 value at the
|
||||
// beggining of the volume array
|
||||
if (xValues[0] !== volumeXData[0]) {
|
||||
volumeYData.unshift(0);
|
||||
}
|
||||
// If the last point don't have volume, add 0 value at the end
|
||||
// of the volume array
|
||||
if (xValues[baseSeriesLength - 1] !==
|
||||
volumeXData[volumeSeriesLength - 1]) {
|
||||
volumeYData.push(0);
|
||||
}
|
||||
}
|
||||
indicator.volumeDataArray = [];
|
||||
priceZones.forEach(function (zone) {
|
||||
zone.wholeVolumeData = 0;
|
||||
zone.positiveVolumeData = 0;
|
||||
zone.negativeVolumeData = 0;
|
||||
for (i = 0; i < baseSeriesLength; i++) {
|
||||
startFlag = false;
|
||||
endFlag = false;
|
||||
value = isOHLC ? yValues[i][3] : yValues[i];
|
||||
previousValue = i ?
|
||||
(isOHLC ?
|
||||
yValues[i - 1][3] :
|
||||
yValues[i - 1]) :
|
||||
value;
|
||||
// Checks if this is the point with the
|
||||
// lowest close value and if so, adds it calculations
|
||||
if (value <= zone.start && zone.index === 0) {
|
||||
startFlag = true;
|
||||
}
|
||||
// Checks if this is the point with the highest
|
||||
// close value and if so, adds it calculations
|
||||
if (value >= zone.end && zone.index === lastZoneIndex) {
|
||||
endFlag = true;
|
||||
}
|
||||
if ((value > zone.start || startFlag) &&
|
||||
(value < zone.end || endFlag)) {
|
||||
zone.wholeVolumeData += volumeYData[i];
|
||||
if (previousValue > value) {
|
||||
zone.negativeVolumeData += volumeYData[i];
|
||||
}
|
||||
else {
|
||||
zone.positiveVolumeData += volumeYData[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
indicator.volumeDataArray.push(zone.wholeVolumeData);
|
||||
});
|
||||
return priceZones;
|
||||
},
|
||||
// Function responsoble for drawing additional lines indicating zones
|
||||
drawZones: function (chart, yAxis, zonesValues, zonesStyles) {
|
||||
var indicator = this, renderer = chart.renderer, zoneLinesSVG = indicator.zoneLinesSVG, zoneLinesPath = [], leftLinePos = 0, rightLinePos = chart.plotWidth, verticalOffset = chart.plotTop, verticalLinePos;
|
||||
zonesValues.forEach(function (value) {
|
||||
verticalLinePos = yAxis.toPixels(value) - verticalOffset;
|
||||
zoneLinesPath = zoneLinesPath.concat(chart.renderer.crispLine([[
|
||||
'M',
|
||||
leftLinePos,
|
||||
verticalLinePos
|
||||
], [
|
||||
'L',
|
||||
rightLinePos,
|
||||
verticalLinePos
|
||||
]], zonesStyles.lineWidth));
|
||||
});
|
||||
// Create zone lines one path or update it while animating
|
||||
if (zoneLinesSVG) {
|
||||
zoneLinesSVG.animate({
|
||||
d: zoneLinesPath
|
||||
});
|
||||
}
|
||||
else {
|
||||
zoneLinesSVG = indicator.zoneLinesSVG =
|
||||
renderer.path(zoneLinesPath).attr({
|
||||
'stroke-width': zonesStyles.lineWidth,
|
||||
'stroke': zonesStyles.color,
|
||||
'dashstyle': zonesStyles.dashStyle,
|
||||
'zIndex': indicator.group.zIndex + 0.1
|
||||
})
|
||||
.add(indicator.group);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Point#
|
||||
*/
|
||||
{
|
||||
// Required for destroying negative part of volume
|
||||
destroy: function () {
|
||||
// @todo: this.negativeGraphic doesn't seem to be used anywhere
|
||||
if (this.negativeGraphic) {
|
||||
this.negativeGraphic = this.negativeGraphic.destroy();
|
||||
}
|
||||
return Point.prototype.destroy.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Volume By Price (VBP)` series. If the [type](#series.vbp.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.vbp
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/volume-by-price
|
||||
* @apioption series.vbp
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,147 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Paweł Dalek
|
||||
*
|
||||
* Volume Weighted Average Price (VWAP) indicator for Highstock
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var error = U.error, isArray = U.isArray, seriesType = U.seriesType;
|
||||
/**
|
||||
* The Volume Weighted Average Price (VWAP) series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.vwap
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('vwap', 'sma',
|
||||
/**
|
||||
* Volume Weighted Average Price indicator.
|
||||
*
|
||||
* This series requires `linkedTo` option to be set.
|
||||
*
|
||||
* @sample stock/indicators/vwap
|
||||
* Volume Weighted Average Price indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/vwap
|
||||
* @optionparent plotOptions.vwap
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index
|
||||
*/
|
||||
params: {
|
||||
period: 30,
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
volumeSeriesID: 'volume'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Returns the final values of the indicator ready to be presented on a
|
||||
* chart
|
||||
* @private
|
||||
* @param {Highcharts.VWAPIndicator} this indicator
|
||||
* @param {Highcharts.Series} series - series for indicator
|
||||
* @param {object} params - params
|
||||
* @return {object} - computed VWAP
|
||||
**/
|
||||
getValues: function (series, params) {
|
||||
var indicator = this, chart = series.chart, xValues = series.xData, yValues = series.yData, period = params.period, isOHLC = true, volumeSeries;
|
||||
// Checks if volume series exists
|
||||
if (!(volumeSeries = (chart.get(params.volumeSeriesID)))) {
|
||||
error('Series ' +
|
||||
params.volumeSeriesID +
|
||||
' not found! Check `volumeSeriesID`.', true, chart);
|
||||
return;
|
||||
}
|
||||
// Checks if series data fits the OHLC format
|
||||
if (!(isArray(yValues[0]))) {
|
||||
isOHLC = false;
|
||||
}
|
||||
return indicator.calculateVWAPValues(isOHLC, xValues, yValues, volumeSeries, period);
|
||||
},
|
||||
/**
|
||||
* Main algorithm used to calculate Volume Weighted Average Price (VWAP)
|
||||
* values
|
||||
* @private
|
||||
* @param {boolean} isOHLC - says if data has OHLC format
|
||||
* @param {Array<number>} xValues - array of timestamps
|
||||
* @param {Array<number|Array<number,number,number,number>>} yValues -
|
||||
* array of yValues, can be an array of a four arrays (OHLC) or array of
|
||||
* values (line)
|
||||
* @param {Array<*>} volumeSeries - volume series
|
||||
* @param {number} period - number of points to be calculated
|
||||
* @return {object} - Object contains computed VWAP
|
||||
**/
|
||||
calculateVWAPValues: function (isOHLC, xValues, yValues, volumeSeries, period) {
|
||||
var volumeValues = volumeSeries.yData, volumeLength = volumeSeries.xData.length, pointsLength = xValues.length, cumulativePrice = [], cumulativeVolume = [], xData = [], yData = [], VWAP = [], commonLength, typicalPrice, cPrice, cVolume, i, j;
|
||||
if (pointsLength <= volumeLength) {
|
||||
commonLength = pointsLength;
|
||||
}
|
||||
else {
|
||||
commonLength = volumeLength;
|
||||
}
|
||||
for (i = 0, j = 0; i < commonLength; i++) {
|
||||
// Depending on whether series is OHLC or line type, price is
|
||||
// average of the high, low and close or a simple value
|
||||
typicalPrice = isOHLC ?
|
||||
((yValues[i][1] + yValues[i][2] +
|
||||
yValues[i][3]) / 3) :
|
||||
yValues[i];
|
||||
typicalPrice *= volumeValues[i];
|
||||
cPrice = j ?
|
||||
(cumulativePrice[i - 1] + typicalPrice) :
|
||||
typicalPrice;
|
||||
cVolume = j ?
|
||||
(cumulativeVolume[i - 1] + volumeValues[i]) :
|
||||
volumeValues[i];
|
||||
cumulativePrice.push(cPrice);
|
||||
cumulativeVolume.push(cVolume);
|
||||
VWAP.push([xValues[i], (cPrice / cVolume)]);
|
||||
xData.push(VWAP[i][0]);
|
||||
yData.push(VWAP[i][1]);
|
||||
j++;
|
||||
if (j === period) {
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: VWAP,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Volume Weighted Average Price (VWAP)` series. If the
|
||||
* [type](#series.vwap.type) option is not specified, it is inherited from
|
||||
* [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.vwap
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/vwap
|
||||
* @apioption series.vwap
|
||||
*/
|
||||
''; // to include the above in the js output
|
||||
@@ -0,0 +1,126 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Kacper Madej
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
/* eslint-disable valid-jsdoc */
|
||||
// Utils:
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function accumulateAverage(points, xVal, yVal, i, index) {
|
||||
var xValue = xVal[i], yValue = index < 0 ? yVal[i] : yVal[i][index];
|
||||
points.push([xValue, yValue]);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function weightedSumArray(array, pLen) {
|
||||
// The denominator is the sum of the number of days as a triangular number.
|
||||
// If there are 5 days, the triangular numbers are 5, 4, 3, 2, and 1.
|
||||
// The sum is 5 + 4 + 3 + 2 + 1 = 15.
|
||||
var denominator = (pLen + 1) / 2 * pLen;
|
||||
// reduce VS loop => reduce
|
||||
return array.reduce(function (prev, cur, i) {
|
||||
return [null, prev[1] + cur[1] * (i + 1)];
|
||||
})[1] / denominator;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function populateAverage(points, xVal, yVal, i) {
|
||||
var pLen = points.length, wmaY = weightedSumArray(points, pLen), wmaX = xVal[i - 1];
|
||||
points.shift(); // remove point until range < period
|
||||
return [wmaX, wmaY];
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* The SMA series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.wma
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('wma', 'sma',
|
||||
/**
|
||||
* Weighted moving average indicator (WMA). This series requires `linkedTo`
|
||||
* option to be set.
|
||||
*
|
||||
* @sample stock/indicators/wma
|
||||
* Weighted moving average indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/wma
|
||||
* @optionparent plotOptions.wma
|
||||
*/
|
||||
{
|
||||
params: {
|
||||
index: 3,
|
||||
period: 9
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, range = 1, xValue = xVal[0], yValue = yVal[0], WMA = [], xData = [], yData = [], index = -1, i, points, WMAPoint;
|
||||
if (xVal.length < period) {
|
||||
return;
|
||||
}
|
||||
// Switch index for OHLC / Candlestick
|
||||
if (isArray(yVal[0])) {
|
||||
index = params.index;
|
||||
yValue = yVal[0][index];
|
||||
}
|
||||
// Starting point
|
||||
points = [[xValue, yValue]];
|
||||
// Accumulate first N-points
|
||||
while (range !== period) {
|
||||
accumulateAverage(points, xVal, yVal, range, index);
|
||||
range++;
|
||||
}
|
||||
// Calculate value one-by-one for each period in visible data
|
||||
for (i = range; i < yValLen; i++) {
|
||||
WMAPoint = populateAverage(points, xVal, yVal, i);
|
||||
WMA.push(WMAPoint);
|
||||
xData.push(WMAPoint[0]);
|
||||
yData.push(WMAPoint[1]);
|
||||
accumulateAverage(points, xVal, yVal, i, index);
|
||||
}
|
||||
WMAPoint = populateAverage(points, xVal, yVal, i);
|
||||
WMA.push(WMAPoint);
|
||||
xData.push(WMAPoint[0]);
|
||||
yData.push(WMAPoint[1]);
|
||||
return {
|
||||
values: WMA,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `WMA` series. If the [type](#series.wma.type) option is not specified, it
|
||||
* is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.wma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/wma
|
||||
* @apioption series.wma
|
||||
*/
|
||||
''; // adds doclet above to the transpiled file
|
||||
@@ -0,0 +1,106 @@
|
||||
/* *
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var isArray = U.isArray, seriesType = U.seriesType;
|
||||
import reduceArrayMixin from '../../Mixins/ReduceArray.js';
|
||||
var getArrayExtremes = reduceArrayMixin.getArrayExtremes;
|
||||
/**
|
||||
* The Williams %R series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.williamsr
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('williamsr', 'sma',
|
||||
/**
|
||||
* Williams %R. This series requires the `linkedTo` option to be
|
||||
* set and should be loaded after the `stock/indicators/indicators.js`.
|
||||
*
|
||||
* @sample {highstock} stock/indicators/williams-r
|
||||
* Williams %R
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 7.0.0
|
||||
* @product highstock
|
||||
* @excluding allAreas, colorAxis, joinBy, keys, navigatorOptions,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement,
|
||||
* pointRange, pointStart, showInNavigator, stacking
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/williams-r
|
||||
* @optionparent plotOptions.williamsr
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Paramters used in calculation of Williams %R series points.
|
||||
* @excluding index
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* Period for Williams %R oscillator
|
||||
*/
|
||||
period: 14
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameBase: 'Williams %R',
|
||||
getValues: function (series, params) {
|
||||
var period = params.period, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, WR = [], // 0- date, 1- Williams %R
|
||||
xData = [], yData = [], slicedY, close = 3, low = 2, high = 1, extremes, R, HH, // Highest high value in period
|
||||
LL, // Lowest low value in period
|
||||
CC, // Current close value
|
||||
i;
|
||||
// Williams %R requires close value
|
||||
if (xVal.length < period ||
|
||||
!isArray(yVal[0]) ||
|
||||
yVal[0].length !== 4) {
|
||||
return;
|
||||
}
|
||||
// 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 + 1);
|
||||
extremes = getArrayExtremes(slicedY, low, high);
|
||||
LL = extremes[0];
|
||||
HH = extremes[1];
|
||||
CC = yVal[i][close];
|
||||
R = ((HH - CC) / (HH - LL)) * -100;
|
||||
if (xVal[i]) {
|
||||
WR.push([xVal[i], R]);
|
||||
xData.push(xVal[i]);
|
||||
yData.push(R);
|
||||
}
|
||||
}
|
||||
return {
|
||||
values: WR,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Williams %R Oscillator` series. If the [type](#series.williamsr.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.williamsr
|
||||
* @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/williams-r
|
||||
* @apioption series.williamsr
|
||||
*/
|
||||
''; // adds doclets above to the transpiled file
|
||||
@@ -0,0 +1,182 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Kacper Madej
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
var UNDEFINED;
|
||||
/**
|
||||
* The Zig Zag series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.zigzag
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('zigzag', 'sma',
|
||||
/**
|
||||
* Zig Zag indicator.
|
||||
*
|
||||
* This series requires `linkedTo` option to be set.
|
||||
*
|
||||
* @sample stock/indicators/zigzag
|
||||
* Zig Zag indicator
|
||||
*
|
||||
* @extends plotOptions.sma
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/zigzag
|
||||
* @optionparent plotOptions.zigzag
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* @excluding index, period
|
||||
*/
|
||||
params: {
|
||||
/**
|
||||
* The point index which indicator calculations will base - low
|
||||
* value.
|
||||
*
|
||||
* For example using OHLC data, index=2 means the indicator will be
|
||||
* calculated using Low values.
|
||||
*/
|
||||
lowIndex: 2,
|
||||
/**
|
||||
* The point index which indicator calculations will base - high
|
||||
* value.
|
||||
*
|
||||
* For example using OHLC data, index=1 means the indicator will be
|
||||
* calculated using High values.
|
||||
*/
|
||||
highIndex: 1,
|
||||
/**
|
||||
* The threshold for the value change.
|
||||
*
|
||||
* For example deviation=1 means the indicator will ignore all price
|
||||
* movements less than 1%.
|
||||
*/
|
||||
deviation: 1
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
nameComponents: ['deviation'],
|
||||
nameSuffixes: ['%'],
|
||||
nameBase: 'Zig Zag',
|
||||
getValues: function (series, params) {
|
||||
var lowIndex = params.lowIndex, highIndex = params.highIndex, deviation = params.deviation / 100, deviations = {
|
||||
'low': 1 + deviation,
|
||||
'high': 1 - deviation
|
||||
}, xVal = series.xData, yVal = series.yData, yValLen = yVal ? yVal.length : 0, zigzag = [], xData = [], yData = [], i, j, zigzagPoint, firstZigzagLow, firstZigzagHigh, directionUp, zigzagLen, exitLoop = false, yIndex = false;
|
||||
// Exit if not enught points or no low or high values
|
||||
if (!xVal || xVal.length <= 1 ||
|
||||
(yValLen &&
|
||||
(yVal[0][lowIndex] === UNDEFINED ||
|
||||
yVal[0][highIndex] === UNDEFINED))) {
|
||||
return;
|
||||
}
|
||||
// Set first zigzag point candidate
|
||||
firstZigzagLow = yVal[0][lowIndex];
|
||||
firstZigzagHigh = yVal[0][highIndex];
|
||||
// Search for a second zigzag point candidate,
|
||||
// this will also set first zigzag point
|
||||
for (i = 1; i < yValLen; i++) {
|
||||
// requried change to go down
|
||||
if (yVal[i][lowIndex] <= firstZigzagHigh * deviations.high) {
|
||||
zigzag.push([xVal[0], firstZigzagHigh]);
|
||||
// second zigzag point candidate
|
||||
zigzagPoint = [xVal[i], yVal[i][lowIndex]];
|
||||
// next line will be going up
|
||||
directionUp = true;
|
||||
exitLoop = true;
|
||||
// requried change to go up
|
||||
}
|
||||
else if (yVal[i][highIndex] >= firstZigzagLow * deviations.low) {
|
||||
zigzag.push([xVal[0], firstZigzagLow]);
|
||||
// second zigzag point candidate
|
||||
zigzagPoint = [xVal[i], yVal[i][highIndex]];
|
||||
// next line will be going down
|
||||
directionUp = false;
|
||||
exitLoop = true;
|
||||
}
|
||||
if (exitLoop) {
|
||||
xData.push(zigzag[0][0]);
|
||||
yData.push(zigzag[0][1]);
|
||||
j = i++;
|
||||
i = yValLen;
|
||||
}
|
||||
}
|
||||
// Search for next zigzags
|
||||
for (i = j; i < yValLen; i++) {
|
||||
if (directionUp) { // next line up
|
||||
// lower when going down -> change zigzag candidate
|
||||
if (yVal[i][lowIndex] <= zigzagPoint[1]) {
|
||||
zigzagPoint = [xVal[i], yVal[i][lowIndex]];
|
||||
}
|
||||
// requried change to go down -> new zigzagpoint and
|
||||
// direction change
|
||||
if (yVal[i][highIndex] >=
|
||||
zigzagPoint[1] * deviations.low) {
|
||||
yIndex = highIndex;
|
||||
}
|
||||
}
|
||||
else { // next line down
|
||||
// higher when going up -> change zigzag candidate
|
||||
if (yVal[i][highIndex] >= zigzagPoint[1]) {
|
||||
zigzagPoint = [xVal[i], yVal[i][highIndex]];
|
||||
}
|
||||
// requried change to go down -> new zigzagpoint and
|
||||
// direction change
|
||||
if (yVal[i][lowIndex] <=
|
||||
zigzagPoint[1] * deviations.high) {
|
||||
yIndex = lowIndex;
|
||||
}
|
||||
}
|
||||
if (yIndex !== false) { // new zigzag point and direction change
|
||||
zigzag.push(zigzagPoint);
|
||||
xData.push(zigzagPoint[0]);
|
||||
yData.push(zigzagPoint[1]);
|
||||
zigzagPoint = [xVal[i], yVal[i][yIndex]];
|
||||
directionUp = !directionUp;
|
||||
yIndex = false;
|
||||
}
|
||||
}
|
||||
zigzagLen = zigzag.length;
|
||||
// no zigzag for last point
|
||||
if (zigzagLen !== 0 &&
|
||||
zigzag[zigzagLen - 1][0] < xVal[yValLen - 1]) {
|
||||
// set last point from zigzag candidate
|
||||
zigzag.push(zigzagPoint);
|
||||
xData.push(zigzagPoint[0]);
|
||||
yData.push(zigzagPoint[1]);
|
||||
}
|
||||
return {
|
||||
values: zigzag,
|
||||
xData: xData,
|
||||
yData: yData
|
||||
};
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `Zig Zag` series. If the [type](#series.zigzag.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.zigzag
|
||||
* @since 6.0.0
|
||||
* @product highstock
|
||||
* @excluding dataParser, dataURL
|
||||
* @requires stock/indicators/indicators
|
||||
* @requires stock/indicators/zigzag
|
||||
* @apioption series.zigzag
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user