Files
Superalgos/ChartLayers/OrderBooksChartLayer.js
T

601 lines
18 KiB
JavaScript

function newOrderBooksChartLayer() {
var orderBooks = [];
let timePeriod = INITIAL_TIME_PERIOD;
var plotArea = newPlotArea();
var datetime = INITIAL_DATE;
var orderBooksChartLayer = {
onLayerStatusChanged: onLayerStatusChanged,
setTimePeriod: setTimePeriod,
setDatetime: setDatetime,
container: undefined,
draw: draw,
getContainer: getContainer,
initialize: initialize
};
var container = newContainer();
container.initialize();
orderBooksChartLayer.container = container;
container.displacement.containerName = "Order Books Chart Layer";
container.zoom.containerName = "Order Books Chart Layer";
container.frame.containerName = "Order Books Chart Layer";
let marketId;
let exchangeId;
let marketIndex;
let orderBookFilesCursor;
let amountScale;
let maxAmount = 0;
const TIME_FOR_RESOLUTION_CHANGE = 10;
const MAX_RESOLUTION = 1;
const MIN_RESOLUTION = 10;
let resolution = MIN_RESOLUTION;
let timeWithoutZoomOrPane = 0;
const RECALCULATE_COUNTER_MAX_VALUE = 100;
let recalculateCounter = 0;
let layerStatus = 'off';
return orderBooksChartLayer;
function initialize(exchange, market, index, botsPanel) {
marketId = market;
exchangeId = exchange;
marketIndex = index;
orderBookFilesCursor = newOrderBookFilesCursor();
orderBookFilesCursor.initialize(exchangeId, marketId);
recalculateScale();
viewPort.eventHandler.listenToEvent("Zoom Changed", onZoomChanged);
canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
canvas.eventHandler.listenToEvent("Dragging", onDragging);
layerStatus = botsPanel.getProductStatus(botsPanel.layerNames.ORDER_BOOKS);
}
function getContainer(point) {
var container;
/* First we check if this point is inside this space. */
if (this.container.frame.isThisPointHere(point) === true) {
return this.container;
} else {
/* This point does not belong to this space. */
return undefined;
}
}
function onLayerStatusChanged(eventData) {
if (eventData.layer === 'Order Books') {
layerStatus = eventData.status;
}
if (layerStatus !== 'on') { return; }
if (timePeriod <= _1_HOUR_IN_MILISECONDS) {
orderBookFilesCursor.setDatetime(datetime);
}
}
function onZoomChanged(event) {
if (timePeriod <= _1_HOUR_IN_MILISECONDS) {
timeWithoutZoomOrPane = 0;
resolution++;
if (resolution > MIN_RESOLUTION) {
resolution = MIN_RESOLUTION;
}
recalculateCounter = recalculateCounter + RECALCULATE_COUNTER_MAX_VALUE / 10;
}
/* Defining when the file cursor is updated */
if (layerStatus === 'off') { return; }
if (timePeriod <= _1_HOUR_IN_MILISECONDS) {
orderBookFilesCursor.setDatetime(datetime);
}
}
function onDragFinished() {
if (timePeriod <= _1_HOUR_IN_MILISECONDS) {
recalculateOrderBooks();
recalculateCounter = 0;
}
/* Defining when the file cursor is updated */
if (layerStatus === 'off') { return; }
if (timePeriod <= _1_HOUR_IN_MILISECONDS) {
orderBookFilesCursor.setDatetime(datetime);
}
}
function onDragging() {
recalculateCounter = 0;
}
function setTimePeriod(period) {
timePeriod = period;
}
function setDatetime(newDatetime) {
datetime = newDatetime;
}
function draw() {
if (layerStatus !== 'on') { return; }
if (timePeriod <= _1_HOUR_IN_MILISECONDS) {
timeWithoutZoomOrPane++;
if (timeWithoutZoomOrPane > TIME_FOR_RESOLUTION_CHANGE) {
resolution--;
timeWithoutZoomOrPane = 0;
if (resolution < MAX_RESOLUTION) {
resolution = MAX_RESOLUTION;
}
//recalculateOrderBooks();
}
this.container.frame.draw();
recalculateCounter++;
if (recalculateCounter > RECALCULATE_COUNTER_MAX_VALUE) {
recalculateOrderBooks();
recalculateCounter = 0;
}
plotOrderBooksChart();
}
}
function recalculateOrderBooks() {
if (layerStatus === 'off') { return; }
/* get the bounderies */
let rateAtTop = getRateFromPoint(viewPort.visibleArea.topLeft, orderBooksChartLayer.container, plotArea);
let rateAtBottom = getRateFromPoint(viewPort.visibleArea.bottomLeft, orderBooksChartLayer.container, plotArea);
let maxPossibleRate = rateAtTop + (rateAtTop - rateAtBottom);
let minPossibleRate = rateAtBottom - (rateAtTop - rateAtBottom);
let timeAtLeft = getMilisecondsFromPoint(viewPort.visibleArea.topLeft, orderBooksChartLayer.container, plotArea);
let timeAtRight = getMilisecondsFromPoint(viewPort.visibleArea.bottomRight, orderBooksChartLayer.container, plotArea);
let maxPossibleTime = timeAtRight + (timeAtRight - timeAtLeft);
let minPossibleTime = timeAtLeft - (timeAtRight - timeAtLeft);
/* continue with the files */
let minutesOnSides = 1440 * 5;
let leftDateTime;
let rightDateTime;
leftDateTime = new Date(datetime.getTime());
leftDateTime.setMinutes(leftDateTime.getMinutes() - minutesOnSides);
let absoluteLeft = Math.trunc(leftDateTime.valueOf() / timePeriod) * timePeriod;
leftDateTime = new Date(absoluteLeft);
rightDateTime = new Date(leftDateTime.getTime());
rightDateTime.setMinutes(rightDateTime.getMinutes() + minutesOnSides * 2 );
let currentDate = new Date(leftDateTime.getTime());
orderBooks = [];
/* Lets calculate the current datetime with minutes precision */
let currentDatetime = new Date(datetime.getTime());
let currentMoment = Math.trunc(currentDatetime.valueOf() / _1_MINUTE_IN_MILISECONDS) * _1_MINUTE_IN_MILISECONDS;
/* Lets calculate the precision */
let presicion = calculatePresicion(marketIndex.maxRate());
function calculatePresicion(number) {
for (let i = -10; i <= 10; i++) {
if (number < Math.pow(10, i)) {
return Math.pow(10, i - 3);
}
}
}
let orderBook = new Map();
const fileTimePeriod = 60 * 1000;
const recordsPerCandle = timePeriod / fileTimePeriod * resolution;
let currentRecord = 0;
while (currentDate.getTime() < rightDateTime.getTime()) {
currentRecord++;
let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2) + '-' + pad(currentDate.getHours(), 2) + '-' + pad(currentDate.getMinutes(), 2);
let orderBookFile = orderBookFilesCursor.orderBookFiles.get(stringDate);
if (orderBookFile !== undefined) {
if (orderBookFile.length > 0) {
let condensedMap;
condensedMap = condenceArray(orderBookFile[0], currentDate.valueOf());
for (let key of condensedMap.keys()) {
aggregate('ask', [key, condensedMap.get(key)]);
}
condensedMap = condenceArray(orderBookFile[1], currentDate.valueOf());
for (let key of condensedMap.keys()) {
aggregate('bid', [key, condensedMap.get(key)]);
}
function aggregate(type, fileRecord) {
let rate = fileRecord[0];
if (rate >= plotArea.max.y) { return; }
if (rate <= plotArea.min.y) { return; }
let amount = fileRecord[1];
if (type === 'bid' && amount > maxAmount) { maxAmount = amount; }
let orderBookPosition = orderBook.get(type + rate) || newResistance();
if (orderBookPosition.type === undefined) {
orderBookPosition.type = type;
orderBookPosition.rate = rate;
orderBookPosition.open = amount;
orderBookPosition.close = amount;
orderBookPosition.min = amount;
orderBookPosition.max = amount;
orderBookPosition.begin = currentDate.valueOf();
orderBookPosition.end = currentDate.valueOf() + fileTimePeriod;
} else {
if (amount < orderBookPosition.min) { orderBookPosition.min = amount; }
if (amount > orderBookPosition.max) { orderBookPosition.max = amount; }
orderBookPosition.end = currentDate.valueOf() + fileTimePeriod;
orderBookPosition.close = amount;
}
orderBook.set(type + rate, orderBookPosition);
}
}
}
currentDate.setMinutes(currentDate.getMinutes() + 1);
if (currentRecord === recordsPerCandle) {
orderBooks.push(orderBook);
/* Lets see now if this order book is the one corresponding to the current moment in time */
let fisrtPosition = orderBook.values().next().value;
if (fisrtPosition !== undefined) {
if (currentMoment >= fisrtPosition.begin && currentMoment < fisrtPosition.end) {
orderBooksChartLayer.container.eventHandler.raiseEvent("Current Order Book Changed", orderBook);
}
}
/* Reset map and counter */
orderBook = new Map();
currentRecord = 0;
}
}
/* Now the amount scale */
amountScale = presicion / maxAmount * 10;
function condenceArray(arrayToCondence, currentDatetime) {
let condencedMap = new Map();
let condenseFactor = getCondenseFactor(timePeriod);
for (var i = 0; i < arrayToCondence.length; i++) {
let fileRate = arrayToCondence[i][0];
let rate = Math.round(fileRate / presicion / condenseFactor) * presicion * condenseFactor;
let amount = arrayToCondence[i][1];
let storedAmount = condencedMap.get(rate) || 0;
amount = amount + storedAmount;
if (rate < maxPossibleRate && rate > minPossibleRate && currentDatetime > minPossibleTime && currentDatetime < maxPossibleTime ) {
condencedMap.set(rate, amount);
}
}
return condencedMap;
}
}
function recalculateScale() {
var minValue = {
x: EARLIEST_DATE.valueOf(),
y: 0
};
var maxValue = {
x: MAX_PLOTABLE_DATE.valueOf(),
y: nextPorwerOf10(marketIndex.maxRate())
};
plotArea.initialize(
minValue,
maxValue,
orderBooksChartLayer.container.frame.width,
orderBooksChartLayer.container.frame.height
);
}
function plotOrderBooksChart() {
let transparencyLevel = getTransparenceFactor(timePeriod);
/* Now we calculate and plot the orderBooks */
for (let i = 0; i < orderBooks.length; i++) {
let orderBooksMap = orderBooks[i];
for (let key of orderBooksMap.keys()) {
let orderBookPosition = orderBooksMap.get(key);
let resistancePoint1 = {
x: orderBookPosition.begin - (orderBookPosition.end - orderBookPosition.begin) * 2,
y: orderBookPosition.rate
};
let resistancePoint2 = {
x: orderBookPosition.end + (orderBookPosition.end - orderBookPosition.begin) * 2,
y: orderBookPosition.rate
};
let resistancePoint3 = {
x: orderBookPosition.end,
y: orderBookPosition.rate + orderBookPosition.close * amountScale
};
let resistancePoint4 = {
x: orderBookPosition.begin,
y: orderBookPosition.rate + orderBookPosition.open * amountScale
};
let resistancePoint5 = {
x: orderBookPosition.end,
y: orderBookPosition.rate - orderBookPosition.close * amountScale
};
let resistancePoint6 = {
x: orderBookPosition.begin,
y: orderBookPosition.rate - orderBookPosition.open * amountScale
};
resistancePoint1 = plotArea.inverseTransform(resistancePoint1, orderBooksChartLayer.container.frame.height);
resistancePoint2 = plotArea.inverseTransform(resistancePoint2, orderBooksChartLayer.container.frame.height);
resistancePoint3 = plotArea.inverseTransform(resistancePoint3, orderBooksChartLayer.container.frame.height);
resistancePoint4 = plotArea.inverseTransform(resistancePoint4, orderBooksChartLayer.container.frame.height);
resistancePoint5 = plotArea.inverseTransform(resistancePoint5, orderBooksChartLayer.container.frame.height);
resistancePoint6 = plotArea.inverseTransform(resistancePoint6, orderBooksChartLayer.container.frame.height);
resistancePoint1 = transformThisPoint(resistancePoint1, orderBooksChartLayer.container);
resistancePoint2 = transformThisPoint(resistancePoint2, orderBooksChartLayer.container);
resistancePoint3 = transformThisPoint(resistancePoint3, orderBooksChartLayer.container);
resistancePoint4 = transformThisPoint(resistancePoint4, orderBooksChartLayer.container);
resistancePoint5 = transformThisPoint(resistancePoint5, orderBooksChartLayer.container);
resistancePoint6 = transformThisPoint(resistancePoint6, orderBooksChartLayer.container);
if (resistancePoint2.x < viewPort.visibleArea.bottomLeft.x || resistancePoint1.x > viewPort.visibleArea.bottomRight.x) {
continue;
}
if (resistancePoint6.y < viewPort.visibleArea.topLeft.y || resistancePoint4.y > viewPort.visibleArea.bottomRight.y) {
continue;
}
resistancePoint1 = viewPort.fitIntoVisibleArea(resistancePoint1);
resistancePoint2 = viewPort.fitIntoVisibleArea(resistancePoint2);
resistancePoint3 = viewPort.fitIntoVisibleArea(resistancePoint3);
resistancePoint4 = viewPort.fitIntoVisibleArea(resistancePoint4);
resistancePoint5 = viewPort.fitIntoVisibleArea(resistancePoint5);
resistancePoint6 = viewPort.fitIntoVisibleArea(resistancePoint6);
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(resistancePoint1.x, resistancePoint1.y);
browserCanvasContext.lineTo(resistancePoint2.x, resistancePoint2.y);
browserCanvasContext.lineTo(resistancePoint3.x, resistancePoint3.y);
//browserCanvasContext.lineTo(resistancePoint6.x, resistancePoint6.y);
browserCanvasContext.lineTo(resistancePoint4.x, resistancePoint4.y);
browserCanvasContext.closePath();
if (orderBookPosition.type === 'bid') {
browserCanvasContext.fillStyle = 'rgba(61, 135, 235, ' + transparencyLevel + ')';
browserCanvasContext.strokeStyle = 'rgba(51, 112, 211, 0.20)';
}
if (orderBookPosition.type === 'ask') {
browserCanvasContext.fillStyle = 'rgba(60, 233, 252, ' + transparencyLevel + ')';
browserCanvasContext.strokeStyle = 'rgba(8, 86, 94, ' + transparencyLevel + ')';
}
if (datetime !== undefined) {
let dateValue = datetime.valueOf();
if (dateValue >= orderBookPosition.begin && dateValue < orderBookPosition.end) {
browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + transparencyLevel + ')'; // Current orderBookPosition accroding to time
}
}
if (
resistancePoint1.x < viewPort.visibleArea.topLeft.x + 50
||
resistancePoint1.x > viewPort.visibleArea.bottomRight.x - 50
) {
// we leave this orderBooks without fill.
} else {
browserCanvasContext.fill();
}
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(resistancePoint1.x, resistancePoint1.y);
browserCanvasContext.lineTo(resistancePoint2.x, resistancePoint2.y);
browserCanvasContext.lineTo(resistancePoint5.x, resistancePoint5.y);
browserCanvasContext.lineTo(resistancePoint6.x, resistancePoint6.y);
browserCanvasContext.closePath();
if (
resistancePoint1.x < viewPort.visibleArea.topLeft.x + 50
||
resistancePoint1.x > viewPort.visibleArea.bottomRight.x - 50
) {
// we leave this orderBooks without fill.
} else {
browserCanvasContext.fill();
}
browserCanvasContext.stroke();
}
}
}
}