You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

502 lines
14 KiB

/*!
* chartjs-chart-financial
* Version: 0.1.0
*
* Copyright 2017 chartjs-chart-financial contributors
* Released under the MIT license
* https://github.com/chartjs/chartjs-chart-financial/blob/master/LICENSE.md
*/
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
},{}],2:[function(require,module,exports){
'use strict';
module.exports = function(Chart) {
Chart.defaults.candlestick = Object.assign({}, Chart.defaults.financial);
Chart.defaults.candlestick.scales = {
xAxes: [Object.assign({}, Chart.defaults.financial.scales.xAxes[0])],
yAxes: [Object.assign({}, Chart.defaults.financial.scales.yAxes[0])]
};
Chart.controllers.candlestick = Chart.controllers.financial.extend({
dataElementType: Chart.elements.Candlestick,
updateElement: function(element, index, reset) {
var me = this;
var meta = me.getMeta();
var dataset = me.getDataset();
element._xScale = me.getScaleForId(meta.xAxisID);
element._yScale = me.getScaleForId(meta.yAxisID);
element._datasetIndex = me.index;
element._index = index;
element._model = {
datasetLabel: dataset.label || '',
// label: '', // to get label value please use dataset.data[index].label
// Appearance
color: dataset.color,
borderColor: dataset.borderColor,
borderWidth: dataset.borderWidth,
};
me.updateElementGeometry(element, index, reset);
element.pivot();
},
});
};
},{}],3:[function(require,module,exports){
'use strict';
module.exports = function(Chart) {
Chart.defaults.financial = {
label: '',
hover: {
mode: 'label'
},
scales: {
xAxes: [{
type: 'time',
distribution: 'series',
categoryPercentage: 0.8,
barPercentage: 0.9,
ticks: {
source: 'data'
}
}],
yAxes: [{
type: 'financialLinear'
}]
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
var o = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].o;
var h = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].h;
var l = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].l;
var c = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].c;
var fractionalDigitsCount = data.datasets[tooltipItem.datasetIndex].fractionalDigitsCount;
if (fractionalDigitsCount !== undefined) {
fractionalDigitsCount = Math.max(0, Math.min(100, fractionalDigitsCount));
o = o.toFixed(fractionalDigitsCount);
h = h.toFixed(fractionalDigitsCount);
l = l.toFixed(fractionalDigitsCount);
c = c.toFixed(fractionalDigitsCount);
}
return ' O: ' + o + ' H: ' + h + ' L: ' + l + ' C: ' + c;
}
}
}
};
/**
* This class is based off controller.bar.js from the upstream Chart.js library
*/
Chart.controllers.financial = Chart.controllers.bar.extend({
dataElementType: Chart.elements.Financial,
/**
* @private
*/
updateElementGeometry: function(element, index, reset) {
var me = this;
var model = element._model;
var vscale = me.getValueScale();
var base = vscale.getBasePixel();
var horizontal = vscale.isHorizontal();
var ruler = me._ruler || me.getRuler();
var vpixels = me.calculateBarValuePixels(me.index, index);
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
var chart = me.chart;
var datasets = chart.data.datasets;
var indexData = datasets[me.index].data[index];
model.horizontal = horizontal;
model.base = reset ? base : vpixels.base;
model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
model.height = horizontal ? ipixels.size : undefined;
model.width = horizontal ? undefined : ipixels.size;
model.candleOpen = vscale.getPixelForValue(Number(indexData.o));
model.candleHigh = vscale.getPixelForValue(Number(indexData.h));
model.candleLow = vscale.getPixelForValue(Number(indexData.l));
model.candleClose = vscale.getPixelForValue(Number(indexData.c));
},
draw: function() {
var ctx = this.chart.chart.ctx;
var elements = this.getMeta().data;
var dataset = this.getDataset();
var ilen = elements.length;
var i = 0;
var d;
Chart.canvasHelpers.clipArea(ctx, this.chart.chartArea);
for (; i < ilen; ++i) {
d = dataset.data[i].o;
if (d !== null && d !== undefined && !isNaN(d)) {
elements[i].draw();
}
}
Chart.canvasHelpers.unclipArea(ctx);
},
});
};
},{}],4:[function(require,module,exports){
'use strict';
module.exports = function(Chart) {
Chart.defaults.ohlc = Object.assign({}, Chart.defaults.financial);
Chart.defaults.ohlc.scales = {
xAxes: [Object.assign({}, Chart.defaults.financial.scales.xAxes[0])],
yAxes: [Object.assign({}, Chart.defaults.financial.scales.yAxes[0])]
};
Chart.defaults.ohlc.scales.xAxes[0].barPercentage = 1.0;
Chart.defaults.ohlc.scales.xAxes[0].categoryPercentage = 1.0;
Chart.controllers.ohlc = Chart.controllers.financial.extend({
dataElementType: Chart.elements.Ohlc,
updateElement: function(element, index, reset) {
var me = this;
var meta = me.getMeta();
var dataset = me.getDataset();
element._xScale = me.getScaleForId(meta.xAxisID);
element._yScale = me.getScaleForId(meta.yAxisID);
element._datasetIndex = me.index;
element._index = index;
element._model = {
datasetLabel: dataset.label || '',
lineWidth: dataset.lineWidth,
armLength: dataset.armLength,
armLengthRatio: dataset.armLengthRatio,
color: dataset.color,
};
me.updateElementGeometry(element, index, reset);
element.pivot();
},
});
};
},{}],5:[function(require,module,exports){
'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
var globalOpts = Chart.defaults.global;
globalOpts.elements.candlestick = Object.assign(globalOpts.elements.financial, {
borderColor: globalOpts.elements.financial.color.unchanged,
borderWidth: 1,
});
Chart.elements.Candlestick = Chart.elements.Financial.extend({
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var x = vm.x;
var o = vm.candleOpen;
var h = vm.candleHigh;
var l = vm.candleLow;
var c = vm.candleClose;
ctx.strokeStyle = helpers.getValueOrDefault(vm.borderColor, globalOpts.elements.candlestick.borderColor);
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.candlestick.borderWidth);
if (c < o) {
ctx.fillStyle = helpers.getValueOrDefault(vm.color ? vm.color.up : undefined, globalOpts.elements.candlestick.color.up);
} else if (c > o) {
ctx.fillStyle = helpers.getValueOrDefault(vm.color ? vm.color.down : undefined, globalOpts.elements.candlestick.color.down);
} else {
ctx.fillStyle = helpers.getValueOrDefault(vm.color ? vm.color.unchanged : undefined, globalOpts.elements.candlestick.color.unchanged);
}
ctx.beginPath();
ctx.moveTo(x, h);
ctx.lineTo(x, Math.min(o, c));
ctx.moveTo(x, l);
ctx.lineTo(x, Math.max(o, c));
ctx.stroke();
ctx.fillRect(x - vm.width / 2, c, vm.width, o - c);
ctx.strokeRect(x - vm.width / 2, c, vm.width, o - c);
ctx.closePath();
},
});
};
},{}],6:[function(require,module,exports){
'use strict';
module.exports = function(Chart) {
var globalOpts = Chart.defaults.global;
globalOpts.elements.financial = {
color: {
up: 'rgba(80, 160, 115, 1)',
down: 'rgba(215, 85, 65, 1)',
unchanged: 'rgba(90, 90, 90, 1)',
},
fractionalDigitsCount: undefined,
};
function isVertical(bar) {
return bar._view.width !== undefined;
}
/**
* Helper function to get the bounds of the candle
* @private
* @param bar {Chart.Element.financial} the bar
* @return {Bounds} bounds of the bar
*/
function getBarBounds(candle) {
var vm = candle._view;
var x1, x2, y1, y2;
var halfWidth = vm.width / 2;
x1 = vm.x - halfWidth;
x2 = vm.x + halfWidth;
y1 = vm.candleHigh;
y2 = vm.candleLow;
return {
left: x1,
top: y1,
right: x2,
bottom: y2
};
}
Chart.elements.Financial = Chart.Element.extend({
height: function() {
var vm = this._view;
return vm.base - vm.y;
},
inRange: function(mouseX, mouseY) {
var inRange = false;
if (this._view) {
var bounds = getBarBounds(this);
inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
}
return inRange;
},
inLabelRange: function(mouseX, mouseY) {
var me = this;
if (!me._view) {
return false;
}
var inRange = false;
var bounds = getBarBounds(me);
if (isVertical(me)) {
inRange = mouseX >= bounds.left && mouseX <= bounds.right;
} else {
inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
}
return inRange;
},
inXRange: function(mouseX) {
var bounds = getBarBounds(this);
return mouseX >= bounds.left && mouseX <= bounds.right;
},
inYRange: function(mouseY) {
var bounds = getBarBounds(this);
return mouseY >= bounds.top && mouseY <= bounds.bottom;
},
getCenterPoint: function() {
var vm = this._view;
var x, y;
var halfWidth = vm.width / 2;
x = vm.x - halfWidth;
y = (vm.candleHigh + vm.candleLow) / 2;
return {x: x, y: y};
},
getArea: function() {
var vm = this._view;
return vm.width * Math.abs(vm.y - vm.base);
},
tooltipPosition: function() {
var vm = this._view;
return {
x: vm.x,
y: (vm.candleHigh + vm.candleLow) / 2
};
}
});
};
},{}],7:[function(require,module,exports){
'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
var globalOpts = Chart.defaults.global;
globalOpts.elements.ohlc = Object.assign(globalOpts.elements.financial, {
lineWidth: 2,
armLength: null,
armLengthRatio: 0.8,
});
Chart.elements.Ohlc = Chart.elements.Financial.extend({
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var x = vm.x;
var o = vm.candleOpen;
var h = vm.candleHigh;
var l = vm.candleLow;
var c = vm.candleClose;
var armLength = helpers.getValueOrDefault(vm.armLength, globalOpts.elements.ohlc.armLength);
var armLengthRatio = helpers.getValueOrDefault(vm.armLengthRatio, globalOpts.elements.ohlc.armLengthRatio);
if (armLength === null) {
// The width of an ohlc is affected by barPercentage and categoryPercentage
// This behavior is caused by extending controller.financial, which extends controller.bar
// barPercentage and categoryPercentage are now set to 1.0 (see controller.ohlc)
// and armLengthRatio is multipled by 0.5,
// so that when armLengthRatio=1.0, the arms from neighbour ohcl touch,
// and when armLengthRatio=0.0, ohcl are just vertical lines.
armLength = vm.width * armLengthRatio * 0.5;
}
if (c < o) {
ctx.strokeStyle = helpers.getValueOrDefault(vm.color ? vm.color.up : undefined, globalOpts.elements.ohlc.color.up);
} else if (c > o) {
ctx.strokeStyle = helpers.getValueOrDefault(vm.color ? vm.color.down : undefined, globalOpts.elements.ohlc.color.down);
} else {
ctx.strokeStyle = helpers.getValueOrDefault(vm.color ? vm.color.unchanged : undefined, globalOpts.elements.ohlc.color.unchanged);
}
ctx.lineWidth = helpers.getValueOrDefault(vm.lineWidth, globalOpts.elements.ohlc.lineWidth);
ctx.beginPath();
ctx.moveTo(x, h);
ctx.lineTo(x, l);
ctx.moveTo(x - armLength, o);
ctx.lineTo(x, o);
ctx.moveTo(x + armLength, c);
ctx.lineTo(x, c);
ctx.stroke();
},
});
};
},{}],8:[function(require,module,exports){
'use strict';
var Chart = require('chart.js');
Chart = typeof Chart === 'function' ? Chart : window.Chart;
require('./scale.financialLinear.js')(Chart);
require('./element.financial.js')(Chart);
require('./element.candlestick.js')(Chart);
require('./element.ohlc.js')(Chart);
require('./controller.financial.js')(Chart);
require('./controller.candlestick.js')(Chart);
require('./controller.ohlc.js')(Chart);
},{"./controller.candlestick.js":2,"./controller.financial.js":3,"./controller.ohlc.js":4,"./element.candlestick.js":5,"./element.financial.js":6,"./element.ohlc.js":7,"./scale.financialLinear.js":9,"chart.js":1}],9:[function(require,module,exports){
'use strict';
module.exports = function(Chart) {
var helpers = Chart.helpers;
var defaultConfig = {
position: 'left',
ticks: {
callback: Chart.Ticks.formatters.linear
}
};
var FinancialLinearScale = Chart.scaleService.getScaleConstructor('linear').extend({
determineDataLimits: function() {
var me = this;
var chart = me.chart;
var data = chart.data;
var datasets = data.datasets;
var isHorizontal = me.isHorizontal();
function IDMatches(meta) {
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
}
// First Calculate the range
me.min = null;
me.max = null;
// Regular charts use x, y values
// For the financial chart we have rawValue.h (hi) and rawValue.l (low) for each point
helpers.each(datasets, function(dataset, datasetIndex) {
var meta = chart.getDatasetMeta(datasetIndex);
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
helpers.each(dataset.data, function(rawValue) {
var high = rawValue.h;
var low = rawValue.l;
if (me.min === null) {
me.min = low;
} else if (low < me.min) {
me.min = low;
}
if (me.max === null) {
me.max = high;
} else if (high > me.max) {
me.max = high;
}
});
}
});
// Add whitespace around bars. Axis shouldn't go exactly from min to max
var space = (me.max - me.min) * 0.05;
me.min -= space;
me.max += space;
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
this.handleTickRangeOptions();
}
});
Chart.scaleService.registerScaleType('financialLinear', FinancialLinearScale, defaultConfig);
};
},{}]},{},[8]);