Merge remote-tracking branch 'PlotterTrading/develop' into develop
This commit is contained in:
commit
594ed79a08
12 files changed
+1806
No files matched your search
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
|
||||
.vs/*
|
||||
obj/*
|
||||
|
||||
bin/*
|
||||
|
||||
*.config
|
||||
*.log
|
||||
*.cache
|
||||
node_modules/*
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
+727
@@ -0,0 +1,727 @@
|
||||
function newAAMastersPlottersTradingDetails() {
|
||||
|
||||
let thisObject = {
|
||||
|
||||
// Main functions and properties.
|
||||
|
||||
initialize: initialize,
|
||||
container: undefined,
|
||||
getContainer: getContainer,
|
||||
setTimePeriod: setTimePeriod,
|
||||
setDatetime: setDatetime,
|
||||
draw: draw
|
||||
};
|
||||
|
||||
/* this is part of the module template */
|
||||
|
||||
let container = newContainer(); // Do not touch this 3 lines, they are just needed.
|
||||
container.initialize();
|
||||
thisObject.container = container;
|
||||
|
||||
let timeLineCoordinateSystem = newTimeLineCoordinateSystem(); // Needed to be able to plot on the timeline, otherwise not.
|
||||
let timeLineCoordinateSystemFrame = newTimeLineCoordinateSystem(); // This chart uses this extra object.
|
||||
|
||||
let timePeriod; // This will hold the current Time Period the user is at.
|
||||
let datetime; // This will hold the current Datetime the user is at.
|
||||
|
||||
let marketFile; // This is the current Market File being plotted.
|
||||
let fileCursor; // This is the current File Cursor being used to retrieve Daily Files.
|
||||
|
||||
let fileCache; // This object will provide the different Market Files at different Time Periods.
|
||||
let fileCursorCache; // This object will provide the different File Cursors at different Time Periods.
|
||||
|
||||
/* these are module specific variables: */
|
||||
|
||||
let volumes = []; // Here we keep the volumes to be ploted every time the Draw() function is called by the AAWebPlatform.
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize(pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) {
|
||||
|
||||
let cursorCacheInProgress = false;
|
||||
let finaleStepsInProgress = false;
|
||||
|
||||
datetime = pDatetime;
|
||||
timePeriod = pTimePeriod;
|
||||
|
||||
fileCache = newFileCache();
|
||||
fileCache.initialize("AAMasters", "AAOlivia", "Market Volumes", "Market Volumes", pExchange, pMarket, onFileReady);
|
||||
|
||||
function onFileReady() {
|
||||
|
||||
let newMarketFile = fileCache.getFile(ONE_DAY_IN_MILISECONDS);
|
||||
|
||||
if (newMarketFile !== undefined) {
|
||||
|
||||
marketFile = newMarketFile;
|
||||
|
||||
initializeFileCursorCache();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFileCursorCache() {
|
||||
|
||||
if (cursorCacheInProgress === false) {
|
||||
|
||||
cursorCacheInProgress = true;
|
||||
|
||||
fileCursorCache = newFileCursorCache();
|
||||
fileCursorCache.initialize("AAMasters", "AAOlivia", "Daily Volumes", "Daily Volumes", pExchange, pMarket, pDatetime, pTimePeriod, onFileCursorReady);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function onFileCursorReady() {
|
||||
|
||||
recalculate();
|
||||
|
||||
let newFileCursor = fileCursorCache.getFileCursor(pTimePeriod);
|
||||
|
||||
if (newFileCursor !== undefined) { // if the file ready is the one we need then it and we dont have it yet, then we will continue here.
|
||||
|
||||
let stringDate = datetime.getUTCFullYear() + '-' + pad(datetime.getUTCMonth() + 1, 2) + '-' + pad(datetime.getUTCDate(), 2);
|
||||
|
||||
let dailyFile = newFileCursor.files.get(stringDate);
|
||||
|
||||
if (dailyFile !== undefined) {
|
||||
|
||||
if (finaleStepsInProgress === false) {
|
||||
|
||||
finaleStepsInProgress = true;
|
||||
|
||||
fileCursor = newFileCursor;
|
||||
|
||||
recalculateScaleX();
|
||||
recalculate();
|
||||
recalculateScaleY();
|
||||
|
||||
viewPort.eventHandler.listenToEvent("Zoom Changed", onZoomChanged);
|
||||
canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
|
||||
|
||||
callBackFunction();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
|
||||
let 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 setTimePeriod(pTimePeriod) {
|
||||
|
||||
timePeriod = pTimePeriod;
|
||||
|
||||
if (timePeriod >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
let newMarketFile = fileCache.getFile(pTimePeriod);
|
||||
|
||||
if (newMarketFile !== undefined) {
|
||||
|
||||
marketFile = newMarketFile;
|
||||
|
||||
recalculate();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fileCursorCache.setTimePeriod(pTimePeriod);
|
||||
|
||||
fileCursorCache.setDatetime(datetime);
|
||||
|
||||
let newFileCursor = fileCursorCache.getFileCursor(pTimePeriod);
|
||||
|
||||
if (newFileCursor !== undefined) {
|
||||
|
||||
fileCursor = newFileCursor;
|
||||
|
||||
recalculate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (timePeriod === _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
fileCursorCache.setTimePeriod(pTimePeriod);
|
||||
|
||||
fileCursorCache.setDatetime(datetime);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function setDatetime(pDatetime) {
|
||||
|
||||
datetime = pDatetime;
|
||||
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
this.container.frame.draw();
|
||||
|
||||
if (timePeriod < _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
if (Math.random() * 1000 > 995) {
|
||||
|
||||
recalculateScaleX();
|
||||
recalculate();
|
||||
recalculateScaleY();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
plotChart();
|
||||
|
||||
}
|
||||
|
||||
function recalculate() {
|
||||
|
||||
if (timePeriod >= _1_HOUR_IN_MILISECONDS) {
|
||||
|
||||
recalculateUsingMarketFiles();
|
||||
|
||||
} else {
|
||||
|
||||
recalculateUsingDailyFiles();
|
||||
|
||||
}
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Volumes Changed", volumes);
|
||||
}
|
||||
|
||||
function recalculateUsingDailyFiles() {
|
||||
|
||||
if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor
|
||||
|
||||
let daysOnSides = getSideDays(timePeriod);
|
||||
|
||||
let leftDate = getDateFromPoint(viewPort.visibleArea.topLeft, thisObject.container, timeLineCoordinateSystem);
|
||||
let rightDate = getDateFromPoint(viewPort.visibleArea.topRight, thisObject.container, timeLineCoordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
let currentDate = new Date(farLeftDate.valueOf());
|
||||
|
||||
volumes = [];
|
||||
|
||||
while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) {
|
||||
|
||||
let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2);
|
||||
|
||||
let dailyFile = fileCursor.files.get(stringDate);
|
||||
|
||||
if (dailyFile !== undefined) {
|
||||
|
||||
for (let i = 0; i < dailyFile.length; i++) {
|
||||
|
||||
let volume = {
|
||||
amountBuy: 0,
|
||||
amountSell: 0,
|
||||
begin: undefined,
|
||||
end: undefined
|
||||
};
|
||||
|
||||
volume.amountBuy = dailyFile[i][0];
|
||||
volume.amountSell = dailyFile[i][1];
|
||||
|
||||
volume.begin = dailyFile[i][2];
|
||||
volume.end = dailyFile[i][3];
|
||||
|
||||
if (volume.begin >= farLeftDate.valueOf() && volume.end <= farRightDate.valueOf()) {
|
||||
|
||||
volumes.push(volume);
|
||||
|
||||
if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) {
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Current Candle Changed", thisObject.currentCandle);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
|
||||
}
|
||||
|
||||
/* Lests check if all the visible screen is going to be covered by volumes. */
|
||||
|
||||
let lowerEnd = leftDate.valueOf();
|
||||
let upperEnd = rightDate.valueOf();
|
||||
|
||||
if (volumes.length > 0) {
|
||||
|
||||
if (volumes[0].begin > lowerEnd || volumes[volumes.length - 1].end < upperEnd) {
|
||||
|
||||
setTimeout(recalculate, 2000);
|
||||
|
||||
//console.log("File missing while calculating volumes, scheduling a recalculation in 2 seconds.");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("Olivia > recalculateUsingDailyFiles > total volumes generated : " + volumes.length);
|
||||
|
||||
}
|
||||
|
||||
function recalculateUsingMarketFiles() {
|
||||
|
||||
if (marketFile === undefined) { return; } // Initialization not complete yet.
|
||||
|
||||
let daysOnSides = getSideDays(timePeriod);
|
||||
|
||||
let leftDate = getDateFromPoint(viewPort.visibleArea.topLeft, thisObject.container, timeLineCoordinateSystem);
|
||||
let rightDate = getDateFromPoint(viewPort.visibleArea.topRight, thisObject.container, timeLineCoordinateSystem);
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
|
||||
|
||||
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
|
||||
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
|
||||
|
||||
volumes = [];
|
||||
|
||||
for (let i = 0; i < marketFile.length; i++) {
|
||||
|
||||
let volume = {
|
||||
amountBuy: 0,
|
||||
amountSell: 0,
|
||||
begin: undefined,
|
||||
end: undefined
|
||||
};
|
||||
|
||||
volume.amountBuy = marketFile[i][0];
|
||||
volume.amountSell = marketFile[i][1];
|
||||
|
||||
volume.begin = marketFile[i][2];
|
||||
volume.end = marketFile[i][3];
|
||||
|
||||
if (volume.begin >= leftDate.valueOf() && volume.end <= rightDate.valueOf()) {
|
||||
|
||||
volumes.push(volume);
|
||||
|
||||
if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) {
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Current Volume Changed", thisObject.currentCandle);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("Olivia > recalculateUsingMarketFiles > total volumes generated : " + volumes.length);
|
||||
}
|
||||
|
||||
function recalculateScaleX() {
|
||||
|
||||
|
||||
var minValue = {
|
||||
x: MIN_PLOTABLE_DATE.valueOf()
|
||||
};
|
||||
|
||||
var maxValue = {
|
||||
x: MAX_PLOTABLE_DATE.valueOf()
|
||||
};
|
||||
|
||||
timeLineCoordinateSystem.initializeX(
|
||||
minValue,
|
||||
maxValue,
|
||||
thisObject.container.frame.width
|
||||
);
|
||||
|
||||
timeLineCoordinateSystemFrame.initializeX(
|
||||
minValue,
|
||||
maxValue,
|
||||
thisObject.container.frame.width
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function recalculateScaleY() {
|
||||
|
||||
var minValue = {
|
||||
y: 0
|
||||
};
|
||||
|
||||
var maxValue = {
|
||||
y: 0
|
||||
};
|
||||
|
||||
let timePeriodRatio = ONE_DAY_IN_MILISECONDS / timePeriod;
|
||||
|
||||
maxValue.y = getMaxVolume() / (timePeriodRatio / 10);
|
||||
|
||||
timeLineCoordinateSystem.initializeY(
|
||||
minValue,
|
||||
maxValue,
|
||||
viewPort.visibleArea.bottomRight.y - viewPort.visibleArea.topLeft.y
|
||||
);
|
||||
|
||||
timeLineCoordinateSystemFrame.initializeY(
|
||||
minValue,
|
||||
maxValue,
|
||||
thisObject.container.frame.height
|
||||
);
|
||||
|
||||
function getMaxVolume() {
|
||||
|
||||
let maxValue = 0;
|
||||
|
||||
for (var i = 0; i < marketFile.length; i++) {
|
||||
|
||||
let currentMax = (marketFile[i][0] + marketFile[i][1]) * 4;
|
||||
|
||||
if (maxValue < currentMax) {
|
||||
maxValue = currentMax;
|
||||
}
|
||||
}
|
||||
|
||||
return maxValue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function plotChart() {
|
||||
|
||||
if (volumes.length > 0) {
|
||||
|
||||
/* Now we calculate and plot the volumes */
|
||||
|
||||
let visibleHeight = viewPort.visibleArea.bottomRight.y - viewPort.visibleArea.topLeft.y;
|
||||
|
||||
let frameCorner1 = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
let frameCorner2 = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: thisObject.container.frame.height
|
||||
};
|
||||
|
||||
|
||||
/* Now the transformations. */
|
||||
|
||||
frameCorner1 = transformThisPoint(frameCorner1, thisObject.container.frame.container);
|
||||
frameCorner2 = transformThisPoint(frameCorner2, thisObject.container.frame.container);
|
||||
|
||||
let frameHeightInViewPort = frameCorner2.y - frameCorner1.y;
|
||||
|
||||
if (volumes.length > 0) {
|
||||
|
||||
for (var i = 0; i < volumes.length; i++) {
|
||||
|
||||
volume = volumes[i];
|
||||
|
||||
let volumePointA1;
|
||||
let volumePointA2;
|
||||
let volumePointA3;
|
||||
let volumePointA4;
|
||||
|
||||
function calculateBuys(plot, height) {
|
||||
|
||||
volumePointA1 = {
|
||||
x: volume.begin + timePeriod / 7 * 2,
|
||||
y: 0
|
||||
};
|
||||
|
||||
volumePointA2 = {
|
||||
x: volume.begin + timePeriod / 7 * 2,
|
||||
y: volume.amountBuy
|
||||
};
|
||||
|
||||
volumePointA3 = {
|
||||
x: volume.begin + timePeriod / 7 * 5,
|
||||
y: volume.amountBuy
|
||||
};
|
||||
|
||||
volumePointA4 = {
|
||||
x: volume.begin + timePeriod / 7 * 5,
|
||||
y: 0
|
||||
};
|
||||
|
||||
volumePointA1 = plot.transformThisPoint(volumePointA1);
|
||||
volumePointA2 = plot.transformThisPoint(volumePointA2);
|
||||
volumePointA3 = plot.transformThisPoint(volumePointA3);
|
||||
volumePointA4 = plot.transformThisPoint(volumePointA4);
|
||||
|
||||
volumePointA1 = transformThisPoint(volumePointA1, thisObject.container);
|
||||
volumePointA2 = transformThisPoint(volumePointA2, thisObject.container);
|
||||
volumePointA3 = transformThisPoint(volumePointA3, thisObject.container);
|
||||
volumePointA4 = transformThisPoint(volumePointA4, thisObject.container);
|
||||
|
||||
let baseIncrement = (volumePointA3.x - volumePointA1.x) * WIDHTER_VOLUME_BAR_BASE_FACTOR;
|
||||
|
||||
volumePointA1.x = volumePointA1.x - baseIncrement;
|
||||
volumePointA4.x = volumePointA4.x + baseIncrement;
|
||||
|
||||
if (volumePointA4.x < viewPort.visibleArea.bottomLeft.x || volumePointA1.x > viewPort.visibleArea.bottomRight.x) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
if (calculateBuys(timeLineCoordinateSystemFrame, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area.
|
||||
|
||||
if (volumePointA1.y > viewPort.visibleArea.bottomLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) {
|
||||
|
||||
if (calculateBuys(timeLineCoordinateSystem, visibleHeight) === false) { continue; } // We snap t to the view port.
|
||||
|
||||
/* Now we set the real value of y. */
|
||||
|
||||
volumePointA1.y = viewPort.visibleArea.bottomRight.y;
|
||||
volumePointA2.y = viewPort.visibleArea.bottomRight.y - volume.amountBuy * timeLineCoordinateSystem.scale.y;
|
||||
volumePointA3.y = viewPort.visibleArea.bottomRight.y - volume.amountBuy * timeLineCoordinateSystem.scale.y;
|
||||
volumePointA4.y = viewPort.visibleArea.bottomRight.y;
|
||||
|
||||
}
|
||||
|
||||
let volumePointB1;
|
||||
let volumePointB2;
|
||||
let volumePointB3;
|
||||
let volumePointB4;
|
||||
|
||||
function calculateSells(plot, height) {
|
||||
|
||||
volumePointB1 = {
|
||||
x: volume.begin + timePeriod / 7 * 2,
|
||||
y: height
|
||||
};
|
||||
|
||||
volumePointB2 = {
|
||||
x: volume.begin + timePeriod / 7 * 2,
|
||||
y: height - volume.amountSell
|
||||
};
|
||||
|
||||
volumePointB3 = {
|
||||
x: volume.begin + timePeriod / 7 * 5,
|
||||
y: height - volume.amountSell
|
||||
};
|
||||
|
||||
volumePointB4 = {
|
||||
x: volume.begin + timePeriod / 7 * 5,
|
||||
y: height
|
||||
};
|
||||
|
||||
volumePointB1 = plot.transformThisPoint2(volumePointB1);
|
||||
volumePointB2 = plot.transformThisPoint2(volumePointB2);
|
||||
volumePointB3 = plot.transformThisPoint2(volumePointB3);
|
||||
volumePointB4 = plot.transformThisPoint2(volumePointB4);
|
||||
|
||||
volumePointB1 = transformThisPoint(volumePointB1, thisObject.container);
|
||||
volumePointB2 = transformThisPoint(volumePointB2, thisObject.container);
|
||||
volumePointB3 = transformThisPoint(volumePointB3, thisObject.container);
|
||||
volumePointB4 = transformThisPoint(volumePointB4, thisObject.container);
|
||||
|
||||
}
|
||||
|
||||
calculateSells(timeLineCoordinateSystemFrame, thisObject.container.frame.height); // We try to see if it fits in the visible area.
|
||||
|
||||
if (volumePointB1.y < viewPort.visibleArea.topLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) {
|
||||
|
||||
calculateSells(timeLineCoordinateSystem, visibleHeight); // We snap it to the view port.
|
||||
|
||||
/* Now we set the real value of y. */
|
||||
|
||||
volumePointB1.y = viewPort.visibleArea.topLeft.y;
|
||||
volumePointB2.y = viewPort.visibleArea.topLeft.y + volume.amountSell * timeLineCoordinateSystem.scale.y;
|
||||
volumePointB3.y = viewPort.visibleArea.topLeft.y + volume.amountSell * timeLineCoordinateSystem.scale.y;
|
||||
volumePointB4.y = viewPort.visibleArea.topLeft.y;
|
||||
|
||||
}
|
||||
|
||||
/* We put a wider base */
|
||||
|
||||
let baseIncrement = (volumePointB3.x - volumePointB1.x) * WIDHTER_VOLUME_BAR_BASE_FACTOR;
|
||||
|
||||
volumePointB1.x = volumePointB1.x - baseIncrement;
|
||||
volumePointB4.x = volumePointB4.x + baseIncrement;
|
||||
|
||||
/* We put a less wider top */
|
||||
|
||||
let baseDencrement = (volumePointA3.x - volumePointA2.x) * LESS_WIDHTER_VOLUME_BAR_TOP_FACTOR;
|
||||
|
||||
volumePointA2.x = volumePointA2.x + baseDencrement;
|
||||
volumePointA3.x = volumePointA3.x - baseDencrement;
|
||||
|
||||
volumePointB2.x = volumePointB2.x + baseDencrement;
|
||||
volumePointB3.x = volumePointB3.x - baseDencrement;
|
||||
|
||||
|
||||
/* Everything must fit within the visible area */
|
||||
|
||||
volumePointA1 = viewPort.fitIntoVisibleArea(volumePointA1);
|
||||
volumePointA2 = viewPort.fitIntoVisibleArea(volumePointA2);
|
||||
volumePointA3 = viewPort.fitIntoVisibleArea(volumePointA3);
|
||||
volumePointA4 = viewPort.fitIntoVisibleArea(volumePointA4);
|
||||
|
||||
volumePointB1 = viewPort.fitIntoVisibleArea(volumePointB1);
|
||||
volumePointB2 = viewPort.fitIntoVisibleArea(volumePointB2);
|
||||
volumePointB3 = viewPort.fitIntoVisibleArea(volumePointB3);
|
||||
volumePointB4 = viewPort.fitIntoVisibleArea(volumePointB4);
|
||||
|
||||
/* Everything must fit within the Frame. We know that that is equivalent to say that each bar con not be higher than the base of the opposite . */
|
||||
|
||||
if (volumePointA2.y < volumePointB1.y) {
|
||||
|
||||
volumePointA2.y = volumePointB1.y;
|
||||
volumePointA3.y = volumePointB1.y;
|
||||
|
||||
}
|
||||
|
||||
if (volumePointB2.y > volumePointA1.y) {
|
||||
|
||||
volumePointB2.y = volumePointA1.y;
|
||||
volumePointB3.y = volumePointA1.y;
|
||||
|
||||
}
|
||||
|
||||
/* Now the drawing of the volume bars*/
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(volumePointA1.x, volumePointA1.y);
|
||||
browserCanvasContext.lineTo(volumePointA2.x, volumePointA2.y);
|
||||
browserCanvasContext.lineTo(volumePointA3.x, volumePointA3.y);
|
||||
browserCanvasContext.lineTo(volumePointA4.x, volumePointA4.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
|
||||
if (datetime !== undefined) {
|
||||
|
||||
let dateValue = datetime.valueOf();
|
||||
|
||||
if (dateValue >= volume.begin && dateValue <= volume.end) {
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 233, 31, 0.40)'; // Current bar accroding to time
|
||||
|
||||
} else {
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(64, 217, 26, 0.40)';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(64, 217, 26, 0.40)';
|
||||
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, 0.40)';
|
||||
browserCanvasContext.lineWidth = 1;
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(volumePointB1.x, volumePointB1.y);
|
||||
browserCanvasContext.lineTo(volumePointB2.x, volumePointB2.y);
|
||||
browserCanvasContext.lineTo(volumePointB3.x, volumePointB3.y);
|
||||
browserCanvasContext.lineTo(volumePointB4.x, volumePointB4.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (datetime !== undefined) {
|
||||
|
||||
let dateValue = datetime.valueOf();
|
||||
|
||||
if (dateValue >= volume.begin && dateValue <= volume.end) {
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 233, 31, 0.40)'; // Current candle accroding to time
|
||||
|
||||
} else {
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(219, 18, 18, 0.40)';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(219, 18, 18, 0.40)';
|
||||
|
||||
}
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, 0.40)';
|
||||
|
||||
browserCanvasContext.fill();
|
||||
browserCanvasContext.lineWidth = 1;
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
|
||||
|
||||
if (datetime !== undefined) {
|
||||
|
||||
let dateValue = datetime.valueOf();
|
||||
|
||||
if (dateValue >= volume.begin && dateValue <= volume.end) {
|
||||
|
||||
let buyInfo = {
|
||||
baseWidth: volumePointA4.x - volumePointA1.x,
|
||||
topWidth: volumePointA3.x - volumePointA2.x,
|
||||
height: volumePointA2.y - volumePointA1.y
|
||||
};
|
||||
|
||||
let sellInfo = {
|
||||
baseWidth: volumePointB4.x - volumePointB1.x,
|
||||
topWidth: volumePointB3.x - volumePointB2.x,
|
||||
height: volumePointB2.y - volumePointB1.y
|
||||
};
|
||||
|
||||
let currentVolume = {
|
||||
buyInfo: buyInfo,
|
||||
sellInfo: sellInfo,
|
||||
period: timePeriod,
|
||||
innerVolumeBar: volume
|
||||
};
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Current Volume Info Changed", currentVolume);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onZoomChanged(event) {
|
||||
|
||||
recalculateScaleX();
|
||||
recalculate();
|
||||
recalculateScaleY();
|
||||
|
||||
}
|
||||
|
||||
function onDragFinished() {
|
||||
|
||||
recalculateScaleX();
|
||||
recalculate();
|
||||
recalculateScaleY();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+926
@@ -0,0 +1,926 @@
|
||||
function newAAMastersPlottersTradingHistory() {
|
||||
|
||||
const MODULE_NAME = "AAMasters Plotters Trading History";
|
||||
const INTENSIVE_LOG = false;
|
||||
const ERROR_LOG = true;
|
||||
const logger = newWebDebugLog();
|
||||
logger.fileName = MODULE_NAME;
|
||||
|
||||
let thisObject = {
|
||||
|
||||
// Main functions and properties.
|
||||
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
container: undefined,
|
||||
getContainer: getContainer,
|
||||
setTimePeriod: setTimePeriod,
|
||||
setDatetime: setDatetime,
|
||||
recalculateScale: recalculateScale,
|
||||
draw: draw,
|
||||
payload: {
|
||||
profile: {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
visible: false
|
||||
},
|
||||
notes: []
|
||||
}
|
||||
};
|
||||
|
||||
/* this is part of the module template */
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
|
||||
let timeLineCoordinateSystem = newTimeLineCoordinateSystem(); // Needed to be able to plot on the timeline, otherwise not.
|
||||
|
||||
let timePeriod; // This will hold the current Time Period the user is at.
|
||||
let datetime; // This will hold the current Datetime the user is at.
|
||||
|
||||
/* these are module specific variables: */
|
||||
|
||||
let fileSequence;
|
||||
let plotElements = []; // This is where the elements to be plotted are stored before plotting.
|
||||
let plotLines = []; // Here we store the lines of open positions.
|
||||
let notes = []; // Here we store the notes with messages from the bot.
|
||||
|
||||
let notesChangedEventRaised = true; // This controls when to raise the event that notes changed.
|
||||
|
||||
let previousNotesSetKey;
|
||||
let currentNotesSetKey;
|
||||
|
||||
let offsetChangedEventSubscriptionId
|
||||
let filesUpdatedEventSubscriptionId
|
||||
let dimmensionsChangedEventSubscriptionId
|
||||
|
||||
return thisObject;
|
||||
|
||||
function finalize() {
|
||||
try {
|
||||
|
||||
/* Stop listening to the necesary events. */
|
||||
|
||||
viewPort.eventHandler.stopListening(offsetChangedEventSubscriptionId);
|
||||
fileSequence.eventHandler.stopListening(filesUpdatedEventSubscriptionId);
|
||||
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
|
||||
|
||||
/* Destroyd References */
|
||||
|
||||
datetime = undefined;
|
||||
timePeriod = undefined;
|
||||
|
||||
fileSequence = undefined;
|
||||
plotElements = undefined;
|
||||
plotLines = undefined;
|
||||
notes = undefined;
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err.message = " + err.message); }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize(pStorage, pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
datetime = pDatetime;
|
||||
timePeriod = pTimePeriod;
|
||||
|
||||
fileSequence = pStorage.fileSequences[0];
|
||||
|
||||
recalculate(callBackFunction);
|
||||
recalculateScale(callBackFunction);
|
||||
|
||||
filesUpdatedEventSubscriptionId = fileSequence.eventHandler.listenToEvent("Files Updated", onFilesUpdated);// Only the first sequence is supported right now.
|
||||
offsetChangedEventSubscriptionId = viewPort.eventHandler.listenToEvent("Offset Changed", onOffsetChanged);
|
||||
|
||||
immensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
|
||||
recalculate(callBackFunction);
|
||||
recalculateScale(callBackFunction);
|
||||
})
|
||||
|
||||
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
|
||||
callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer(point) {
|
||||
|
||||
try {
|
||||
|
||||
let container;
|
||||
|
||||
/* First we check if this point is inside this space. */
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
||||
|
||||
return thisObject.container;
|
||||
|
||||
} else {
|
||||
|
||||
/* This point does not belong to this space. */
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onFilesUpdated() {
|
||||
|
||||
recalculate();
|
||||
|
||||
}
|
||||
|
||||
function setTimePeriod(pTimePeriod) {
|
||||
|
||||
try {
|
||||
|
||||
timePeriod = pTimePeriod;
|
||||
|
||||
recalculate();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime(newDatetime) {
|
||||
|
||||
try {
|
||||
|
||||
datetime = newDatetime;
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function onOffsetChanged() {
|
||||
|
||||
if (Math.random() * 100 > 95) {
|
||||
|
||||
recalculate()
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function recalculate(callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (fileSequence === undefined) { return; }
|
||||
|
||||
/*
|
||||
|
||||
We are going to filter the records depending on the Time Period. We want that for a 1 min time peroid all the records appears on screen,
|
||||
but for higher periods, we will filter out some records, so that they do not overlap ever.
|
||||
|
||||
*/
|
||||
|
||||
plotElements = [];
|
||||
plotLines = [];
|
||||
notes = [];
|
||||
|
||||
let lastSellRate;
|
||||
let lastSellDate;
|
||||
let sellExecRate;
|
||||
|
||||
let lastBuyRate;
|
||||
let lastBuyDate;
|
||||
let buyExecRate;
|
||||
|
||||
let maxSequence = fileSequence.getFilesLoaded();
|
||||
|
||||
for (let j = 0; j < maxSequence; j++) {
|
||||
|
||||
let file = fileSequence.getFile(j);
|
||||
|
||||
let history = [];
|
||||
let lines = [];
|
||||
|
||||
/* First the small balls */
|
||||
|
||||
const ONE_MIN_IN_MILISECONDS = 60 * 1000;
|
||||
let step = timePeriod / ONE_MIN_IN_MILISECONDS;
|
||||
|
||||
let i = 0;
|
||||
let lastRecordPushed = 0;
|
||||
|
||||
for (i = 0; i < file.length; i = i + step) {
|
||||
|
||||
let newHistoryRecord = {
|
||||
|
||||
date: Math.trunc(file[i][0] / 60000) * 60000 + 30000,
|
||||
buyAvgRate: file[i][1],
|
||||
sellAvgRate: file[i][2],
|
||||
|
||||
lastSellRate: file[i][3],
|
||||
sellExecRate: file[i][4],
|
||||
lastBuyRate: file[i][5],
|
||||
buyExecRate: file[i][6],
|
||||
|
||||
marketRate: file[i][7],
|
||||
newPositions: file[i][8],
|
||||
newTrades: file[i][9],
|
||||
movedPositions: file[i][10],
|
||||
profitsAssetA: file[i][11],
|
||||
profitsAssetB: file[i][12],
|
||||
combinedProfitsA: file[i][13],
|
||||
combinedProfitsB: file[i][14],
|
||||
|
||||
messageRelevance: file[i][15],
|
||||
messageTitle: file[i][16],
|
||||
messageBody: file[i][17]
|
||||
};
|
||||
|
||||
history.push(newHistoryRecord);
|
||||
lastRecordPushed = i;
|
||||
|
||||
/* Here we build the lines. */
|
||||
|
||||
if (timePeriod <= 1 * 60 * 1000) {
|
||||
|
||||
if (newHistoryRecord.lastSellRate > 0) {
|
||||
|
||||
lastSellRate = newHistoryRecord.lastSellRate;
|
||||
lastSellDate = newHistoryRecord.date;
|
||||
|
||||
}
|
||||
|
||||
if (newHistoryRecord.sellExecRate > 0) {
|
||||
|
||||
let newLine = {
|
||||
type: "sell",
|
||||
x1: lastSellDate,
|
||||
y1: lastSellRate,
|
||||
x2: newHistoryRecord.date,
|
||||
y2: newHistoryRecord.sellExecRate
|
||||
};
|
||||
|
||||
lines.push(newLine);
|
||||
|
||||
}
|
||||
|
||||
if (newHistoryRecord.lastBuyRate > 0) {
|
||||
|
||||
lastBuyRate = newHistoryRecord.lastBuyRate;
|
||||
lastBuyDate = newHistoryRecord.date;
|
||||
|
||||
}
|
||||
|
||||
if (newHistoryRecord.buyExecRate > 0) {
|
||||
|
||||
let newLine = {
|
||||
type: "buy",
|
||||
x1: lastBuyDate,
|
||||
y1: lastBuyRate,
|
||||
x2: newHistoryRecord.date,
|
||||
y2: newHistoryRecord.buyExecRate
|
||||
};
|
||||
|
||||
lines.push(newLine);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* We allways want to put the last record of the file on the filterd dataset, so as to allways show the latest advance of the bot. */
|
||||
|
||||
i = file.length - 1;
|
||||
|
||||
if (lastRecordPushed !== i) {
|
||||
|
||||
let newHistoryRecord = {
|
||||
|
||||
date: Math.trunc(file[i][0] / 60000) * 60000 + 30000,
|
||||
buyAvgRate: file[i][1],
|
||||
sellAvgRate: file[i][2],
|
||||
|
||||
lastSellRate: file[i][3],
|
||||
sellExecRate: file[i][4],
|
||||
lastBuyRate: file[i][5],
|
||||
buyExecRate: file[i][6],
|
||||
|
||||
marketRate: file[i][7],
|
||||
newPositions: file[i][8],
|
||||
newTrades: file[i][9],
|
||||
movedPositions: file[i][10],
|
||||
profitsAssetA: file[i][11],
|
||||
profitsAssetB: file[i][12],
|
||||
combinedProfitsA: file[i][13],
|
||||
combinedProfitsB: file[i][14],
|
||||
|
||||
messageRelevance: file[i][15],
|
||||
messageTitle: file[i][16],
|
||||
messageBody: file[i][17]
|
||||
};
|
||||
|
||||
history.push(newHistoryRecord);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Second we process the text */
|
||||
|
||||
for (let i = 0; i < file.length; i++) {
|
||||
|
||||
let newHistoryRecord = {
|
||||
|
||||
date: Math.trunc(file[i][0] / 60000) * 60000 + 30000,
|
||||
buyAvgRate: file[i][1],
|
||||
sellAvgRate: file[i][2],
|
||||
|
||||
lastSellRate: file[i][3],
|
||||
sellExecRate: file[i][4],
|
||||
lastBuyRate: file[i][5],
|
||||
buyExecRate: file[i][6],
|
||||
|
||||
marketRate: file[i][7],
|
||||
newPositions: file[i][8],
|
||||
newTrades: file[i][9],
|
||||
movedPositions: file[i][10],
|
||||
profitsAssetA: file[i][11],
|
||||
profitsAssetB: file[i][12],
|
||||
combinedProfitsA: file[i][13],
|
||||
combinedProfitsB: file[i][14],
|
||||
|
||||
messageRelevance: file[i][15],
|
||||
messageTitle: file[i][16],
|
||||
messageBody: file[i][17]
|
||||
};
|
||||
|
||||
if (newHistoryRecord.messageTitle !== "" && newHistoryRecord.messageBody !== "") {
|
||||
|
||||
if (newHistoryRecord.messageRelevance >= 0 && newHistoryRecord.messageRelevance <= 10) {
|
||||
|
||||
let relevanceTimePeriod = (dailyFilePeriods[10 - newHistoryRecord.messageRelevance][0]);
|
||||
|
||||
if (timePeriod <= relevanceTimePeriod) {
|
||||
|
||||
let note = {
|
||||
title: newHistoryRecord.messageTitle,
|
||||
body: newHistoryRecord.messageBody,
|
||||
date: newHistoryRecord.date,
|
||||
rate: newHistoryRecord.marketRate,
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
visible: false
|
||||
};
|
||||
|
||||
notes.push(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plotElements.push(history);
|
||||
plotLines.push(lines);
|
||||
|
||||
}
|
||||
|
||||
notesChangedEventRaised = false;
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("History Changed", history);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
|
||||
callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateScale(callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (fileSequence === undefined) { return; } // We need the market file to be loaded to make the calculation.
|
||||
|
||||
if (timeLineCoordinateSystem.maxValue > 0) { return; } // Already calculated.
|
||||
|
||||
let minValue = {
|
||||
x: MIN_PLOTABLE_DATE.valueOf(),
|
||||
y: 0
|
||||
};
|
||||
|
||||
let maxValue = {
|
||||
x: MAX_PLOTABLE_DATE.valueOf(),
|
||||
y: nextPorwerOf10(USDT_BTC_HTH) / 4 // TODO: This 4 is temporary
|
||||
};
|
||||
|
||||
|
||||
timeLineCoordinateSystem.initialize(
|
||||
minValue,
|
||||
maxValue,
|
||||
thisObject.container.frame.width,
|
||||
thisObject.container.frame.height
|
||||
);
|
||||
|
||||
function getMaxRate() {
|
||||
|
||||
try {
|
||||
|
||||
let maxValue = 0;
|
||||
|
||||
let maxSequence = fileSequence.getFilesLoaded();
|
||||
|
||||
for (let j = 0; j < maxSequence; j++) {
|
||||
|
||||
let file = fileSequence.getFile(j);
|
||||
|
||||
for (let i = 0; i < file.length; i++) {
|
||||
|
||||
let currentMax = file[i][1] + file[i][2]; // 1 = rates.
|
||||
|
||||
if (maxValue < currentMax) {
|
||||
maxValue = currentMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxValue;
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScale -> getMaxRate -> err = " + err.stack); }
|
||||
callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScale -> err = " + err.stack); }
|
||||
callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
try {
|
||||
|
||||
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
|
||||
|
||||
plotChart();
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
|
||||
function plotChart() {
|
||||
|
||||
try {
|
||||
|
||||
let userPosition = getUserPosition()
|
||||
let userPositionDate = userPosition.point.x
|
||||
|
||||
let point = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
let history;
|
||||
|
||||
for (let j = 0; j < plotElements.length; j++) {
|
||||
|
||||
let history = plotElements[j];
|
||||
|
||||
for (let i = 0; i < history.length; i++) {
|
||||
|
||||
record = history[i];
|
||||
|
||||
let timestamp = Math.trunc(record.date / timePeriod) * timePeriod + timePeriod / 2;
|
||||
|
||||
point = {
|
||||
x: timestamp,
|
||||
y: record.marketRate
|
||||
};
|
||||
|
||||
point = timeLineCoordinateSystem.transformThisPoint(point);
|
||||
point = transformThisPoint(point, thisObject.container);
|
||||
|
||||
if (point.x < viewPort.visibleArea.bottomLeft.x || point.x > viewPort.visibleArea.bottomRight.x) { continue; }
|
||||
|
||||
point = viewPort.fitIntoVisibleArea(point);
|
||||
|
||||
let isCurrentRecord = false;
|
||||
|
||||
if (userPositionDate >= record.date - timePeriod / 2 && userPositionDate <= record.date + timePeriod / 2 - 1) {
|
||||
isCurrentRecord = true;
|
||||
}
|
||||
|
||||
let radius = 3;
|
||||
|
||||
let opacity = '0.2';
|
||||
|
||||
browserCanvasContext.lineWidth = 1;
|
||||
|
||||
/* Outer Circle */
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.LIGHT + ', ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true);
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (point.x < viewPort.visibleArea.topLeft.x + 50 || point.x > viewPort.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
|
||||
browserCanvasContext.fill();
|
||||
}
|
||||
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
/* Draw a red inverted triangle on exec sell */
|
||||
|
||||
if (record.sellExecRate > 0) {
|
||||
|
||||
opacity = '0.5';
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.sellExecRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timePeriod / 7 * 2,
|
||||
y: record.sellExecRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timePeriod / 7 * 2,
|
||||
y: record.sellExecRate
|
||||
};
|
||||
|
||||
point1 = timeLineCoordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = viewPort.fitIntoVisibleArea(point1);
|
||||
|
||||
point2 = timeLineCoordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
|
||||
point3 = timeLineCoordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y - diff;
|
||||
point3.y = point3.y - diff;
|
||||
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(219, 18, 18, ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, ' + opacity + ')';
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
/* Draw a green triangle on exec buy */
|
||||
|
||||
if (record.buyExecRate > 0) {
|
||||
|
||||
opacity = '0.5';
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.buyExecRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timePeriod / 7 * 2,
|
||||
y: record.buyExecRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timePeriod / 7 * 2,
|
||||
y: record.buyExecRate
|
||||
};
|
||||
|
||||
point1 = timeLineCoordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = viewPort.fitIntoVisibleArea(point1);
|
||||
|
||||
point2 = timeLineCoordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
|
||||
point3 = timeLineCoordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y + diff;
|
||||
point3.y = point3.y + diff;
|
||||
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(64, 217, 26, ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, ' + opacity + ')';
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
/* Draw a red inverted triangle on sell */
|
||||
|
||||
if (record.lastSellRate > 0) {
|
||||
|
||||
opacity = '0.5';
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.lastSellRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timePeriod / 7 * 2,
|
||||
y: record.lastSellRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timePeriod / 7 * 2,
|
||||
y: record.lastSellRate
|
||||
};
|
||||
|
||||
point1 = timeLineCoordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = viewPort.fitIntoVisibleArea(point1);
|
||||
|
||||
point2 = timeLineCoordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
|
||||
point3 = timeLineCoordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y - diff;
|
||||
point3.y = point3.y - diff;
|
||||
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 255, 255, ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, ' + opacity + ')';
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
/* Draw a green triangle on buy */
|
||||
|
||||
if (record.lastBuyRate > 0) {
|
||||
|
||||
opacity = '0.5';
|
||||
|
||||
let point1 = {
|
||||
x: record.date,
|
||||
y: record.lastBuyRate
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: record.date + timePeriod / 7 * 2,
|
||||
y: record.lastBuyRate
|
||||
};
|
||||
|
||||
let point3 = {
|
||||
x: record.date - timePeriod / 7 * 2,
|
||||
y: record.lastBuyRate
|
||||
};
|
||||
|
||||
point1 = timeLineCoordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = viewPort.fitIntoVisibleArea(point1);
|
||||
|
||||
point2 = timeLineCoordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
|
||||
point3 = timeLineCoordinateSystem.transformThisPoint(point3);
|
||||
point3 = transformThisPoint(point3, thisObject.container);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
let diff = point2.x - point3.x;
|
||||
point2.y = point2.y + diff;
|
||||
point3.y = point3.y + diff;
|
||||
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
point3 = viewPort.fitIntoVisibleArea(point3);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
browserCanvasContext.lineTo(point3.x, point3.y);
|
||||
browserCanvasContext.lineTo(point1.x, point1.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (isCurrentRecord === false) {
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 255, 255, ' + opacity + ')';
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(255, 233, 31, ' + opacity + ')'; /* highlight the current record */
|
||||
}
|
||||
|
||||
browserCanvasContext.fill();
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, ' + opacity + ')';
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Since there is at least some point plotted, then the profile should be visible. */
|
||||
|
||||
thisObject.payload.profile.visible = true;
|
||||
}
|
||||
|
||||
/* Draw the lines connecting plot elements. */
|
||||
|
||||
let lines = plotLines[j];
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
|
||||
let line = lines[i];
|
||||
|
||||
opacity = '0.2';
|
||||
|
||||
let point1 = {
|
||||
x: line.x1,
|
||||
y: line.y1
|
||||
};
|
||||
|
||||
let point2 = {
|
||||
x: line.x2,
|
||||
y: line.y2
|
||||
};
|
||||
|
||||
point1 = timeLineCoordinateSystem.transformThisPoint(point1);
|
||||
point1 = transformThisPoint(point1, thisObject.container);
|
||||
point1 = viewPort.fitIntoVisibleArea(point1);
|
||||
|
||||
point2 = timeLineCoordinateSystem.transformThisPoint(point2);
|
||||
point2 = transformThisPoint(point2, thisObject.container);
|
||||
point2 = viewPort.fitIntoVisibleArea(point2);
|
||||
|
||||
browserCanvasContext.beginPath();
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y);
|
||||
browserCanvasContext.lineTo(point2.x, point2.y);
|
||||
|
||||
browserCanvasContext.closePath();
|
||||
|
||||
if (line.type === "sell") {
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(130, 9, 9, ' + opacity + ')';
|
||||
|
||||
} else {
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(27, 105, 7, ' + opacity + ')';
|
||||
|
||||
}
|
||||
|
||||
browserCanvasContext.stroke();
|
||||
|
||||
}
|
||||
|
||||
/* Now we calculate the anchor position of notes. */
|
||||
|
||||
currentNotesSetKey = "";
|
||||
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
|
||||
let note = notes[i];
|
||||
|
||||
opacity = '0.2';
|
||||
|
||||
note.position = {
|
||||
x: note.date,
|
||||
y: note.rate
|
||||
};
|
||||
|
||||
note.position = timeLineCoordinateSystem.transformThisPoint(note.position);
|
||||
note.position = transformThisPoint(note.position, thisObject.container);
|
||||
|
||||
if (note.position.x < (viewPort.visibleArea.bottomRight.x / 2) * (-1) || note.position.x > (viewPort.visibleArea.bottomRight.x) * (1.5)) {
|
||||
note.visible = false;
|
||||
currentNotesSetKey = currentNotesSetKey + "0";
|
||||
} else {
|
||||
note.visible = true;
|
||||
currentNotesSetKey = currentNotesSetKey + "1";
|
||||
}
|
||||
}
|
||||
|
||||
if (notesChangedEventRaised === false) {
|
||||
|
||||
/* In this case the event is raised because we might have a different set of notes. */
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Notes Changed", notes);
|
||||
thisObject.payload.notes = notes;
|
||||
|
||||
notesChangedEventRaised = true;
|
||||
|
||||
} else {
|
||||
|
||||
if (currentNotesSetKey !== previousNotesSetKey) {
|
||||
|
||||
/* In this case the event is raised because the visibility of some of the notes changed. */
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent("Notes Changed", notes);
|
||||
thisObject.payload.notes = notes;
|
||||
|
||||
previousNotesSetKey = currentNotesSetKey;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
We replace the coordinate of the profile point so that whoever has a reference to it, gets the new position.
|
||||
We will use the last point plotted on screen as the profilePoint.
|
||||
|
||||
*/
|
||||
|
||||
thisObject.payload.profile.position.x = point.x;
|
||||
thisObject.payload.profile.position.y = point.y;
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
@@ -0,0 +1,24 @@
|
||||
Financial Beings License
|
||||
|
||||
Collective Copyright by the Advanced Algos Community
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute and sublicense copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
1. Using, copying, modifying, publishing, distributing and sublicensing rights
|
||||
are restricted to the Advanced Algos Platform, meaning the rights granted can
|
||||
only be exercised through the Advanced Algos Service.
|
||||
|
||||
2. The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2027
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "Project", "Project.njsproj", "{096B35AD-F86F-47B9-A99F-04CA93397DA8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{096B35AD-F86F-47B9-A99F-04CA93397DA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{096B35AD-F86F-47B9-A99F-04CA93397DA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{096B35AD-F86F-47B9-A99F-04CA93397DA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{096B35AD-F86F-47B9-A99F-04CA93397DA8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E75EDC9D-66A7-4C54-8EA1-5ADC148CACA8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{096b35ad-f86f-47b9-a99f-04ca93397da8}</ProjectGuid>
|
||||
<ProjectHome />
|
||||
<ProjectView>ShowAllFiles</ProjectView>
|
||||
<StartupFile>History.js</StartupFile>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{349c5851-65df-11da-9384-00065b846f21};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<Name>Project</Name>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
|
||||
<ItemGroup>
|
||||
<Compile Include="History.js" />
|
||||
<Compile Include="Details.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="this.plotter.config.json" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<!--Do not delete the following Import Project. While this appears to do nothing it is a marker for setting TypeScript properties before our import that depends on them.-->
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="False" />
|
||||
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsTools.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>0</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:48022/</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://localhost:1337</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}" User="">
|
||||
<WebProjectProperties>
|
||||
<StartPageUrl>
|
||||
</StartPageUrl>
|
||||
<StartAction>CurrentPage</StartAction>
|
||||
<AspNetDebugging>True</AspNetDebugging>
|
||||
<SilverlightDebugging>False</SilverlightDebugging>
|
||||
<NativeDebugging>False</NativeDebugging>
|
||||
<SQLDebugging>False</SQLDebugging>
|
||||
<ExternalProgram>
|
||||
</ExternalProgram>
|
||||
<StartExternalURL>
|
||||
</StartExternalURL>
|
||||
<StartCmdLineArguments>
|
||||
</StartCmdLineArguments>
|
||||
<StartWorkingDirectory>
|
||||
</StartWorkingDirectory>
|
||||
<EnableENC>False</EnableENC>
|
||||
<AlwaysStartWebServerOnDebug>False</AlwaysStartWebServerOnDebug>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"displayName": "Plotters Trading",
|
||||
"codeName": "PlottersTrading",
|
||||
"devTeam": "AAMasters",
|
||||
"modules": [
|
||||
{
|
||||
"codeName": "Details",
|
||||
"moduleName": "Details",
|
||||
"description": "Plots trading decitions in an easy to understand way.",
|
||||
"panels": []
|
||||
},
|
||||
{
|
||||
"codeName": "History",
|
||||
"moduleName": "History",
|
||||
"description": "Plots trading history.",
|
||||
"profilePicture": "History.png",
|
||||
"panels": []
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user