highcharts-indicators
Version:
Highstock plugin to add technical indicators to charts.
196 lines (172 loc) • 4.97 kB
JavaScript
/* global Highcharts module:true */
(function (factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory;
} else {
factory(Highcharts);
}
}(function (HC) {
'use strict';
/**
Each indicator requires mothods:
- getDefaultOptions() - returns object with default parameters, like period etc.
- getValues(chart, series, options, points) - returns array of calculated values for indicator
- getGraph(chart, series, options, values) - returns path, or columns as SVG elements to add.
Doesn't add to the chart via renderer!
**/
function toFixed(a, n) {
return parseFloat(a.toFixed(n));
}
var UNDEFINED,
each = HC.each,
merge = HC.merge,
isArray = HC.isArray,
addAxisPane = HC.Axis.prototype.addAxisPane;
HC.Indicator.prototype.rsi = {
getDefaultOptions: function () {
return {
period: 14,
overbought: 70,
oversold: 30,
approximation: 'average',
decimals: 4
};
},
getValues: function (chart, series, options, points) {
var utils = this.utils,
params = options.params,
period = params.period,
xVal = points[0].concat(series.processedXData || []), // #22
yVal = points[1].concat(series.processedYData || []), // #22
yValLen = yVal ? yVal.length : 0,
//EMA = HC.Indicator.prototype.ema,
decimals = params.decimals,
// EMApercent = (2 / (period + 1)),
// calEMAGain = 0,
// calEMALoss = 0,
range = 1,
RSI = [],
xData = [],
yData = [],
index = 3,
gain = [],
loss = [],
RSIPoint, change, RS, avgGain, avgLoss, i;
// atr requires close value
if ((xVal.length <= period) || !isArray(yVal[0]) || yVal[0].length !== 4 /*|| EMA === UNDEFINED*/) {
return false;
}
// accumulate first N-points
while (range < period + 1) {
change = toFixed(yVal[range][index] - yVal[range - 1][index], decimals);
gain.push(change > 0 ? change : 0);
loss.push(change < 0 ? Math.abs(change) : 0);
range++;
}
for (i = range - 1; i < yValLen; i++) {
// gain.push(change > 0 ? change : 0);
// len = loss.push(change < 0 ? Math.abs(change) : 0); // better than loss.length
// EMA for loss and gains
// avgGain = EMA.utils.populateAverage([], gain, [ yVal[i-1], [utils.sumArray(gain) / len] ], 2, EMApercent, calEMAGain, 0);
// avgLoss = EMA.utils.populateAverage([], gain, [ yVal[i-1], [utils.sumArray(loss) / len] ], 2, EMApercent, calEMALoss, 0);
if (i > range - 1) {
// remove first point from array
gain.shift();
loss.shift();
// calculate new change
change = toFixed(yVal[i][index] - yVal[i - 1][index], decimals);
// add to array
gain.push(change > 0 ? change : 0);
loss.push(change < 0 ? Math.abs(change) : 0);
}
// calculate averages, RS, RSI values:
avgGain = toFixed(utils.sumArray(gain) / period, decimals);
avgLoss = toFixed(utils.sumArray(loss) / period, decimals);
if (avgLoss === 0) {
RS = 100;
} else {
RS = toFixed(avgGain / avgLoss, decimals);
}
RSIPoint = toFixed(100 - (100 / (1 + RS)), decimals);
RSI.push([xVal[i], RSIPoint]);
xData.push(xVal[i]);
yData.push(RSIPoint);
// calEMAGain = avgGain[1];
// calEMALoss = avgLoss[1];
}
options.yAxisMax = 100;
options.yAxisMin = 0;
return {
values: RSI,
xData: xData,
yData: yData
};
},
getGraph: function (chart, series, options, values) {
var path = [],
attrs = {},
xAxis = series.xAxis,
atr = values,
atrLen = atr.length,
defaultOptions,
userOptions,
yAxis,
index,
atrX,
atrY,
i;
defaultOptions = {
min: 0,
tickInterval: 25,
plotLines: [{
value: options.params.overbought,
color: 'orange',
width: 1
}, {
value: options.params.oversold,
color: 'orange',
width: 1
}],
// height: 100,
max: 100,
title: {
text: 'RSI'
}
};
if (options.visible === false) {
return false;
}
userOptions = merge(defaultOptions, options.yAxis);
if (options.Axis === UNDEFINED) {
index = addAxisPane(chart, userOptions);
options.Axis = chart.yAxis[index];
} else {
each(options.Axis.plotLinesAndBands, function (p, j) {
p.options = merge(p.options, userOptions.plotLines[j]);
p.render();
});
}
yAxis = options.Axis;
options.styles = attrs = merge({
'stroke-width': 2,
stroke: 'red',
dashstyle: 'Dash'
}, options.styles);
path.push('M', xAxis.toPixels(atr[0][0]), yAxis.toPixels(atr[0][1]));
for (i = 0; i < atrLen; i++) {
atrX = atr[i][0];
atrY = atr[i][1];
path.push('L', xAxis.toPixels(atrX), yAxis.toPixels(atrY));
}
return [chart.renderer.path(path).attr(attrs)];
},
utils: {
sumArray: function (array) {
// reduce VS loop => reduce
return array.reduce(function (prev, cur) {
return prev + cur;
});
}
}
};
}));