candlestick-to-png
Version:
Lightweight library to draw an array of candles into a canvas and render it as png file.
1,024 lines (1,023 loc) • 51.1 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CandleStickGraph = void 0;
var technicalindicators_1 = require("technicalindicators");
var CandleStickDrag = /** @class */ (function () {
function CandleStickDrag() {
this.dragging = false;
this.dragEnd = CandleStickPoint.origin;
this.dragStart = CandleStickPoint.origin;
}
return CandleStickDrag;
}());
var CandleStickPoint = /** @class */ (function () {
function CandleStickPoint() {
this.x = 0;
this.y = 0;
}
CandleStickPoint.origin = { x: 0, y: 0 };
return CandleStickPoint;
}());
var CandleStickGraph = /** @class */ (function () {
function CandleStickGraph(options) {
var _this = this;
this.options = {
wantLines: false,
wantCandles: true,
zoom: 0,
zoomSpeed: 0.15,
wantTrades: false,
wantSMA: false,
wantEMA: false,
granularity: 1,
wantRSI: true,
wantMACD: true,
wantBollingerBands: true,
wantGrid: true,
wantStats: false,
triangleSize: 10,
lineWidth: 1,
lineWidthGreen: 1,
lineWidthRed: 1,
wantSideMarksMove: false,
baseFontName: 'Arial'
};
this.candlesticks = [];
this.selectedCandleStick = [];
this.moves = [];
this.drops = [];
this.rightCandlesOffset = 0;
this.curRightCandleOffset = 0;
this.dragInfo = {
dragging: false,
dragStart: CandleStickPoint.origin,
dragEnd: CandleStickPoint.origin
};
this.colorInfo = {
gridColor: "#e2e2e2",
gridTextColor: "#a0a0a0",
mouseHoverBackgroundColor: "#f5f5f5",
lineColor: "#a0a0a0",
mouseHoverTextColor: "#000000",
greenColor: "#C1FF72",
redColor: "#CB6CE6",
greenHoverColor: "#27ae60",
redHoverColor: "#c0392b",
debugLineColor: "#D11538",
growLineColor: "#a0a0a0",
blackColor: "#131422",
whiteColor: "#ffffff",
yellowColor: "#f9ca24",
purpleColor: "#9b59b6",
purpleColorTransparent: "rgba(155,89,182,0.21)",
yellowColorTransparent: "rgba(249,202,36,0.2)",
whiteColorTrasparent: 'rgba(255,255,255,0.55)',
blueColor: '#3498db',
greenAreaColor: 'rgba(46,204,113,0.27)',
redAreaColor: 'rgba(231,76,60,0.27)',
greenAreaColorIntens: 'rgba(46,204,113,0.57)',
redAreaColorIntes: 'rgba(231,76,60,0.57)',
whiteColorMoreTrasparent: 'rgba(255,255,255,0.25)',
logoColor: 'white'
};
this.GENERAL_FONT_SIZE = 12;
this.TEXT_FONT_SIZE = 20;
this.draw = function () {
_this.context.clearRect(0, 0, _this.width, _this.height);
_this.fillRect(0, 0, _this.width, _this.height, _this.colorInfo.blackColor);
_this.selectedCandleStick = _this.getSelectedCandleStick();
_this.calculateYRange(_this.selectedCandleStick);
_this.calculateXRange(_this.selectedCandleStick);
_this.drawGrid();
_this.candleWidth = _this.calculateCandleWidth();
var points = [];
if (_this.options.wantRSI) {
var rsiAreas = _this.calculateRsi();
_this.drawOverAreas(rsiAreas);
}
if (_this.options.wantBollingerBands) {
var bollingerBandsRanges = _this.calculateBollingerBands(18);
_this.drawBollingRangeArea(bollingerBandsRanges, _this.colorInfo.purpleColorTransparent);
}
if (_this.options.wantMACD) {
var emaPoints12 = _this.calculateEma(12);
_this.drawLines(emaPoints12, _this.colorInfo.purpleColor);
var emaPoints26 = _this.calculateEma(26);
_this.drawLines(emaPoints26, _this.colorInfo.purpleColor);
_this.drawMACD(emaPoints12, emaPoints26);
}
if (_this.options.wantSMA) {
var smaPoints = _this.calculateSma();
_this.drawLines(smaPoints, _this.colorInfo.yellowColor);
}
if (_this.options.wantEMA) {
var emaPoints26 = _this.calculateEma(26);
_this.drawLines(emaPoints26, _this.colorInfo.purpleColor);
}
for (var i = 0; i < _this.selectedCandleStick.length; ++i) {
_this.drawCandlesAndValuatePoints(i, points);
}
if (_this.options.wantLines) {
_this.drawLines(points, _this.colorInfo.lineColor);
}
if (_this.options.wantTrades) {
_this.drawMoves();
}
// draw mouse hover
if (_this.b_drawMouseOverlay) {
// price line
var _a = _this.drawPriceLine(), str = _a.str, textWidth = _a.textWidth;
// time line
_this.drawTimeLine(str, textWidth);
// data
_this.drawInfoLabel();
}
if (_this.options.wantStats) {
try {
_this.drawStatsLabel();
}
catch (e) {
console.error(e);
}
try {
_this.drawLogo();
}
catch (e) {
console.error(e);
}
}
};
this.addCandlestick = function (candlestick, adaptZoom) {
_this.candlesticks.push(candlestick);
if (adaptZoom) {
_this.selectedCandleStick = _this.candlesticks.slice(Math.max(0, _this.options.zoom));
if ((_this.xPixelRange / _this.selectedCandleStick.length) < 3) {
_this.options.zoom += 2;
}
}
};
this.mouseMove = function (e) {
var getMousePos = function (e) {
// @ts-ignore
var rect = _this.canvas.getBoundingClientRect();
return { x: e.clientX - rect.left, y: e.clientY - rect.top };
};
_this.mousePosition = getMousePos(e);
_this.mousePosition.x += _this.candleWidth / 2;
_this.b_drawMouseOverlay = _this.mousePosition.x >= _this.marginLeft;
if (_this.mousePosition.x > _this.width - _this.marginRight + _this.candleWidth)
_this.b_drawMouseOverlay = false;
if (_this.mousePosition.y > _this.height - _this.marginBottom)
_this.b_drawMouseOverlay = false;
if (_this.b_drawMouseOverlay) {
_this.yMouseHover = _this.yToValueCoords(_this.mousePosition.y);
_this.xMouseHover = _this.xToValueCoords(_this.mousePosition.x);
// snap to candlesticks
var candlestickDelta = _this.selectedCandleStick[1].timestamp - _this.selectedCandleStick[0].timestamp;
_this.hoveredCandlestickID = Math.floor((_this.xMouseHover - _this.selectedCandleStick[0].timestamp) / candlestickDelta);
_this.xMouseHover = Math.floor(_this.xMouseHover / candlestickDelta) * candlestickDelta;
_this.mousePosition.x = _this.xToPixelCoords(_this.xMouseHover);
_this.draw();
}
else
_this.draw();
};
this.mouseUp = function () {
_this.b_drawMouseOverlay = false;
_this.dragInfo.dragging = false;
_this.dragInfo.dragStart = _this.dragInfo.dragEnd;
_this.curRightCandleOffset =
Math.max(0, _this.rightCandlesOffset + _this.curRightCandleOffset);
_this.rightCandlesOffset = 0;
};
this.mouseDown = function (e) {
_this.b_drawMouseOverlay = false;
_this.dragInfo.dragStart = {
x: e.pageX - _this.canvas.offsetLeft,
y: e.pageY - _this.canvas.offsetTop
};
_this.dragInfo.dragging = true;
};
this.mouseDrag = function (e) {
_this.b_drawMouseOverlay = false;
if (_this.dragInfo.dragging) {
_this.dragInfo.dragEnd = {
x: e.pageX - _this.canvas.offsetLeft,
y: e.pageY - _this.canvas.offsetTop
};
_this.rightCandlesOffset = (_this.dragInfo.dragEnd.x - _this.dragInfo.dragStart.x) / _this.candleWidth;
_this.draw();
}
};
this.mouseOut = function (e) {
_this.b_drawMouseOverlay = false;
_this.mouseUp();
_this.draw();
};
this.mouseWheel = function (e) {
var AT_LEAST_CANDLES = 20;
var zoomFactor = Math.min(15, e.deltaY * -_this.options.zoomSpeed);
var noNegativeZoom = _this.options.zoom + zoomFactor > 0;
var noTooManyCandles = zoomFactor > 0 || (_this.xPixelRange / (_this.selectedCandleStick.length + zoomFactor)) > 2;
var noSoFewCandles = zoomFactor < 0 || _this.selectedCandleStick.length > Math.max(AT_LEAST_CANDLES, 20);
if (noNegativeZoom && noTooManyCandles && noSoFewCandles) {
_this.options.zoom += zoomFactor;
_this.draw();
}
};
this.drawGrid = function () {
var yGridSize = (_this.yRange) / _this.yGridCells;
var niceNumber = Math.pow(10, Math.ceil(Math.log10(yGridSize)));
if (yGridSize < 0.25 * niceNumber)
niceNumber = 0.25 * niceNumber;
else if (yGridSize < 0.5 * niceNumber)
niceNumber = 0.5 * niceNumber;
var yStartRoundNumber = Math.ceil(_this.yStart / niceNumber) * niceNumber;
var yEndRoundNumber = Math.floor(_this.yEnd / niceNumber) * niceNumber;
for (var y = yStartRoundNumber; y <= yEndRoundNumber; y += niceNumber) {
if (_this.options.wantGrid)
_this.drawLine(0, _this.yToPixelCoords(y), _this.width, _this.yToPixelCoords(y), _this.colorInfo.gridColor);
var textWidth = _this.context.measureText(String(_this.roundPriceValue(y))).width;
_this.context.fillStyle = _this.colorInfo.gridTextColor;
_this.context.fillText(String(_this.roundPriceValue(y)), _this.width - textWidth - 5, _this.yToPixelCoords(y) - 5);
}
var xGridSize = (_this.xRange) / _this.xGridCells;
niceNumber = Math.pow(10, Math.ceil(Math.log10(xGridSize)));
if (xGridSize < 0.25 * niceNumber)
niceNumber = 0.25 * niceNumber;
else if (xGridSize < 0.5 * niceNumber)
niceNumber = 0.5 * niceNumber;
var xStartRoundNumber = Math.ceil(_this.xStart / niceNumber) * niceNumber;
var xEndRoundNumber = Math.floor(_this.xEnd / niceNumber) * niceNumber;
var b_formatAsDate = false;
if (_this.xRange > 60 * 60 * 24 * 1000 * 5)
b_formatAsDate = true;
for (var x = xStartRoundNumber; x <= xEndRoundNumber; x += niceNumber) {
if (_this.options.wantGrid)
_this.drawLine(_this.xToPixelCoords(x), 0, _this.xToPixelCoords(x), _this.height, _this.colorInfo.gridColor);
var date = new Date(x);
var dateStr = "";
if (b_formatAsDate) {
var day = date.getDate();
if (day < 10)
day = "0" + day;
var month = date.getMonth() + 1;
if (month < 10)
month = "0" + month;
dateStr = day + "." + month;
}
else {
var minutes = date.getMinutes();
if (minutes < 10)
minutes = "0" + minutes;
dateStr = date.getHours() + ":" + minutes;
}
_this.context.fillStyle = _this.colorInfo.gridTextColor;
_this.context.fillText(dateStr, _this.xToPixelCoords(x) + 5, _this.height - 5);
}
};
this.calculateYRange = function (candlesticks) {
for (var i = 0; i < candlesticks.length; ++i) {
if (i === 0) {
_this.yStart = candlesticks[i].low;
_this.yEnd = candlesticks[i].high;
}
else {
if (candlesticks[i].low < _this.yStart) {
_this.yStart = candlesticks[i].low;
}
if (candlesticks[i].high > _this.yEnd) {
_this.yEnd = candlesticks[i].high;
}
}
}
_this.yRange = _this.yEnd - _this.yStart;
};
this.calculateXRange = function (candlesticks) {
_this.xStart = candlesticks[0].timestamp;
_this.xEnd = candlesticks[candlesticks.length - 1].timestamp;
_this.xRange = _this.xEnd - _this.xStart;
};
this.yToPixelCoords = function (y) {
return _this.height - _this.marginBottom - (y - _this.yStart) * _this.yPixelRange / _this.yRange;
};
this.xToPixelCoords = function (x) {
return _this.marginLeft + (x - _this.xStart) * _this.xPixelRange / _this.xRange;
};
this.yToValueCoords = function (y) {
return _this.yStart + (_this.height - _this.marginBottom - y) * _this.yRange / _this.yPixelRange;
};
this.xToValueCoords = function (x) {
return _this.xStart + (x - _this.marginLeft) * _this.xRange / _this.xPixelRange;
};
this.drawLine = function (xStart, yStart, xEnd, yEnd, color) {
_this.context.beginPath();
// to get a crisp 1 pixel wide line, we need to add 0.5 to the coords
_this.context.moveTo(xStart + 0.5, yStart + 0.5);
_this.context.lineTo(xEnd + 0.5, yEnd + 0.5);
_this.context.strokeStyle = color;
_this.context.stroke();
};
this.fillRect = function (x, y, width, height, color) {
_this.context.beginPath();
_this.context.fillStyle = color;
_this.context.rect(x, y, width, height);
_this.context.fill();
};
this.fillRectRounded = function (x, y, width, height, color, radius) {
_this.context.beginPath();
_this.context.fillStyle = color;
_this.context.moveTo(x + radius, y);
_this.context.lineTo(x + width - radius, y);
_this.context.quadraticCurveTo(x + width, y, x + width, y + radius);
_this.context.lineTo(x + width, y + height - radius);
_this.context.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
_this.context.lineTo(x + radius, y + height);
_this.context.quadraticCurveTo(x, y + height, x, y + height - radius);
_this.context.lineTo(x, y + radius);
_this.context.quadraticCurveTo(x, y, x + radius, y);
_this.context.closePath();
_this.context.fill();
};
this.drawTriangleUp = function (x, y, lat, color, label) {
_this.context.beginPath();
_this.context.fillStyle = color;
_this.context.moveTo(x + lat, y + lat);
_this.context.lineTo(x, y - lat / 1.5);
_this.context.lineTo(x - lat, y + lat);
_this.context.fill();
if (label) {
_this.context.font = CandleStickGraph.getFont((_this.TEXT_FONT_SIZE * 2), _this.options.baseFontName);
var textW = _this.context.measureText(label).width;
_this.context.fillStyle = _this.colorInfo.whiteColorMoreTrasparent;
_this.context.fillText(label, x - textW / 2, y - lat * 8);
}
};
this.drawTriangleDown = function (x, y, lat, color, label) {
_this.context.beginPath();
_this.context.fillStyle = color;
_this.context.moveTo(x + lat, y - lat);
_this.context.lineTo(x, y + lat / 1.5);
_this.context.lineTo(x - lat, y - lat);
_this.context.fill();
if (label) {
_this.context.font = CandleStickGraph.getFont((_this.TEXT_FONT_SIZE * 2), _this.options.baseFontName);
var textW = _this.context.measureText(label).width;
_this.context.fillStyle = _this.colorInfo.whiteColorMoreTrasparent;
_this.context.fillText(label, x - textW / 2, y + lat * 8);
}
};
this.drawRect = function (x, y, width, height, color) {
_this.context.beginPath();
_this.context.strokeStyle = color;
_this.context.rect(x, y, width, height);
_this.context.stroke();
};
this.formatDate = function (date) {
var day = date.getDate();
if (day < 10)
day = "0" + day;
var month = date.getMonth() + 1;
if (month < 10)
month = "0" + month;
var hours = date.getHours();
if (hours < 10)
hours = "0" + hours;
var minutes = date.getMinutes();
if (minutes < 10)
minutes = "0" + minutes;
return day + "." + month + "." + date.getFullYear() + " - " + hours + ":" + minutes;
};
this.roundPriceValue = function (value) {
if (value > 1.0)
return Math.round(value * 100) / 100;
if (value > 0.001)
return Math.round(value * 1000) / 1000;
if (value > 0.00001)
return Math.round(value * 100000) / 100000;
if (value > 0.0000001)
return Math.round(value * 10000000) / 10000000;
else
return Math.round(value * 1000000000) / 1000000000;
};
this.formatDateNoHours = function (date) {
var day = date.getDate();
if (day < 10)
day = "0" + day;
var month = date.getMonth() + 1;
if (month < 10)
month = "0" + month;
return day + "." + month + "." + date.getFullYear();
};
this.apply(options);
}
CandleStickGraph.prototype.toCandleStick = function (candle) {
if (!candle.candle) {
return {
timestamp: 0, open: 0, close: 0, high: 0, low: 0
};
}
return {
timestamp: candle.openTimeInMillis,
open: candle.candle.open,
close: candle.candle.close,
high: candle.candle.high,
low: candle.candle.low
};
};
CandleStickGraph.prototype.reset = function () {
this.clean();
this.rightCandlesOffset = 0;
this.curRightCandleOffset = 0;
this.options.zoom = 0;
};
CandleStickGraph.prototype.clean = function () {
this.candlesticks = [];
this.selectedCandleStick = [];
this.moves = [];
this.drops = [];
};
CandleStickGraph.prototype.apply = function (options) {
if (options) {
this.options = options;
}
};
CandleStickGraph.prototype.applyColors = function (options) {
this.colorInfo = options;
};
CandleStickGraph.prototype._initCanvas = function (canvas, canvasId) {
// @ts-ignore
this.canvas = canvas;
// @ts-ignore
this.width = parseInt(this.canvas.width);
// @ts-ignore
this.height = parseInt(this.canvas.height);
// @ts-ignore
this.context = this.canvas.getContext("2d");
/*
const scale = 2;
this.canvas.width = this.width * scale;
this.canvas.height = this.height * scale;
*/
// this.canvas.style.backgroundColor = "#000";
this.context.lineWidth = 1;
this.candleWidth = 5;
this.marginLeft = 10;
this.marginRight = 100;
this.marginTop = 10;
this.marginBottom = 30;
this.yStart = 0;
this.yEnd = 0;
this.yRange = 0;
this.yPixelRange = this.height - this.marginTop - this.marginBottom;
this.xStart = 0;
this.xEnd = 0;
this.xRange = 0;
this.xPixelRange = this.width - this.marginLeft - this.marginRight;
this.xGridCells = 16;
this.yGridCells = 16;
this.b_drawMouseOverlay = false;
this.mousePosition = { x: 0, y: 0 };
this.xMouseHover = 0;
this.yMouseHover = 0;
this.hoveredCandlestickID = 0;
this.context.font = CandleStickGraph.getFont(this.GENERAL_FONT_SIZE, this.options.baseFontName);
this.leftAxis = {
x1: 0,
y1: 0,
x2: 0,
y2: this.yPixelRange
};
this.rightAxis = {
x1: this.xPixelRange,
y1: 0,
x2: this.xPixelRange,
y2: this.yPixelRange
};
this.reset();
};
CandleStickGraph.prototype.initCanvasHeadless = function (canvas) {
this._initCanvas(canvas);
};
CandleStickGraph.prototype.initCanvas = function (canvas, canvasId) {
this._initCanvas(canvas);
this.bindCanvasListeners();
};
CandleStickGraph.prototype.bindCanvasListeners = function () {
var _this = this;
this.canvas.addEventListener("mousedown", function (e) {
if (_this.areSelectedCandlesNotNull()) {
_this.mouseDown(e);
}
});
this.canvas.addEventListener("mouseup", function (e) {
if (_this.areSelectedCandlesNotNull()) {
_this.mouseUp();
}
});
this.canvas.addEventListener("mousemove", function (e) {
if (_this.areSelectedCandlesNotNull()) {
_this.mouseMove(e);
_this.mouseDrag(e);
}
});
this.canvas.addEventListener("mouseout", function (e) {
if (_this.areSelectedCandlesNotNull()) {
_this.mouseOut(e);
}
});
this.canvas.addEventListener('wheel', function (e) {
if (_this.areSelectedCandlesNotNull()) {
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
_this.mouseWheel(e);
}
}, false);
};
CandleStickGraph.getFont = function (fontSize, baseFont) {
return fontSize + "px " + baseFont;
};
CandleStickGraph.prototype.areSelectedCandlesNotNull = function () {
return this.selectedCandleStick && this.selectedCandleStick.length > 0;
};
CandleStickGraph.prototype.calculateCandleWidth = function () {
var ww = (this.xPixelRange / this.selectedCandleStick.length) - 1;
if (ww % 2 === 0)
ww--;
return ww;
};
CandleStickGraph.prototype.getSelectedCandleStick = function () {
var nCandlesSkipFromLeft = Math.max(0, this.options.zoom);
var untilFromRight = Math.floor(Math.max(0, this.rightCandlesOffset + this.curRightCandleOffset));
return this.candlesticks.slice(Math.max(0, nCandlesSkipFromLeft - untilFromRight), this.candlesticks.length - untilFromRight);
};
CandleStickGraph.prototype.drawRectangleWithText = function (textX, textY, str) {
var oldFont = this.context.font;
this.context.font = CandleStickGraph.getFont((this.TEXT_FONT_SIZE + 2), this.options.baseFontName);
var textWidth = this.context.measureText(str).width;
this.fillRectRounded(textX - textWidth / 2 - 10, textY - 15, textWidth + 20, 25, this.colorInfo.whiteColor, 25 / 2);
this.context.fillStyle = this.colorInfo.blackColor;
this.context.fillText(str, textX - textWidth / 2, textY + (10) / 2);
this.context.font = oldFont;
};
CandleStickGraph.prototype.drawSmallRectangleWithText = function (textX, textY, str, bgColor, textColor) {
if (bgColor === void 0) { bgColor = this.colorInfo.whiteColor; }
if (textColor === void 0) { textColor = this.colorInfo.blackColor; }
var oldFont = this.context.font;
this.context.font = CandleStickGraph.getFont((this.GENERAL_FONT_SIZE * 1.5), this.options.baseFontName);
var textWidth = this.context.measureText(str).width;
this.fillRect(textX - textWidth - 5, textY - 15, textWidth + 10, 30, bgColor !== null && bgColor !== void 0 ? bgColor : this.colorInfo.whiteColor);
this.context.fillStyle = textColor;
this.context.fillText(str, textX - textWidth, textY + 5);
this.context.font = oldFont;
};
CandleStickGraph.prototype.drawMoves = function () {
this.getIncreaseMsg = function (tradeBuy, tradeSell) {
function twoDecimalsOf(number) {
// @ts-ignore
return number.toString().match(/^-?\d+(?:\.\d{0,2})?/)[0];
}
var from = (tradeBuy.cryptoValue);
var to = (tradeSell.cryptoValue);
var diffPercentage = twoDecimalsOf(100 * ((to - from) / (from)));
if (tradeSell.profitPercOverride) {
diffPercentage = twoDecimalsOf(tradeSell.profitPercOverride);
}
return /*twoDecimalsOf(from) + "-" + twoDecimalsOf(to) + " -> " + */ diffPercentage +
"% " + ((from > to) ? "down" : "up");
};
for (var i = 0; i < this.moves.length; i++) {
var TRIANGLE_SIZE = this.options.triangleSize;
var x = Math.min(Math.max(TRIANGLE_SIZE * 2, this.xToPixelCoords(this.moves[i].timestamp)), this.xPixelRange + TRIANGLE_SIZE * 2);
var y = Math.max(TRIANGLE_SIZE, this.yToPixelCoords(this.moves[i].cryptoValue));
if (this.moves[i].type === "buy") {
this.drawTriangleUp(x, y + TRIANGLE_SIZE, TRIANGLE_SIZE, this.colorInfo.whiteColor);
}
else {
this.drawTriangleDown(x, y - TRIANGLE_SIZE, TRIANGLE_SIZE, this.colorInfo.whiteColor);
}
if (i !== 0 && this.moves[i].type === "sell") {
var xSell = this.xToPixelCoords(this.moves[i].timestamp);
var ySell = this.yToPixelCoords(this.moves[i].cryptoValue);
var xBuy = this.xToPixelCoords(this.moves[i - 1].timestamp);
var yBuy = this.yToPixelCoords(this.moves[i - 1].cryptoValue);
this.context.setLineDash([20, 10]);
this.setLineWidth(2);
this.drawLine(xSell, ySell, xBuy, yBuy, this.colorInfo.growLineColor);
this.resetLineWidth();
this.context.setLineDash([]);
var leftIntersect = this.lineIntersect(xSell, ySell, xBuy, yBuy, this.leftAxis.x1, this.leftAxis.y1, this.leftAxis.x2, this.leftAxis.y2);
var rightIntersect = this.lineIntersect(xSell, ySell, xBuy, yBuy, this.rightAxis.x1, this.rightAxis.y1, this.rightAxis.x2, this.rightAxis.y2);
if (!!leftIntersect) {
if (typeof leftIntersect !== "boolean") {
this.drawRectangleWithText((xSell + leftIntersect.x) / 2, (ySell + leftIntersect.y) / 2, this.getIncreaseMsg(this.moves[i - 1], this.moves[i]));
}
}
else if (!!rightIntersect) {
if (typeof rightIntersect !== "boolean") {
this.drawRectangleWithText((xBuy + rightIntersect.x) / 2, (yBuy + rightIntersect.y) / 2, this.getIncreaseMsg(this.moves[i - 1], this.moves[i]));
}
}
else {
this.drawRectangleWithText((xBuy + xSell) / 2, (yBuy + ySell) / 2, this.getIncreaseMsg(this.moves[i - 1], this.moves[i]));
}
}
if (this.options.wantSideMarksMove) {
this.drawSideMarksMove(this.moves[i]);
}
}
};
CandleStickGraph.prototype.drawSideMarksMove = function (m) {
var color = m.type === 'sell' ? this.colorInfo.redColor : this.colorInfo.greenColor;
var textColor = m.type === 'sell' ? this.colorInfo.blackColor : this.colorInfo.blackColor;
// draw a rectangle with the price on the right side
var str = this.roundPriceValue(m.cryptoValue).toString();
this.drawSmallRectangleWithText(this.width, this.yToPixelCoords(m.cryptoValue), str, color, textColor);
};
CandleStickGraph.prototype.drawDrops = function () {
var _this = this;
this.drops.forEach(function (drop) {
var wl = 10;
var x1 = _this.xToPixelCoords(drop.fromTime);
var x2 = _this.xToPixelCoords(drop.toTime);
var y1 = _this.yToPixelCoords(drop.fromCrypto);
var y2 = _this.yToPixelCoords(drop.toCrypto);
_this.setLineWidth(2);
_this.drawLine(x2 - wl, y2, x2 + wl, y2, _this.colorInfo.redColor);
_this.resetLineWidth();
});
};
CandleStickGraph.prototype.drawLines = function (points, color) {
for (var i = 0; i < points.length - 1; i++) {
this.drawLine(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, color);
}
};
CandleStickGraph.prototype.drawOverAreas = function (points) {
for (var i = 0; i < points.length - 1; i++) {
if (points[i].y > 70) {
// should sell (overbought)
this.fillRect(points[i].x - Math.floor(this.candleWidth / 2), 0, this.candleWidth, this.height, this.colorInfo.redAreaColor);
}
else if (points[i].y < 30) {
// should buy (oversell)
this.fillRect(points[i].x - Math.floor(this.candleWidth / 2), 0, this.candleWidth, this.height, this.colorInfo.greenAreaColor);
}
}
};
CandleStickGraph.prototype.drawCandlesAndValuatePoints = function (i, points) {
var _a, _b;
var isRising = (this.selectedCandleStick[i].close > this.selectedCandleStick[i].open);
var color = isRising ? this.colorInfo.greenColor : this.colorInfo.redColor;
if (i === this.hoveredCandlestickID) {
if (isRising)
color = this.colorInfo.greenHoverColor;
else
color = this.colorInfo.redHoverColor;
}
var xOnGraph = this.xToPixelCoords(this.selectedCandleStick[i].timestamp);
var yTopOnGraph = this.yToPixelCoords(this.selectedCandleStick[i].open);
var yDownOnGraph = this.yToPixelCoords(this.selectedCandleStick[i].close);
var height = yDownOnGraph - yTopOnGraph;
points.push({ x: xOnGraph, y: yTopOnGraph + height });
var yLineLow = this.yToPixelCoords(this.selectedCandleStick[i].low);
var yLineHigh = this.yToPixelCoords(this.selectedCandleStick[i].high);
// draw the candle and the wick
if (this.options.wantCandles) {
if (isRising) {
this.setLineWidth((_a = this.options.lineWidthGreen) !== null && _a !== void 0 ? _a : this.options.lineWidth);
this.drawRect(xOnGraph - Math.floor(this.candleWidth / 2), yTopOnGraph, this.candleWidth, height, color);
this.drawLine(xOnGraph, yDownOnGraph, xOnGraph, yLineHigh, color);
this.drawLine(xOnGraph, yTopOnGraph, xOnGraph, yLineLow, color);
// fill rect here as well
this.fillRect(xOnGraph - Math.floor(this.candleWidth / 2), yTopOnGraph, this.candleWidth, height, color);
this.resetLineWidth();
}
else {
this.setLineWidth((_b = this.options.lineWidthRed) !== null && _b !== void 0 ? _b : this.options.lineWidth);
this.drawRect(xOnGraph - Math.floor(this.candleWidth / 2), yTopOnGraph, this.candleWidth, height, color);
this.drawLine(xOnGraph, yTopOnGraph, xOnGraph, yLineHigh, color);
this.drawLine(xOnGraph, yDownOnGraph, xOnGraph, yLineLow, color);
this.resetLineWidth();
}
}
};
CandleStickGraph.prototype.drawPriceLine = function () {
this.context.setLineDash([5, 5]);
this.drawLine(0, this.mousePosition.y, this.width, this.mousePosition.y, this.colorInfo.mouseHoverBackgroundColor);
this.context.setLineDash([]);
var str = this.roundPriceValue(this.yMouseHover);
var textWidth = this.context.measureText(String(str)).width;
this.fillRect(this.width - 70, this.mousePosition.y - 10, 70, 20, this.colorInfo.whiteColor);
this.context.fillStyle = this.colorInfo.blackColor;
this.context.fillText(String(str), this.width - textWidth - 5, this.mousePosition.y + 5);
return { str: str, textWidth: textWidth };
};
CandleStickGraph.prototype.drawTimeLine = function (str, textWidth) {
this.context.setLineDash([5, 5]);
this.drawLine(this.mousePosition.x, 0, this.mousePosition.x, this.height, this.colorInfo.mouseHoverBackgroundColor);
this.context.setLineDash([]);
str = this.formatDate(new Date(this.xMouseHover));
textWidth = this.context.measureText(str).width;
this.fillRect(this.mousePosition.x - textWidth / 2 - 5, this.height - 20, textWidth + 10, 20, this.colorInfo.whiteColor);
this.context.fillStyle = this.colorInfo.gridTextColor;
this.context.fillText(str, this.mousePosition.x - textWidth / 2, this.height - 5);
};
CandleStickGraph.prototype.drawInfoLabel = function () {
var yPos = this.mousePosition.y - 95;
if (yPos < 0)
yPos = this.mousePosition.y + 15;
this.fillRect(this.mousePosition.x + 15, yPos, 100, 80, this.colorInfo.whiteColor);
var color = (this.selectedCandleStick[this.hoveredCandlestickID].close > this.selectedCandleStick[this.hoveredCandlestickID].open) ? this.colorInfo.greenColor : this.colorInfo.redColor;
this.fillRect(this.mousePosition.x + 15, yPos, 10, 80, color);
this.context.lineWidth = 2;
this.drawRect(this.mousePosition.x + 15, yPos, 100, 80, color);
this.context.lineWidth = 1;
this.context.fillStyle = this.colorInfo.mouseHoverTextColor;
this.context.fillText("O: " + this.selectedCandleStick[this.hoveredCandlestickID].open, this.mousePosition.x + 30, yPos + 15);
this.context.fillText("C: " + this.selectedCandleStick[this.hoveredCandlestickID].close, this.mousePosition.x + 30, yPos + 35);
this.context.fillText("H: " + this.selectedCandleStick[this.hoveredCandlestickID].high, this.mousePosition.x + 30, yPos + 55);
this.context.fillText("L: " + this.selectedCandleStick[this.hoveredCandlestickID].low, this.mousePosition.x + 30, yPos + 75);
};
CandleStickGraph.prototype.addTrade = function (move) {
var goalTs = move.timestamp;
var closestOnTs = this.candlesticks.reduce(function (prev, curr) {
return (Math.abs(curr.timestamp - goalTs) < Math.abs(prev.timestamp - goalTs) ? curr : prev);
});
var cryptoValue = closestOnTs.close;
this.moves.push({
timestamp: move.timestamp,
type: move.type,
cryptoValue: move.cryptoValue ? move.cryptoValue : cryptoValue,
currencyType: move.currencyType,
profitPercOverride: move.profitPercOverride,
baseType: move.baseType
});
return this;
};
CandleStickGraph.prototype.concatCandleSticks = function (canvasCandleSticks, adaptZoom) {
var _this = this;
if (adaptZoom === void 0) { adaptZoom = false; }
canvasCandleSticks = canvasCandleSticks.concat(this.candlesticks);
var actualZoom = this.options.zoom;
this.candlesticks = [];
this.selectedCandleStick = [];
canvasCandleSticks.forEach(function (c) {
_this.addCandlestick(c, adaptZoom);
});
if (!adaptZoom) {
this.options.zoom = actualZoom;
this.selectedCandleStick = this.candlesticks.slice(Math.max(0, this.options.zoom));
}
/*
this.candlesticks.sort((a,b)=>{
return a.timestamp - b.timestamp;
})
*/
return this;
};
CandleStickGraph.prototype.fillPolygon = function (points, greenColor) {
this.context.beginPath();
this.context.fillStyle = greenColor;
this.context.moveTo(points[0].x, points[0].y);
for (var i = 0; i < points.length; i++)
this.context.lineTo(points[i].x, points[i].y);
this.context.lineTo(points[0].x, points[0].y);
this.context.closePath();
this.context.fill();
};
// MATH UTILS
CandleStickGraph.prototype.lineIntersect = function (x1, y1, x2, y2, x3, y3, x4, y4) {
//console.log("line interpolation : " + x3 + ','+ y3 + '-'+ x4 + ','+ y4);
// Check if none of the lines are of length 0
if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) {
return false;
}
var denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
// Lines are parallel
if (denominator === 0) {
return false;
}
var ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
var ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
// is the intersection along the segments
if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
return false;
}
// Return object with the x and y coordinates of the intersection
var x = x1 + ua * (x2 - x1);
var y = y1 + ua * (y2 - y1);
return { x: x, y: y };
};
CandleStickGraph.prototype.cleanCandleSticks = function () {
this.selectedCandleStick = [];
};
CandleStickGraph.prototype.setLineWidth = function (number) {
this.context.lineWidth = number;
};
CandleStickGraph.prototype.resetLineWidth = function () {
this.setLineWidth(1);
};
CandleStickGraph.prototype.calculateSma = function (period) {
var _this = this;
if (period === void 0) { period = 26; }
var values = this.candlesticks;
var valuesY = this.candlesticks.map(function (c) { return c.close; });
var indicators = technicalindicators_1.SMA.calculate({ period: period, values: valuesY });
var newCandleSticks = values.map(function (el, i) {
return __assign(__assign({}, el), { close: (i > (valuesY.length - indicators.length)) ? indicators[i - period] : el.close });
});
return newCandleSticks.map(function (c) {
var xOnGraph = _this.xToPixelCoords(c.timestamp);
var yTopOnGraph = _this.yToPixelCoords(c.open);
var yDownOnGraph = _this.yToPixelCoords(c.close);
var height = yDownOnGraph - yTopOnGraph;
return { x: xOnGraph, y: yTopOnGraph + height };
});
};
CandleStickGraph.prototype.calculateEma = function (period) {
var _this = this;
if (period === void 0) { period = 26; }
var values = this.candlesticks;
var valuesY = this.candlesticks.map(function (c) { return c.close; });
var indicators = technicalindicators_1.EMA.calculate({ period: period, values: valuesY });
var newCandleSticks = values.map(function (el, i) {
return __assign(__assign({}, el), { close: (i > (valuesY.length - indicators.length)) ? indicators[i - period] : el.close });
});
return newCandleSticks.map(function (c) {
var xOnGraph = _this.xToPixelCoords(c.timestamp);
var yTopOnGraph = _this.yToPixelCoords(c.open);
var yDownOnGraph = _this.yToPixelCoords(c.close);
var height = yDownOnGraph - yTopOnGraph;
return { x: xOnGraph, y: yTopOnGraph + height };
});
};
//FIXME: trix is too low valued to be displayed
CandleStickGraph.prototype.calculateTrix = function () {
var _this = this;
var period = 30;
var values = this.candlesticks;
var valuesY = this.candlesticks.map(function (c) { return c.close; });
var indicators = technicalindicators_1.TRIX.calculate({ period: period, values: valuesY });
var newCandleSticks = values.map(function (el, i) {
return __assign(__assign({}, el), { close: (i > (valuesY.length - indicators.length)) ? indicators[i - period] : el.close });
});
return newCandleSticks.map(function (c) {
var xOnGraph = _this.xToPixelCoords(c.timestamp);
var yTopOnGraph = _this.yToPixelCoords(c.open);
var yDownOnGraph = _this.yToPixelCoords(c.close);
var height = yDownOnGraph - yTopOnGraph;
return { x: xOnGraph, y: yTopOnGraph + height };
});
};
CandleStickGraph.prototype.calculateRsi = function () {
var _this = this;
var period = 8;
var values = this.candlesticks;
var valuesY = this.candlesticks.map(function (c) { return c.close; });
var indicators = technicalindicators_1.RSI.calculate({ period: period, values: valuesY });
var newCandleSticks = values.map(function (el, i) {
return __assign(__assign({}, el), { close: (i > (valuesY.length - indicators.length)) ? indicators[i - period] : el.close });
});
return newCandleSticks.map(function (c) {
var xOnGraph = _this.xToPixelCoords(c.timestamp);
return { x: xOnGraph, y: c.close };
});
};
CandleStickGraph.prototype.drawMACD = function (emaPoints1, emaPoints2) {
this.fillPolygon(emaPoints1.concat(emaPoints2.reverse()), this.colorInfo.whiteColorMoreTrasparent);
emaPoints2.reverse();
for (var i = 0; i < Math.min(emaPoints1.length, emaPoints2.length); i++) {
var h = (emaPoints1[i].y - emaPoints2[i].y);
var colorMACD = h > 0 ? this.colorInfo.greenAreaColorIntens : this.colorInfo.redAreaColorIntes;
this.fillRect(emaPoints1[i].x, emaPoints1[i].y, this.candleWidth, -h, colorMACD);
}
};
CandleStickGraph.prototype.calculateBollingerBands = function (period) {
var _this = this;
var values = this.candlesticks;
var valuesY = this.candlesticks.map(function (c) { return c.close; });
var indicators = technicalindicators_1.BollingerBands.calculate({ period: period, values: valuesY, stdDev: 2 });
var newCandleSticks = values.map(function (el, i) {
return __assign(__assign({}, el), { open: (i > (valuesY.length - indicators.length)) ? indicators[i - period].upper : el.open, close: (i > (valuesY.length - indicators.length)) ? indicators[i - period].lower : el.close });
});
return newCandleSticks.map(function (c) {
var xOnGraph = _this.xToPixelCoords(c.timestamp);
return {
x: xOnGraph,
y: c.open,
y2: c.close
};
});
};
CandleStickGraph.prototype.drawBollingRangeArea = function (bollingerBandsRanges, color) {
var _this = this;
this.drawLines(bollingerBandsRanges.map(function (el) {
var yTopOnGraph = _this.yToPixelCoords(el.y);
return {
x: el.x,
y: yTopOnGraph
};
}), color);
this.drawLines(bollingerBandsRanges.map(function (el) {
var yTopOnGraph = _this.yToPixelCoords(el.y2);
return {
x: el.x,
y: yTopOnGraph
};
}), color);
var polygonPoints = bollingerBandsRanges
.map(function (el) {
var yTopOnGraph = _this.yToPixelCoords(el.y);
return {
x: el.x,
y: yTopOnGraph
};
})
.concat(bollingerBandsRanges
.map(function (el) {
var yTopOnGraph = _this.yToPixelCoords(el.y2);
return {
x: el.x,
y: yTopOnGraph
};
}).reverse());
this.fillPolygon(polygonPoints, color);
};
CandleStickGraph.prototype.drawStatsLabel = function () {
var _a;
var preFont = this.context.font;
var preColor = this.context.fillStyle;
var paddingLeft = 80;
var paddingTop = 60;
var textSize = (40);
this.context.font = CandleStickGraph.getFont(textSize, this.options.baseFontName);
this.context.fillStyle = this.colorInfo.whiteColor;
var baseType = (_a = this.moves[0].baseType) !== null && _a !== void 0 ? _a : 'USD';
this.context.fillText("".concat(this.moves[0].currencyType, "/").concat(baseType), 10 + paddingLeft, textSize);
this.context.fillStyle = this.colorInfo.gridTextColor;
this.context.font = CandleStickGraph.getFont(textSize / 2, this.options.baseFontName);
var day = new Date(this.candlesticks[Math.floor(this.candlesticks.length / 2)].timestamp);
this.context.fillText(this.formatDateNoHours(day), 10 + paddingLeft, textSize + textSize / 2);
this.context.fillStyle = preColor;
this.context.font = preFont;
};
CandleStickGraph.prototype.drawLogo = function () {
var _this = this;
var _a;
var SVGIcons = {
"logo_only_wave_black.svg": {
draw: function (ctx) {
ctx.save();
ctx.strokeStyle = "rgba(0,0,0,0)";
ctx.miterLimit = 4;
ctx.font = "15px ''";
ctx.font = " 15px ''";
ctx.translate(0.5540540540540562, 0);
ctx.scale(0.5675675675675675, 0.5675675675675675);
ctx.save();
ctx.fillStyle = _this.colorInfo.logoColor;
ctx.strokeStyle = "rgba(0,0,0,0)";
ctx.font = " 15px ''";
ctx.beginPath();
ctx.moveTo(3.468657, 112);
ctx.bezierCurveTo(4.020682, 110.817299, 4.825206, 109.280014, 6.094991, 108.505936);
ctx.bezierCurveTo(21.296396, 99.238838, 34.968487, 88.210617, 44.995117, 73.342888);
ctx.bezierCurveTo(50.24588, 65.556938, 53.777756, 56.854958, 53.971645, 47.214466);
ctx.bezierCurveTo(54.098431, 40.910656, 49.403709, 36.394333, 43.021671, 35.966431);
ctx.bezierCurveTo(32.913063, 35.288681, 23.390165, 37.276749, 13.563206, 42.203903);
ctx.bezierCurveTo(16.78842, 31.64435, 20.66403, 22.60828, 28.800495, 15.462938);
ctx.bezierCurveTo(35.724667, 9.382215, 43.249119, 5.420638, 52.264214, 3.928263);
ctx.bezierCurveTo(61.730152, 2.361255, 71.043755, 2.495655, 80.439186, 4.763796);
ctx.bezierCurveTo(95.638657, 8.433086, 108.042816, 16.535461, 118.391975, 27.9454);
ctx.bezierCurveTo(127.008179, 37.444775, 132.411224, 48.666054, 134.99324, 61.223808);
ctx.bezierCurveTo(138.522568, 78.388939, 135.801132, 94.988892, 130.683777, 111.666382);
ctx.bezierCurveTo(88.645775, 112, 46.291542, 112, 3.468657, 112);
ctx.moveTo(35.521973, 17.985134);
ctx.bezierCurveTo(31.913422, 22.007553, 28.304873, 26.029974, 24.611584, 30.146849);
ctx.bezierCurveTo(31.886415, 29.730671, 38.266277, 28.570263, 44.471703, 29.181328);
ctx.bezierCurveTo(56.280746, 30.344191, 61.625313, 38.039322, 60.023434, 49.94173);
ctx.bezierCurveTo(59.939613, 50.564533, 60.156376, 51.227787, 60.445763, 53.655109);
ctx.bezierCurveTo(63.584568, 50.26371, 65.973213, 47.883102, 68.131683, 45.309452);
ctx.bezierCurveTo(72.790947, 39.753975, 76.516441, 40.059505, 80.033485, 46.463547);
ctx.bezierCurveTo(81.581734, 49.282703, 82.759308, 52.355904, 84.644165, 54.91711);
ctx.bezierCurveTo(85.506744, 56.089203, 87.645317, 56.620827, 89.262398, 56.755348);
ctx.bezierCurveTo(89.741348, 56.795193, 90.919563, 54.485046, 90.90345, 53.263275);
ctx.bezierCurveTo(90.858521, 49.856037, 89.930351, 46.42281, 90.183434, 43.06255);
ctx.bezierCurveTo(90.351166, 40.835518, 91.389603, 37.979366, 93.041145, 36.719131);
ctx.bezierCurveTo(95.616463, 34.753994, 98.297409, 36.349041, 99.967766, 38.931087);
ctx.bezierCurveTo(102.061996, 42.168354, 103.658569, 45.787243, 106.09317, 48.724934);
ctx.bezierCurveTo(107.897293, 50.901863, 110.776886, 53.75631, 113.032768, 53.634323);
ctx.bezierCurveTo(116.71669, 53.435123, 117.701836, 49.508595, 118.018204, 45.958389);
ctx.bezierCurveTo(118.623749, 39.16328, 115.103226, 33.990963, 110.868752, 29.535463);