Old bot names refactored

This commit is contained in:
Luis Fernando Molina committed 2020-03-12 11:41:24 +01:00
1 parent ec052d2e94
commit 2b5abf1a61
65 files changed
+316 -1383

No files matched your search

@@ -0,0 +1,713 @@
function newMastersPlottersBollingerBandsBollingerBands() {
const MODULE_NAME = "Bands Plotter";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
// Main functions and properties.
initialize: initialize,
finalize: finalize,
container: undefined,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
draw: draw,
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Secondary functions and properties.
currentBand: undefined
};
/* 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 coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
/* these are module specific variables: */
let bands = []; // Here we keep the bands to be ploted every time the Draw() function is called by the AAWebPlatform.
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
function getContainer(point) {
try {
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;
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
let newMarketFile = marketFiles.getFile(pTimeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
try {
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
this.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Bands Changed", bands);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (fileCursor === undefined) { return; } // We need to wait
if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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());
bands = [];
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 band = {
begin: undefined,
end: undefined,
movingAverage: undefined,
standardDeviation: undefined,
deviation: undefined
};
band.begin = dailyFile[i][0];
band.end = dailyFile[i][1];
band.movingAverage = dailyFile[i][2];
band.standardDeviation = dailyFile[i][3];
band.deviation = dailyFile[i][4];
if (
(band.begin >= farLeftDate.valueOf() && band.end <= farRightDate.valueOf()) &&
(band.begin >= coordinateSystem.min.x && band.end <= coordinateSystem.max.x)
) {
bands.push(band);
if (datetime.valueOf() >= band.begin && datetime.valueOf() <= band.end) {
thisObject.currentBand = band;
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentBand);
}
}
}
}
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
}
/* Lests check if all the visible screen is going to be covered by bands. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
if (bands.length > 0) {
if (bands[0].begin > lowerEnd || bands[bands.length - 1].end < upperEnd) {
setTimeout(recalculate, 2000);
//console.log("File missing while calculating bands, scheduling a recalculation in 2 seconds.");
}
}
//console.log("Olivia > recalculateUsingDailyFiles > total bands generated : " + bands.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
bands = [];
for (let i = 0; i < marketFile.length; i++) {
let band = {
begin: undefined,
end: undefined,
movingAverage: undefined,
standardDeviation: undefined,
deviation: undefined
};
band.begin = marketFile[i][0];
band.end = marketFile[i][1];
band.movingAverage = marketFile[i][2];
band.standardDeviation = marketFile[i][3];
band.deviation = marketFile[i][4];
if (
(band.begin >= leftDate.valueOf() && band.end <= rightDate.valueOf()) &&
(band.begin >= coordinateSystem.min.x && band.end <= coordinateSystem.max.x)
) {
bands.push(band);
if (datetime.valueOf() >= band.begin && datetime.valueOf() <= band.end) {
thisObject.currentBand = band;
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentBand);
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
let band;
let previousBand;
if (bands.length > 0) {
/* Now we calculate and plot the bands */
for (let i = 1; i < bands.length; i++) {
band = bands[i];
previousBand = bands[i - 1];
/* From here we draw the actual Bollinger Bands and lines it contains. */
let bandPoint1 = {
x: band.begin,
y: previousBand.movingAverage - previousBand.deviation
};
let bandPoint2 = {
x: band.begin,
y: previousBand.movingAverage + previousBand.deviation
};
let bandPoint3 = {
x: band.end,
y: band.movingAverage + band.deviation
};
let bandPoint4 = {
x: band.end,
y: band.movingAverage - band.deviation
};
let bandPoint5 = {
x: band.begin,
y: previousBand.movingAverage
};
let bandPoint6 = {
x: band.end,
y: band.movingAverage
};
bandPoint1 = coordinateSystem.transformThisPoint(bandPoint1);
bandPoint2 = coordinateSystem.transformThisPoint(bandPoint2);
bandPoint3 = coordinateSystem.transformThisPoint(bandPoint3);
bandPoint4 = coordinateSystem.transformThisPoint(bandPoint4);
bandPoint5 = coordinateSystem.transformThisPoint(bandPoint5);
bandPoint6 = coordinateSystem.transformThisPoint(bandPoint6);
bandPoint1 = transformThisPoint(bandPoint1, thisObject.container);
bandPoint2 = transformThisPoint(bandPoint2, thisObject.container);
bandPoint3 = transformThisPoint(bandPoint3, thisObject.container);
bandPoint4 = transformThisPoint(bandPoint4, thisObject.container);
bandPoint5 = transformThisPoint(bandPoint5, thisObject.container);
bandPoint6 = transformThisPoint(bandPoint6, thisObject.container);
if (bandPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || bandPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue;
}
if (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT) {
let diffA = bandPoint1.y - canvas.chartingSpace.viewport.visibleArea.bottomLeft.y
if (diffA > 0) {
bandPoint1.y = bandPoint1.y - diffA
bandPoint2.y = bandPoint2.y - diffA
bandPoint3.y = bandPoint3.y - diffA
bandPoint4.y = bandPoint4.y - diffA
bandPoint5.y = bandPoint5.y - diffA
bandPoint6.y = bandPoint6.y - diffA
}
let diffB = bandPoint2.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y
if (diffB < 0) {
bandPoint1.y = bandPoint1.y - diffB
bandPoint2.y = bandPoint2.y - diffB
bandPoint3.y = bandPoint3.y - diffB
bandPoint4.y = bandPoint4.y - diffB
bandPoint5.y = bandPoint5.y - diffB
bandPoint6.y = bandPoint6.y - diffB
}
}
bandPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint1);
bandPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint2);
bandPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint3);
bandPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint4);
bandPoint5 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint5);
bandPoint6 = canvas.chartingSpace.viewport.fitIntoVisibleArea(bandPoint6);
bandPoint1 = thisObject.fitFunction(bandPoint1);
bandPoint2 = thisObject.fitFunction(bandPoint2);
bandPoint3 = thisObject.fitFunction(bandPoint3);
bandPoint4 = thisObject.fitFunction(bandPoint4);
bandPoint5 = thisObject.fitFunction(bandPoint5);
bandPoint6 = thisObject.fitFunction(bandPoint6);
/* First we are drawing a semi-transparent background */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y);
browserCanvasContext.lineTo(bandPoint2.x, bandPoint2.y);
browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y);
browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y);
browserCanvasContext.closePath();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 0.05)';
if (userPositionDate >= band.begin && userPositionDate <= band.end) {
/* highlight the current band */
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.05)'; // Current band accroding to time
}
if (
bandPoint1.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50
||
bandPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50
) {
// we leave this bands without fill.
} else {
browserCanvasContext.fill();
}
/* Next we are drawing the outter bands lines */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y);
browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y);
browserCanvasContext.moveTo(bandPoint2.x, bandPoint2.y);
browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)';
if (userPositionDate >= band.begin && userPositionDate <= band.end) {
/* highlight the current band */
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current band accroding to time
let currentBand = {
bodyWidth: bandPoint3.x - bandPoint2.x,
leftBodyHeight: bandPoint2.y - bandPoint1.y,
rightBodyHeight: bandPoint3.y - bandPoint4.y,
topDelta: bandPoint3.y - bandPoint2.y,
bottomDelta: bandPoint4.y - bandPoint1.y,
period: timeFrame,
innerBand: band
};
thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentBand);
}
browserCanvasContext.lineWidth = 0.2;
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
/* Finally we draw the moving average line */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(bandPoint5.x, bandPoint5.y);
browserCanvasContext.lineTo(bandPoint6.x, bandPoint6.y);
browserCanvasContext.closePath();
if (bandPoint5.y > bandPoint6.y) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)';
}
if (userPositionDate >= band.begin && userPositionDate <= band.end) {
/* highlight the current band */
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current band accroding to time
}
browserCanvasContext.lineWidth = 0.2;
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
/* Contributing to Auto-Scale*/
coordinateSystem.reportYValue(band.movingAverage + band.deviation)
coordinateSystem.reportYValue(band.movingAverage - band.deviation)
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
}
}
function onViewportPositionChanged(event) {
try {
if (event !== undefined) {
if (event.recalculate === true) {
recalculate()
return
}
}
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,243 @@
function newMastersPlottersBollingerBandsBollingerBandsBollingerBandsPanel () {
var thisObject = {
fitFunction: undefined,
onRecordChange: onRecordChange,
container: undefined,
isVisible: true,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
};
thisObject.container = newContainer();
thisObject.container.initialize();
thisObject.container.frame.containerName = "Bollinger Band";
let currentBand;
let panelTabButton
return thisObject;
function finalize() {
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.isVisible = undefined
currentBand = undefined
panelTabButton.finalize()
panelTabButton = undefined
}
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL;
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 3;
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height;
panelTabButton = newPanelTabButton()
panelTabButton.parentContainer = thisObject.container
panelTabButton.container.frame.parentFrame = thisObject.container.frame
panelTabButton.fitFunction = thisObject.fitFunction
panelTabButton.initialize()
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = panelTabButton.getContainer(point)
if (container !== undefined) { return container }
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
let checkPoint = {
x: point.x,
y: point.y
}
checkPoint = thisObject.fitFunction(checkPoint)
if (point.x === checkPoint.x && point.y === checkPoint.y) {
return thisObject.container;
}
}
}
function onRecordChange(lastCurrentBand) {
currentBand = lastCurrentBand;
}
function draw() {
if (thisObject.isVisible !== true) { return }
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
plotCurrentBandInfo();
panelTabButton.draw()
}
function plotCurrentBandInfo() {
if (currentBand === undefined) { return; }
if (currentBand.innerBand === undefined) { return; }
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
const X_AXIS = thisObject.container.frame.width / 2;
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
var bandPoint1 = {
x: X_AXIS - currentBand.bodyWidth / 2,
y: Y_AXIS - currentBand.leftBodyHeight / 2 - currentBand.bottomDelta / 4
};
var bandPoint2 = {
x: X_AXIS - currentBand.bodyWidth / 2,
y: Y_AXIS + currentBand.leftBodyHeight / 2 - currentBand.topDelta / 4
};
var bandPoint3 = {
x: X_AXIS + currentBand.bodyWidth / 2,
y: Y_AXIS + currentBand.rightBodyHeight / 2 + currentBand.topDelta / 4
};
var bandPoint4 = {
x: X_AXIS + currentBand.bodyWidth / 2,
y: Y_AXIS - currentBand.rightBodyHeight / 2 + currentBand.bottomDelta / 4
};
/* Extra bounderies due to constrained space */
const upperLimit = frameTitleHeight + frameBodyHeight * 0.15;
const downLimit = frameTitleHeight + frameBodyHeight * 0.85;
if (bandPoint1.y > downLimit) { bandPoint1.y = downLimit; }
if (bandPoint4.y > downLimit) { bandPoint4.y = downLimit; }
if (bandPoint2.y < upperLimit) { bandPoint2.y = upperLimit; }
if (bandPoint3.y < upperLimit) { bandPoint3.y = upperLimit; }
bandPoint1 = thisObject.container.frame.frameThisPoint(bandPoint1);
bandPoint2 = thisObject.container.frame.frameThisPoint(bandPoint2);
bandPoint3 = thisObject.container.frame.frameThisPoint(bandPoint3);
bandPoint4 = thisObject.container.frame.frameThisPoint(bandPoint4);
bandPoint1 = thisObject.fitFunction(bandPoint1)
bandPoint2 = thisObject.fitFunction(bandPoint2)
bandPoint3 = thisObject.fitFunction(bandPoint3)
bandPoint4 = thisObject.fitFunction(bandPoint4)
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y);
browserCanvasContext.lineTo(bandPoint2.x, bandPoint2.y);
browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y);
browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y);
browserCanvasContext.closePath();
const OPACITY = '0.1';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + OPACITY + ')';
browserCanvasContext.fill();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(bandPoint1.x, bandPoint1.y);
browserCanvasContext.lineTo(bandPoint4.x, bandPoint4.y);
browserCanvasContext.moveTo(bandPoint2.x, bandPoint2.y);
browserCanvasContext.lineTo(bandPoint3.x, bandPoint3.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)';
browserCanvasContext.lineWidth = 0.2;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
let y1 = bandPoint1.y + (bandPoint2.y - bandPoint1.y) / 2;
let y2 = bandPoint4.y - (bandPoint4.y - bandPoint3.y) / 2;
browserCanvasContext.moveTo(bandPoint1.x, y1);
browserCanvasContext.lineTo(bandPoint4.x, y2);
browserCanvasContext.closePath();
if (y1 > y2) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)';
}
browserCanvasContext.lineWidth = 0.2;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
/* put the labels with the bands values */
let y;
printLabel('Moving Average', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '1');
printLabel(currentBand.innerBand.movingAverage, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '0.50');
printLabel('Deviation', X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '1');
printLabel(currentBand.innerBand.deviation, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '0.50');
function printLabel(labelToPrint, x, y, opacity) {
let labelPoint;
let fontSize = 10;
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira';
let label = '' + labelToPrint;
if (isNaN(label) === false) {
label = Number(label).toLocaleString();
}
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
labelPoint = {
x: x - xOffset,
y: y
};
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint);
labelPoint = thisObject.fitFunction(labelPoint)
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
}
browserCanvasContext.closePath();
browserCanvasContext.fill();
}
}
@@ -0,0 +1,921 @@
function newMastersPlottersBollingerBandsPercentageBandwidth() {
const MODULE_NAME = "Bands Plotter";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
// Main functions and properties.
initialize: initialize,
finalize: finalize,
container: undefined,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
draw: draw,
recalculateScale: recalculateScale,
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Secondary functions and properties.
currentPercentageBandwidth: undefined
};
/* 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 plotAreaFrame = newCoordinateSystem(); // Used for full frame view.
let plotAreaViewport = newCoordinateSystem(); // Used for viewport view.
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
/* these are module specific variables: */
let percentageBandwidthArray = []; // Here we keep the percentageBandwidthArray to be ploted every time the Draw() function is called by the AAWebPlatform.
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, callBackFunction) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
recalculateScaleY();
recalculateScaleX();
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
/* Get ready for plotting. */
recalculate();
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculateScaleY();
recalculateScaleX();
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
function recalculateScale() {
recalculateScaleY();
recalculateScaleX();
}
function getContainer(point) {
try {
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;
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
let newMarketFile = marketFiles.getFile(pTimeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function onDailyFileLoaded(event) {
try {
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
this.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Bands Changed", percentageBandwidthArray);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (fileCursor === undefined) { return; } // We need to wait
if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, plotAreaFrame);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, plotAreaFrame);
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());
percentageBandwidthArray = [];
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 percentageBandwidth = {
begin: undefined,
end: undefined,
value: undefined,
movingAverage: undefined,
bandwidth: undefined
};
percentageBandwidth.begin = dailyFile[i][0];
percentageBandwidth.end = dailyFile[i][1];
percentageBandwidth.value = dailyFile[i][2];
percentageBandwidth.movingAverage = dailyFile[i][3];
percentageBandwidth.bandwidth = dailyFile[i][4];
if (percentageBandwidth.begin >= farLeftDate.valueOf() && percentageBandwidth.end <= farRightDate.valueOf()) {
percentageBandwidthArray.push(percentageBandwidth);
if (datetime.valueOf() >= percentageBandwidth.begin && datetime.valueOf() <= percentageBandwidth.end) {
thisObject.currentPercentageBandwidth = percentageBandwidth;
thisObject.container.eventHandler.raiseEvent("Current Percentage Bandwidth Changed", thisObject.currentPercentageBandwidth);
}
}
}
}
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
}
/* Lests check if all the visible screen is going to be covered by percentageBandwidthArray. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
if (percentageBandwidthArray.length > 0) {
if (percentageBandwidthArray[0].begin > lowerEnd || percentageBandwidthArray[percentageBandwidthArray.length - 1].end < upperEnd) {
setTimeout(recalculate, 2000);
//console.log("File missing while calculating percentageBandwidthArray, scheduling a recalculation in 2 seconds.");
}
}
//console.log("Olivia > recalculateUsingDailyFiles > total percentageBandwidthArray generated : " + percentageBandwidthArray.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, plotAreaFrame);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, plotAreaFrame);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
percentageBandwidthArray = [];
for (let i = 0; i < marketFile.length; i++) {
let percentageBandwidth = {
begin: undefined,
end: undefined,
value: undefined,
movingAverage: undefined,
bandwidth: undefined
};
percentageBandwidth.begin = marketFile[i][0];
percentageBandwidth.end = marketFile[i][1];
percentageBandwidth.value = marketFile[i][2];
percentageBandwidth.movingAverage = marketFile[i][3];
percentageBandwidth.bandwidth = marketFile[i][4];
if (percentageBandwidth.begin >= leftDate.valueOf() && percentageBandwidth.end <= rightDate.valueOf()) {
percentageBandwidthArray.push(percentageBandwidth);
if (datetime.valueOf() >= percentageBandwidth.begin && datetime.valueOf() <= percentageBandwidth.end) {
thisObject.currentPercentageBandwidth = percentageBandwidth;
thisObject.container.eventHandler.raiseEvent("Current Percentage Bandwidth Changed", thisObject.currentPercentageBandwidth);
}
}
}
//console.log("Olivia > recalculateUsingMarketFiles > total percentageBandwidthArray generated : " + percentageBandwidthArray.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function recalculateScaleX() {
try {
var minValue = {
x: MIN_PLOTABLE_DATE.valueOf()
};
var maxValue = {
x: MAX_PLOTABLE_DATE.valueOf()
};
plotAreaViewport.initializeX(
minValue,
maxValue,
thisObject.container.frame.width
);
plotAreaFrame.initializeX(
minValue,
maxValue,
thisObject.container.frame.width
);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScaleX -> err = " + err.stack); }
}
}
function recalculateScaleY() {
try {
var minValue = {
y: 0
};
var maxValue = {
y: 0
};
maxValue.y = 100;
plotAreaViewport.initializeY(
minValue,
maxValue,
canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y
);
plotAreaFrame.initializeY(
minValue,
maxValue,
thisObject.container.frame.height
);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScaleY -> err = " + err.stack); }
}
}
function plotChart() {
try {
let userPosition = getUserPosition()
let userPositionDate = userPosition.point.x
let percentageBandwidth;
let previousBand;
if (percentageBandwidthArray.length > 0) {
/* This next section is to get ready in order to be able to plot dinamically constrained to the viewport */
let visibleHeight = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.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;
let pbChartHeight = 20;
let pbOffset = 100 / 5 * 4;
/* Now we calculate and plot the percentageBandwidthArray */
for (let i = 1; i < percentageBandwidthArray.length; i++) {
percentageBandwidth = percentageBandwidthArray[i];
previousBand = percentageBandwidthArray[i - 1];
/* Here we will draw the percent bandwidth chart */
let currentPercentBandwidth = percentageBandwidth.value * pbChartHeight / 100;
if (currentPercentBandwidth > pbChartHeight) { currentPercentBandwidth = pbChartHeight };
if (currentPercentBandwidth < 0) { currentPercentBandwidth = 0 };
let previousPercentBandwidth = previousBand.value * pbChartHeight / 100;
if (previousPercentBandwidth > pbChartHeight) { previousPercentBandwidth = pbChartHeight };
if (previousPercentBandwidth < 0) { previousPercentBandwidth = 0 };
let currentMovingAverage = percentageBandwidth.movingAverage * pbChartHeight / 100;
let previousMovingAverage = previousBand.movingAverage * pbChartHeight / 100;
let currentBandwidth = percentageBandwidth.bandwidth * 100 * pbChartHeight / 100;
let previousBandwidth = previousBand.bandwidth * 100 * pbChartHeight / 100;
let pbPoint1;
let pbPoint2;
let pbPoint3;
let pbPoint4;
let pbPoint5;
let pbPoint6;
let pbPoint7;
let pbPoint8;
let pbPoint9;
let pbPoint10;
function calculateCoordinates(plot, height) {
pbPoint1 = {
x: percentageBandwidth.begin,
y: previousPercentBandwidth + pbOffset
};
pbPoint2 = {
x: percentageBandwidth.end,
y: currentPercentBandwidth + pbOffset
};
/* Points needed for the lines */
pbPoint3 = {
x: 0,
y: 0 * pbChartHeight / 100 + pbOffset
};
pbPoint4 = {
x: 0,
y: 30 * pbChartHeight / 100 + pbOffset
};
pbPoint5 = {
x: 0,
y: 70 * pbChartHeight / 100 + pbOffset
};
pbPoint6 = {
x: 0,
y: 100 * pbChartHeight / 100 + pbOffset
};
/* Moving Average */
pbPoint7 = {
x: percentageBandwidth.begin,
y: previousMovingAverage + pbOffset
};
pbPoint8 = {
x: percentageBandwidth.end,
y: currentMovingAverage + pbOffset
};
/* Bandwidth */
pbPoint9 = {
x: percentageBandwidth.begin,
y: previousBandwidth + pbOffset
};
pbPoint10 = {
x: percentageBandwidth.end,
y: currentBandwidth + pbOffset
};
pbPoint1 = plot.transformThisPoint(pbPoint1);
pbPoint2 = plot.transformThisPoint(pbPoint2);
pbPoint3 = plot.transformThisPoint(pbPoint3);
pbPoint4 = plot.transformThisPoint(pbPoint4);
pbPoint5 = plot.transformThisPoint(pbPoint5);
pbPoint6 = plot.transformThisPoint(pbPoint6);
pbPoint7 = plot.transformThisPoint(pbPoint7);
pbPoint8 = plot.transformThisPoint(pbPoint8);
pbPoint9 = plot.transformThisPoint(pbPoint9);
pbPoint10 = plot.transformThisPoint(pbPoint10);
pbPoint1 = transformThisPoint(pbPoint1, thisObject.container);
pbPoint2 = transformThisPoint(pbPoint2, thisObject.container);
pbPoint3 = transformThisPoint(pbPoint3, thisObject.container);
pbPoint4 = transformThisPoint(pbPoint4, thisObject.container);
pbPoint5 = transformThisPoint(pbPoint5, thisObject.container);
pbPoint6 = transformThisPoint(pbPoint6, thisObject.container);
pbPoint7 = transformThisPoint(pbPoint7, thisObject.container);
pbPoint8 = transformThisPoint(pbPoint8, thisObject.container);
pbPoint9 = transformThisPoint(pbPoint9, thisObject.container);
pbPoint10 = transformThisPoint(pbPoint10, thisObject.container);
if (pbPoint1.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || pbPoint2.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
return false;
}
return true;
}
if (calculateCoordinates(plotAreaFrame, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area.
if (frameHeightInViewPort > visibleHeight * 2 / 3) {
if (calculateCoordinates(plotAreaViewport, visibleHeight) === false) {
continue;
} // We snap t to the view port.
/* Now we set the real value of y. */
pbPoint1.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (previousPercentBandwidth + pbOffset) * plotAreaViewport.scale.y;
pbPoint2.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (currentPercentBandwidth + pbOffset) * plotAreaViewport.scale.y;
pbPoint3.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (0 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint4.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (30 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint5.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (70 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint6.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (100 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint7.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (previousMovingAverage + pbOffset) * plotAreaViewport.scale.y;
pbPoint8.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (currentMovingAverage + pbOffset) * plotAreaViewport.scale.y;
pbPoint9.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (previousBandwidth + pbOffset) * plotAreaViewport.scale.y;
pbPoint10.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - (currentBandwidth + pbOffset) * plotAreaViewport.scale.y;
}
/* Everything must fit within the visible area */
pbPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint1);
pbPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint2);
pbPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint3);
pbPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint4);
pbPoint5 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint5);
pbPoint6 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint6);
pbPoint7 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint7);
pbPoint8 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint8);
pbPoint9 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint9);
pbPoint10 = canvas.chartingSpace.viewport.fitIntoVisibleArea(pbPoint10);
/* Now the drawing of the lines */
let bgOpacityA = 0.05;
let bgOpacityB = 0.05;
let bgOpacityC = 0.05;
let fgOpacity = '1';
let lineWidthA = 0.2;
let lineWidthB = 0.2;
let lineWidthC = 0.2;
if (pbPoint1.y > pbPoint4.y || pbPoint2.y > pbPoint4.y) {
bgOpacityA = bgOpacityA * 2;
}
if (pbPoint1.y < pbPoint5.y || pbPoint2.y < pbPoint5.y) {
bgOpacityC = bgOpacityC * 2;
}
if (pbPoint1.y >= pbPoint3.y || pbPoint2.y >= pbPoint3.y) {
bgOpacityA = bgOpacityA * 1.5;
lineWidthA = lineWidthA * 2;
}
if (pbPoint1.y <= pbPoint6.y || pbPoint2.y <= pbPoint6.y) {
bgOpacityC = bgOpacityC * 1.5;
lineWidthC = lineWidthC * 2;
}
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint1.x, pbPoint3.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint3.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint4.y);
browserCanvasContext.lineTo(pbPoint1.x, pbPoint4.y);
browserCanvasContext.closePath();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ',' + bgOpacityA + ' )';
browserCanvasContext.fill();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint1.x, pbPoint4.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint4.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint5.y);
browserCanvasContext.lineTo(pbPoint1.x, pbPoint5.y);
browserCanvasContext.closePath();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + bgOpacityB + ')';
browserCanvasContext.fill();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint1.x, pbPoint5.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint5.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint6.y);
browserCanvasContext.lineTo(pbPoint1.x, pbPoint6.y);
browserCanvasContext.closePath();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + bgOpacityC + ')';
browserCanvasContext.fill();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint1.x, pbPoint3.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint3.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + fgOpacity + ')';
browserCanvasContext.lineWidth = lineWidthA;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint1.x, pbPoint4.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint4.y);
browserCanvasContext.moveTo(pbPoint1.x, pbPoint5.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint5.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + fgOpacity + ')';
browserCanvasContext.lineWidth = lineWidthB;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint1.x, pbPoint6.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint6.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + fgOpacity + ')';
browserCanvasContext.lineWidth = lineWidthC;
browserCanvasContext.stroke();
/* Now the drawing of the percentage bandwidth*/
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint1.x, pbPoint1.y);
browserCanvasContext.lineTo(pbPoint2.x, pbPoint2.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', 1)';
if (userPositionDate >= percentageBandwidth.begin && userPositionDate <= percentageBandwidth.end) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)';
}
browserCanvasContext.lineWidth = 0.2;
browserCanvasContext.stroke();
/* Now the drawing of the moving average*/
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint7.x, pbPoint7.y);
browserCanvasContext.lineTo(pbPoint8.x, pbPoint8.y);
browserCanvasContext.closePath();
if (pbPoint7.y > pbPoint8.y) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)';
}
if (userPositionDate >= percentageBandwidth.begin && userPositionDate <= percentageBandwidth.end) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)';
}
browserCanvasContext.lineWidth = 0.2;
browserCanvasContext.stroke();
/* Now the drawing of the bandwidth*/
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(pbPoint9.x, pbPoint9.y);
browserCanvasContext.lineTo(pbPoint10.x, pbPoint10.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', 1)';
if (userPositionDate >= percentageBandwidth.begin && userPositionDate <= percentageBandwidth.end) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)';
}
browserCanvasContext.setLineDash([1, 4])
browserCanvasContext.lineWidth = 1
browserCanvasContext.stroke()
browserCanvasContext.setLineDash([0, 0])
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
}
}
function onViewportPositionChanged(event) {
try {
if (event !== undefined) {
if (event.recalculate === true) {
recalculate()
return
}
}
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,28 @@
{
"displayName": "Plotters Bollinger Bands",
"codeName": "PlottersBollingerBands",
"dataMine": "Masters",
"modules": [
{
"codeName": "BollingerBands",
"moduleName": "BollingerBands",
"description": "Plots Bollinger Bands.",
"profilePicture": "BollingerBands.png",
"panels": [
{
"codeName": "BollingerBandsPanel",
"moduleName": "BollingerBandsPanel",
"event": "Current Record Changed"
}
]
},
{
"codeName": "PercentageBandwidth",
"moduleName": "PercentageBandwidth",
"description": "Plots Bollinger Bands.",
"profilePicture": "PercentageBandwidth.png",
"panels": [
]
}
]
}
@@ -0,0 +1,333 @@
function newMastersPlottersCandlesVolumesCandlesCandlePanel() {
let thisObject = {
fitFunction: undefined,
container: undefined,
isVisible: true,
onRecordChange: onRecordChange,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
};
thisObject.container = newContainer();
thisObject.container.initialize();
thisObject.container.frame.containerName = "Candle";
let currentCandle;
let panelTabButton
return thisObject;
function finalize() {
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.isVisible = undefined
currentCandle = undefined
panelTabButton.finalize()
panelTabButton = undefined
}
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL;
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 2;
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height;
panelTabButton = newPanelTabButton()
panelTabButton.parentContainer = thisObject.container
panelTabButton.container.frame.parentFrame = thisObject.container.frame
panelTabButton.fitFunction = thisObject.fitFunction
panelTabButton.initialize()
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = panelTabButton.getContainer(point)
if (container !== undefined) { return container }
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
let checkPoint = {
x: point.x,
y: point.y
}
checkPoint = thisObject.fitFunction(checkPoint)
if (point.x === checkPoint.x && point.y === checkPoint.y) {
return thisObject.container;
}
}
}
function onRecordChange(lastCurrentCandle) {
currentCandle = lastCurrentCandle;
}
function draw() {
if (thisObject.isVisible !== true) { return }
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
plotCurrentCandleInfo();
panelTabButton.draw()
}
function plotCurrentCandleInfo() {
if (currentCandle === undefined) { return; }
if (currentCandle.innerCandle === undefined) { return; }
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
const X_AXIS = thisObject.container.frame.width / 2;
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
let candlePoint1 = {
x: X_AXIS - currentCandle.bodyWidth / 2,
y: Y_AXIS - currentCandle.bodyHeight / 2
};
let candlePoint2 = {
x: X_AXIS + currentCandle.bodyWidth / 2,
y: Y_AXIS - currentCandle.bodyHeight / 2
};
let candlePoint3 = {
x: X_AXIS + currentCandle.bodyWidth / 2,
y: Y_AXIS + currentCandle.bodyHeight / 2
};
let candlePoint4 = {
x: X_AXIS - currentCandle.bodyWidth / 2,
y: Y_AXIS + currentCandle.bodyHeight / 2
};
let stickPoint1 = {
x: X_AXIS - currentCandle.stickWidth / 2,
y: candlePoint1.y - currentCandle.stickStart
};
let stickPoint2 = {
x: X_AXIS + currentCandle.stickWidth / 2,
y: candlePoint1.y - currentCandle.stickStart
};
let stickPoint3 = {
x: X_AXIS + currentCandle.stickWidth / 2,
y: candlePoint1.y - currentCandle.stickStart + currentCandle.stickHeight
};
let stickPoint4 = {
x: X_AXIS - currentCandle.stickWidth / 2,
y: candlePoint1.y - currentCandle.stickStart + currentCandle.stickHeight
};
/* Extra bounderies due to constrained space */
const upperLimit = frameTitleHeight + frameBodyHeight * 0.15;
const downLimit = frameTitleHeight + frameBodyHeight * 0.85;
if (candlePoint1.y < upperLimit) {
candlePoint1.y = upperLimit;
candlePoint2.y = upperLimit;
}
if (stickPoint1.y < upperLimit) {
stickPoint1.y = upperLimit;
stickPoint2.y = upperLimit;
}
if (candlePoint3.y < upperLimit) {
candlePoint3.y = upperLimit;
candlePoint4.y = upperLimit;
}
if (stickPoint3.y < upperLimit) {
stickPoint3.y = upperLimit;
stickPoint4.y = upperLimit;
}
if (candlePoint1.y > downLimit) {
candlePoint1.y = downLimit;
candlePoint2.y = downLimit;
}
if (stickPoint1.y > downLimit) {
stickPoint1.y = downLimit;
stickPoint2.y = downLimit;
}
if (candlePoint3.y > downLimit) {
candlePoint3.y = downLimit;
candlePoint4.y = downLimit;
}
if (stickPoint3.y > downLimit) {
stickPoint3.y = downLimit;
stickPoint4.y = downLimit;
}
candlePoint1 = thisObject.container.frame.frameThisPoint(candlePoint1);
candlePoint2 = thisObject.container.frame.frameThisPoint(candlePoint2);
candlePoint3 = thisObject.container.frame.frameThisPoint(candlePoint3);
candlePoint4 = thisObject.container.frame.frameThisPoint(candlePoint4);
candlePoint1 = thisObject.fitFunction(candlePoint1)
candlePoint2 = thisObject.fitFunction(candlePoint2)
candlePoint3 = thisObject.fitFunction(candlePoint3)
candlePoint4 = thisObject.fitFunction(candlePoint4)
stickPoint1 = thisObject.container.frame.frameThisPoint(stickPoint1);
stickPoint2 = thisObject.container.frame.frameThisPoint(stickPoint2);
stickPoint3 = thisObject.container.frame.frameThisPoint(stickPoint3);
stickPoint4 = thisObject.container.frame.frameThisPoint(stickPoint4);
stickPoint1 = thisObject.fitFunction(stickPoint1)
stickPoint2 = thisObject.fitFunction(stickPoint2)
stickPoint3 = thisObject.fitFunction(stickPoint3)
stickPoint4 = thisObject.fitFunction(stickPoint4)
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(stickPoint1.x, stickPoint1.y);
browserCanvasContext.lineTo(stickPoint2.x, stickPoint2.y);
browserCanvasContext.lineTo(stickPoint3.x, stickPoint3.y);
browserCanvasContext.lineTo(stickPoint4.x, stickPoint4.y);
browserCanvasContext.closePath();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)';
browserCanvasContext.fill();
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(candlePoint1.x, candlePoint1.y);
browserCanvasContext.lineTo(candlePoint2.x, candlePoint2.y);
browserCanvasContext.lineTo(candlePoint3.x, candlePoint3.y);
browserCanvasContext.lineTo(candlePoint4.x, candlePoint4.y);
browserCanvasContext.closePath();
const OPACITY = '1';
if (currentCandle.innerCandle.direction === 'up') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + OPACITY + ')'; }
if (currentCandle.innerCandle.direction === 'down') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')'; }
if (currentCandle.innerCandle.direction === 'side') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', ' + OPACITY + ')'; }
if (currentCandle.innerCandle.direction === 'up') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')'; }
if (currentCandle.innerCandle.direction === 'down') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')'; }
if (currentCandle.innerCandle.direction === 'side') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + OPACITY + ')'; }
browserCanvasContext.fill();
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
/* put the labels with the candles values */
let y;
printLabel('Max', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '1');
printLabel(currentCandle.innerCandle.max, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '0.50');
printLabel('Min', X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '1');
printLabel(currentCandle.innerCandle.min, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '0.50');
y = Y_AXIS - currentCandle.bodyHeight / 2;
if (y < frameTitleHeight + frameBodyHeight * 0.15) { y = frameTitleHeight + frameBodyHeight * 0.15; }
if (y > frameTitleHeight + frameBodyHeight * 0.80) { y = frameTitleHeight + frameBodyHeight * 0.80; }
printLabel('Open', X_AXIS * 1 / 2, y, '1');
printLabel(currentCandle.innerCandle.open, X_AXIS * 1 / 2, y + frameBodyHeight * 0.05, '0.50');
y = Y_AXIS + currentCandle.bodyHeight / 2;
if (y < frameTitleHeight + frameBodyHeight * 0.15) { y = frameTitleHeight + frameBodyHeight * 0.15; }
if (y > frameTitleHeight + frameBodyHeight * 0.80) { y = frameTitleHeight + frameBodyHeight * 0.80; }
printLabel('Close', X_AXIS * 3 / 2, y, '1');
printLabel(currentCandle.innerCandle.close, X_AXIS * 3 / 2, y + frameBodyHeight * 0.05, '0.50');
function printLabel(labelToPrint, x, y, opacity) {
let labelPoint;
let fontSize = 10;
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira';
let label = '' + labelToPrint;
if (isNaN(label) === false) {
label = Number(label).toLocaleString();
}
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
labelPoint = {
x: x - xOffset,
y: y
};
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint);
labelPoint = thisObject.fitFunction(labelPoint)
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
}
browserCanvasContext.closePath();
browserCanvasContext.fill();
}
}
@@ -0,0 +1,807 @@
function newMastersPlottersCandlesVolumesCandles() {
const MODULE_NAME = "Candles Plotter";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
initialize: initialize,
finalize: finalize,
container: undefined,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
draw: draw,
onDailyFileLoaded: onDailyFileLoaded,
currentCandle: undefined
};
/* this is part of the module template */
thisObject.container = newContainer()
thisObject.container.initialize(MODULE_NAME)
let coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
/* these are module specific variables: */
let candles = []; // Here we keep the candles to be ploted every time the Draw() function is called by the AAWebPlatform.
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
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] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
let newMarketFile = marketFiles.getFile(pTimeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
try {
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
thisObject.container.frame.draw();
return plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Candles Changed", candles);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (fileCursor === undefined) { return; } // We need to wait
if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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());
candles = [];
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 candle = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined,
direction: undefined
};
candle.min = dailyFile[i][0];
candle.max = dailyFile[i][1];
candle.open = dailyFile[i][2];
candle.close = dailyFile[i][3];
candle.begin = dailyFile[i][4];
candle.end = dailyFile[i][5];
if (candle.open > candle.close) { candle.direction = 'down'; }
if (candle.open < candle.close) { candle.direction = 'up'; }
if (candle.open === candle.close) { candle.direction = 'side'; }
/* Contributing to Auto-Scale*/
if (i === 0) {
coordinateSystem.reportXValue(candle.begin)
}
if (i === dailyFile.length - 1) {
coordinateSystem.reportXValue(candle.end)
}
if (
(candle.begin >= farLeftDate.valueOf() && candle.end <= farRightDate.valueOf()) &&
(candle.begin >= coordinateSystem.min.x && candle.end <= coordinateSystem.max.x)
) {
candles.push(candle);
if (datetime.valueOf() >= candle.begin && datetime.valueOf() <= candle.end) {
thisObject.currentCandle = candle;
thisObject.container.eventHandler.raiseEvent("Current Record 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 candles. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
if (candles.length > 0) {
if (candles[0].begin > lowerEnd || candles[candles.length - 1].end < upperEnd) {
setTimeout(recalculate, 2000);
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
candles = [];
for (let i = 0; i < marketFile.length; i++) {
let candle = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined,
direction: undefined
};
candle.min = marketFile[i][0];
candle.max = marketFile[i][1];
candle.open = marketFile[i][2];
candle.close = marketFile[i][3];
candle.begin = marketFile[i][4];
candle.end = marketFile[i][5];
if (candle.open > candle.close) { candle.direction = 'down'; }
if (candle.open < candle.close) { candle.direction = 'up'; }
if (candle.open === candle.close) { candle.direction = 'side'; }
/* Contributing to Auto-Scale*/
if (i === 0) {
coordinateSystem.reportXValue(candle.begin)
}
if (i === marketFile.length - 1) {
coordinateSystem.reportXValue(candle.end)
}
if (
(candle.begin >= leftDate.valueOf() && candle.end <= rightDate.valueOf()) &&
(candle.begin >= coordinateSystem.min.x && candle.end <= coordinateSystem.max.x)
)
{
candles.push(candle);
if (datetime.valueOf() >= candle.begin && datetime.valueOf() <= candle.end) {
thisObject.currentCandle = candle;
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentCandle);
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
let elementsPlotted = 0
let lowResolution = false
if (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_PLOTTING_IN_LOW_RESOLUTION) {
if (candles.length > 100) {
lowResolution = true
}
}
if (candles.length > 0) {
/* Now we calculate all the points. */
for (let i = 0; i < candles.length; i++) {
candle = candles[i];
candle.candlePoint1 = {
x: candle.begin + timeFrame / 7 * 1.5,
y: candle.open
};
candle.candlePoint2 = {
x: candle.begin + timeFrame / 7 * 5.5,
y: candle.open
};
candle.candlePoint3 = {
x: candle.begin + timeFrame / 7 * 5.5,
y: candle.close
};
candle.candlePoint4 = {
x: candle.begin + timeFrame / 7 * 1.5,
y: candle.close
};
candle.candlePoint1 = coordinateSystem.transformThisPoint(candle.candlePoint1);
candle.candlePoint2 = coordinateSystem.transformThisPoint(candle.candlePoint2);
candle.candlePoint3 = coordinateSystem.transformThisPoint(candle.candlePoint3);
candle.candlePoint4 = coordinateSystem.transformThisPoint(candle.candlePoint4);
candle.candlePoint1 = transformThisPoint(candle.candlePoint1, thisObject.container);
candle.candlePoint2 = transformThisPoint(candle.candlePoint2, thisObject.container);
candle.candlePoint3 = transformThisPoint(candle.candlePoint3, thisObject.container);
candle.candlePoint4 = transformThisPoint(candle.candlePoint4, thisObject.container);
candle.stickPoint1 = {
x: candle.begin + timeFrame / 7 * 3.2,
y: candle.max
};
candle.stickPoint2 = {
x: candle.begin + timeFrame / 7 * 3.8,
y: candle.max
};
candle.stickPoint3 = {
x: candle.begin + timeFrame / 7 * 3.8,
y: candle.min
};
candle.stickPoint4 = {
x: candle.begin + timeFrame / 7 * 3.2,
y: candle.min
};
candle.stickPoint1 = coordinateSystem.transformThisPoint(candle.stickPoint1);
candle.stickPoint2 = coordinateSystem.transformThisPoint(candle.stickPoint2);
candle.stickPoint3 = coordinateSystem.transformThisPoint(candle.stickPoint3);
candle.stickPoint4 = coordinateSystem.transformThisPoint(candle.stickPoint4);
candle.stickPoint1 = transformThisPoint(candle.stickPoint1, thisObject.container);
candle.stickPoint2 = transformThisPoint(candle.stickPoint2, thisObject.container);
candle.stickPoint3 = transformThisPoint(candle.stickPoint3, thisObject.container);
candle.stickPoint4 = transformThisPoint(candle.stickPoint4, thisObject.container);
if (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT) {
let diffA = candle.stickPoint3.y - canvas.chartingSpace.viewport.visibleArea.bottomLeft.y
if (diffA > 0) {
candle.candlePoint1.y = candle.candlePoint1.y - diffA
candle.candlePoint2.y = candle.candlePoint2.y - diffA
candle.candlePoint3.y = candle.candlePoint3.y - diffA
candle.candlePoint4.y = candle.candlePoint4.y - diffA
candle.stickPoint1.y = candle.stickPoint1.y - diffA
candle.stickPoint2.y = candle.stickPoint2.y - diffA
candle.stickPoint3.y = candle.stickPoint3.y - diffA
candle.stickPoint4.y = candle.stickPoint4.y - diffA
}
let diffB = candle.stickPoint1.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y
if (diffB < 0) {
candle.candlePoint1.y = candle.candlePoint1.y - diffB
candle.candlePoint2.y = candle.candlePoint2.y - diffB
candle.candlePoint3.y = candle.candlePoint3.y - diffB
candle.candlePoint4.y = candle.candlePoint4.y - diffB
candle.stickPoint1.y = candle.stickPoint1.y - diffB
candle.stickPoint2.y = candle.stickPoint2.y - diffB
candle.stickPoint3.y = candle.stickPoint3.y - diffB
candle.stickPoint4.y = candle.stickPoint4.y - diffB
}
}
candle.candlePoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint1);
candle.candlePoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint2);
candle.candlePoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint3);
candle.candlePoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint4);
candle.candlePoint1 = thisObject.fitFunction(candle.candlePoint1);
candle.candlePoint2 = thisObject.fitFunction(candle.candlePoint2);
candle.candlePoint3 = thisObject.fitFunction(candle.candlePoint3);
candle.candlePoint4 = thisObject.fitFunction(candle.candlePoint4);
candle.stickPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint1);
candle.stickPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint2);
candle.stickPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint3);
candle.stickPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint4);
candle.stickPoint1 = thisObject.fitFunction(candle.stickPoint1);
candle.stickPoint2 = thisObject.fitFunction(candle.stickPoint2);
candle.stickPoint3 = thisObject.fitFunction(candle.stickPoint3);
candle.stickPoint4 = thisObject.fitFunction(candle.stickPoint4);
}
/* On screen candles */
let onScreenCandles = []
let mouseCandle
for (let i = 0; i < candles.length; i++) {
candle = candles[i];
if (candle.candlePoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || candle.candlePoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue
}
/* Contributing to Auto-Scale*/
coordinateSystem.reportYValue(candle.max)
coordinateSystem.reportYValue(candle.min)
//if (onScreenCandles.length < 15000) {
onScreenCandles.push(candle)
//}
if (userPositionDate >= candle.begin && userPositionDate <= candle.end) {
mouseCandle = candle
}
}
/* We draw the sticks */
browserCanvasContext.beginPath();
for (let i = 0; i < onScreenCandles.length; i++) {
candle = onScreenCandles[i];
browserCanvasContext.moveTo(candle.stickPoint1.x, candle.stickPoint1.y);
browserCanvasContext.lineTo(candle.stickPoint2.x, candle.stickPoint2.y);
browserCanvasContext.lineTo(candle.stickPoint3.x, candle.stickPoint3.y);
browserCanvasContext.lineTo(candle.stickPoint4.x, candle.stickPoint4.y);
}
browserCanvasContext.closePath();
if (lowResolution === false) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)';
browserCanvasContext.fill();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.LIGHT + ', 1)';
browserCanvasContext.lineWidth = 1;
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
/* The stick at the mouse candle */
if (mouseCandle) {
browserCanvasContext.beginPath();
candle = mouseCandle
browserCanvasContext.moveTo(candle.stickPoint1.x, candle.stickPoint1.y);
browserCanvasContext.lineTo(candle.stickPoint2.x, candle.stickPoint2.y);
browserCanvasContext.lineTo(candle.stickPoint3.x, candle.stickPoint3.y);
browserCanvasContext.lineTo(candle.stickPoint4.x, candle.stickPoint4.y);
browserCanvasContext.closePath();
if (lowResolution === false) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)';
browserCanvasContext.fill();
}
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)';
browserCanvasContext.lineWidth = 1;
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
/* We draw the candles. */
drawCandlesByDirection('up')
drawCandlesByDirection('down')
drawCandlesByDirection('side')
function drawCandlesByDirection(direction) {
browserCanvasContext.beginPath();
for (let i = 0; i < onScreenCandles.length; i++) {
candle = onScreenCandles[i];
if (candle.direction !== direction) { continue }
browserCanvasContext.moveTo(candle.candlePoint1.x, candle.candlePoint1.y);
browserCanvasContext.lineTo(candle.candlePoint2.x, candle.candlePoint2.y);
browserCanvasContext.lineTo(candle.candlePoint3.x, candle.candlePoint3.y);
browserCanvasContext.lineTo(candle.candlePoint4.x, candle.candlePoint4.y);
browserCanvasContext.lineTo(candle.candlePoint1.x, candle.candlePoint1.y);
elementsPlotted++
}
browserCanvasContext.closePath();
if (lowResolution === false) {
if (direction === 'up') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', 1)'; }
if (direction === 'down') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)'; }
if (direction === 'side') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'; }
browserCanvasContext.fill();
}
if (direction === 'up') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'; }
if (direction === 'down') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', 1)'; }
if (direction === 'side') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'; }
browserCanvasContext.lineWidth = 1;
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
/* Draw mouse candel and Raise Event. */
if (mouseCandle) {
/* highlight the current candle */
browserCanvasContext.beginPath();
candle = mouseCandle
browserCanvasContext.moveTo(candle.candlePoint1.x, candle.candlePoint1.y);
browserCanvasContext.lineTo(candle.candlePoint2.x, candle.candlePoint2.y);
browserCanvasContext.lineTo(candle.candlePoint3.x, candle.candlePoint3.y);
browserCanvasContext.lineTo(candle.candlePoint4.x, candle.candlePoint4.y);
browserCanvasContext.closePath();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current candle accroding to time
browserCanvasContext.fill();
let currentCandle = {
bodyWidth: candle.candlePoint2.x - candle.candlePoint1.x,
bodyHeight: candle.candlePoint3.y - candle.candlePoint2.y,
stickHeight: candle.stickPoint4.y - candle.stickPoint2.y,
stickWidth: candle.stickPoint2.x - candle.stickPoint1.x,
stickStart: candle.candlePoint2.y - candle.stickPoint2.y,
period: timeFrame,
innerCandle: candle
};
thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentCandle);
}
}
return elementsPlotted
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
}
}
function onViewportPositionChanged(event) {
try {
if (event !== undefined) {
if (event.recalculate === true) {
recalculate()
return
}
}
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,254 @@
function newMastersPlottersCandlesVolumesVolumesVolumePanel() {
let thisObject = {
fitFunction: undefined,
container: undefined,
isVisible: true,
onRecordChange: onRecordChange,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
};
thisObject.container = newContainer();
thisObject.container.initialize();
thisObject.container.frame.containerName = "Volume";
let currentVolume;
let panelTabButton
return thisObject;
function finalize() {
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.isVisible = undefined
currentVolume = undefined
panelTabButton.finalize()
panelTabButton = undefined
}
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL;
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width;
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height;
panelTabButton = newPanelTabButton()
panelTabButton.parentContainer = thisObject.container
panelTabButton.container.frame.parentFrame = thisObject.container.frame
panelTabButton.fitFunction = thisObject.fitFunction
panelTabButton.initialize()
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = panelTabButton.getContainer(point)
if (container !== undefined) { return container }
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
let checkPoint = {
x: point.x,
y: point.y
}
checkPoint = thisObject.fitFunction(checkPoint)
if (point.x === checkPoint.x && point.y === checkPoint.y) {
return thisObject.container;
}
}
}
function onRecordChange(lastCurrentVolume) {
currentVolume = lastCurrentVolume;
}
function draw() {
if (thisObject.isVisible !== true) { return }
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
plotCurrentVolumeInfo();
panelTabButton.draw()
}
function plotCurrentVolumeInfo() {
if (currentVolume === undefined) { return; }
if (currentVolume.innerVolumeBar === undefined) { return; }
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
const X_AXIS = thisObject.container.frame.width / 2;
const Y_AXIS_BUY = frameTitleHeight + frameBodyHeight * 0.85;
const Y_AXIS_SELL = frameTitleHeight + frameBodyHeight * 0.15;
let buyVolumePoint1 = {
x: X_AXIS - currentVolume.buyInfo.baseWidth / 2,
y: Y_AXIS_BUY
};
let buyVolumePoint2 = {
x: X_AXIS - currentVolume.buyInfo.topWidth / 2,
y: Y_AXIS_BUY + currentVolume.buyInfo.height
};
let buyVolumePoint3 = {
x: X_AXIS + currentVolume.buyInfo.topWidth / 2,
y: Y_AXIS_BUY + currentVolume.buyInfo.height
};
let buyVolumePoint4 = {
x: X_AXIS + currentVolume.buyInfo.baseWidth / 2,
y: Y_AXIS_BUY
};
buyVolumePoint1 = thisObject.container.frame.fitIntoFrame(buyVolumePoint1);
buyVolumePoint2 = thisObject.container.frame.fitIntoFrame(buyVolumePoint2);
buyVolumePoint3 = thisObject.container.frame.fitIntoFrame(buyVolumePoint3);
buyVolumePoint4 = thisObject.container.frame.fitIntoFrame(buyVolumePoint4);
buyVolumePoint1 = thisObject.container.frame.frameThisPoint(buyVolumePoint1);
buyVolumePoint2 = thisObject.container.frame.frameThisPoint(buyVolumePoint2);
buyVolumePoint3 = thisObject.container.frame.frameThisPoint(buyVolumePoint3);
buyVolumePoint4 = thisObject.container.frame.frameThisPoint(buyVolumePoint4);
buyVolumePoint1 = thisObject.fitFunction(buyVolumePoint1)
buyVolumePoint2 = thisObject.fitFunction(buyVolumePoint2)
buyVolumePoint3 = thisObject.fitFunction(buyVolumePoint3)
buyVolumePoint4 = thisObject.fitFunction(buyVolumePoint4)
let sellVolumePoint1 = {
x: X_AXIS - currentVolume.buyInfo.baseWidth / 2,
y: Y_AXIS_SELL
};
let sellVolumePoint2 = {
x: X_AXIS - currentVolume.buyInfo.topWidth / 2,
y: Y_AXIS_SELL + currentVolume.sellInfo.height
};
let sellVolumePoint3 = {
x: X_AXIS + currentVolume.buyInfo.topWidth / 2,
y: Y_AXIS_SELL + currentVolume.sellInfo.height
};
let sellVolumePoint4 = {
x: X_AXIS + currentVolume.buyInfo.baseWidth / 2,
y: Y_AXIS_SELL
};
sellVolumePoint1 = thisObject.container.frame.fitIntoFrame(sellVolumePoint1);
sellVolumePoint2 = thisObject.container.frame.fitIntoFrame(sellVolumePoint2);
sellVolumePoint3 = thisObject.container.frame.fitIntoFrame(sellVolumePoint3);
sellVolumePoint4 = thisObject.container.frame.fitIntoFrame(sellVolumePoint4);
sellVolumePoint1 = thisObject.container.frame.frameThisPoint(sellVolumePoint1);
sellVolumePoint2 = thisObject.container.frame.frameThisPoint(sellVolumePoint2);
sellVolumePoint3 = thisObject.container.frame.frameThisPoint(sellVolumePoint3);
sellVolumePoint4 = thisObject.container.frame.frameThisPoint(sellVolumePoint4);
sellVolumePoint1 = thisObject.fitFunction(sellVolumePoint1)
sellVolumePoint2 = thisObject.fitFunction(sellVolumePoint2)
sellVolumePoint3 = thisObject.fitFunction(sellVolumePoint3)
sellVolumePoint4 = thisObject.fitFunction(sellVolumePoint4)
const OPACITY = '0.40';
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(buyVolumePoint1.x, buyVolumePoint1.y);
browserCanvasContext.lineTo(buyVolumePoint2.x, buyVolumePoint2.y);
browserCanvasContext.lineTo(buyVolumePoint3.x, buyVolumePoint3.y);
browserCanvasContext.lineTo(buyVolumePoint4.x, buyVolumePoint4.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + OPACITY + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')';
browserCanvasContext.fill();
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(sellVolumePoint1.x, sellVolumePoint1.y);
browserCanvasContext.lineTo(sellVolumePoint2.x, sellVolumePoint2.y);
browserCanvasContext.lineTo(sellVolumePoint3.x, sellVolumePoint3.y);
browserCanvasContext.lineTo(sellVolumePoint4.x, sellVolumePoint4.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')';
browserCanvasContext.fill();
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
/* put the labels with the volumes values */
browserCanvasContext.beginPath();
printLabel('Buy Volume', X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '1');
printLabel(currentVolume.innerVolumeBar.amountBuy, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '0.50');
printLabel('Sell Volume', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '1');
printLabel(currentVolume.innerVolumeBar.amountSell, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '0.50');
function printLabel(labelToPrint, x, y, opacity) {
let labelPoint;
let fontSize = 10;
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira';
let label = '' + labelToPrint;
if (isNaN(label) === false) {
label = Number(label).toLocaleString();
}
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
labelPoint = {
x: x - xOffset,
y: y
};
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint);
labelPoint = thisObject.fitFunction(labelPoint)
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
}
browserCanvasContext.closePath();
browserCanvasContext.fill();
}
}
@@ -0,0 +1,756 @@
function newMastersPlottersCandlesVolumesVolumes() {
const MODULE_NAME = "Volumes Plotter";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Main functions and properties.
initialize: initialize,
finalize: finalize,
container: undefined,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
recalculateScale: recalculateScale,
draw: draw
};
/* this is part of the module template */
thisObject.container = newContainer()
thisObject.container.initialize(MODULE_NAME)
let coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
let scaleFile; // This file is used to calculate the scale.
/* 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.
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
scaleFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
function recalculateScale() {
recalculate();
}
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] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
let newMarketFile = marketFiles.getFile(pTimeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
}
function draw() {
try {
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
thisObject.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Volumes Changed", volumes);
}
function recalculateUsingDailyFiles() {
try {
if (fileCursor === undefined) { return; } // We need to wait
if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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()) &&
(volume.begin >= coordinateSystem.min.x && volume.end <= coordinateSystem.max.x)
) {
volumes.push(volume);
if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) {
thisObject.container.eventHandler.raiseEvent("Current Record 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);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (marketFile === undefined) { return; } // Initialization not complete yet.
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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()) &&
(volume.begin >= coordinateSystem.min.x && volume.end <= coordinateSystem.max.x)
) {
volumes.push(volume);
if (datetime.valueOf() >= volume.begin && datetime.valueOf() <= volume.end) {
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentCandle);
}
}
}
//console.log("Olivia > recalculateUsingMarketFiles > total volumes generated : " + volumes.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
let lowResolution = false
if (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_PLOTTING_IN_LOW_RESOLUTION) {
if (volumes.length > 100) {
lowResolution = true
}
}
if (volumes.length > 0) {
/* Now we calculate and plot the volumes */
let visibleHeight = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.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 (let 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 + timeFrame / 7 * 2,
y: 0
};
volumePointA2 = {
x: volume.begin + timeFrame / 7 * 2,
y: volume.amountBuy
};
volumePointA3 = {
x: volume.begin + timeFrame / 7 * 5,
y: volume.amountBuy
};
volumePointA4 = {
x: volume.begin + timeFrame / 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 < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || volumePointA1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
return false;
}
return true;
}
if (calculateBuys(coordinateSystem, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area.
let diffA = volumePointA1.y - canvas.chartingSpace.viewport.visibleArea.bottomLeft.y
if (diffA > 0) {
volumePointA1.y = volumePointA1.y - diffA
volumePointA2.y = volumePointA2.y - diffA
volumePointA3.y = volumePointA3.y - diffA
volumePointA4.y = volumePointA4.y - diffA
}
let volumePointB1;
let volumePointB2;
let volumePointB3;
let volumePointB4;
function calculateSells(plot, height) {
volumePointB1 = {
x: volume.begin + timeFrame / 7 * 2,
y: height
};
volumePointB2 = {
x: volume.begin + timeFrame / 7 * 2,
y: height - volume.amountSell
};
volumePointB3 = {
x: volume.begin + timeFrame / 7 * 5,
y: height - volume.amountSell
};
volumePointB4 = {
x: volume.begin + timeFrame / 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(coordinateSystem, thisObject.container.frame.height); // We try to see if it fits in the visible area.
let diffB = volumePointB1.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y
if (diffB < 0) {
volumePointB1.y = volumePointB1.y - diffB
volumePointB2.y = volumePointB2.y - diffB
volumePointB3.y = volumePointB3.y - diffB
volumePointB4.y = volumePointB4.y - diffB
}
/* 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 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA1);
volumePointA2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA2);
volumePointA3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA3);
volumePointA4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointA4);
volumePointB1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB1);
volumePointB2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB2);
volumePointB3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB3);
volumePointB4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumePointB4);
volumePointA1 = thisObject.fitFunction(volumePointA1);
volumePointA2 = thisObject.fitFunction(volumePointA2);
volumePointA3 = thisObject.fitFunction(volumePointA3);
volumePointA4 = thisObject.fitFunction(volumePointA4);
volumePointB1 = thisObject.fitFunction(volumePointB1);
volumePointB2 = thisObject.fitFunction(volumePointB2);
volumePointB3 = thisObject.fitFunction(volumePointB3);
volumePointB4 = thisObject.fitFunction(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 (lowResolution === false) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', 0.40)';
if (userPositionDate >= volume.begin && userPositionDate <= volume.end) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.40)'; // Current bar accroding to time
}
}
browserCanvasContext.fill();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 0.40)';
browserCanvasContext.lineWidth = 1;
browserCanvasContext.setLineDash([0, 0])
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 (lowResolution === false) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 0.40)';
if (userPositionDate >= volume.begin && userPositionDate <= volume.end) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.40)'; // Current volume accroding to time
}
browserCanvasContext.fill();
}
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', 0.40)';
browserCanvasContext.lineWidth = 1;
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
if (userPositionDate >= volume.begin && userPositionDate <= 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: timeFrame,
innerVolumeBar: volume
};
thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentVolume);
}
/* Contributing to Auto-Scale*/
coordinateSystem.reportYValue(volume.amountBuy * 5)
coordinateSystem.reportYValue(volume.amountSell * 5)
coordinateSystem.reportYValue(0)
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
recalculate()
}
function onViewportPositionChanged(event) {
try {
if (event !== undefined) {
if (event.recalculate === true) {
recalculate();
return
}
}
if (Math.random() * 100 > 95) {
recalculate();
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
recalculate();
}
}
@@ -0,0 +1,33 @@
{
"displayName": "Plotters Candles & Volumes",
"codeName": "PlottersCandlesVolumes",
"dataMine": "Masters",
"modules": [
{
"codeName": "Candles",
"moduleName": "Candles",
"description": "Plots traditional red and green candles.",
"profilePicture": "Candles.png",
"panels": [
{
"codeName": "CandlePanel",
"moduleName": "CandlePanel",
"event": "Current Record Changed"
}
]
},
{
"codeName": "Volumes",
"description": "Plots volumes separated in buy and sell.",
"moduleName": "Volumes",
"profilePicture": "Volumes.png",
"panels": [
{
"codeName": "VolumePanel",
"moduleName": "VolumePanel",
"event": "Current Record Changed"
}
]
}
]
}
@@ -0,0 +1,679 @@
function newMastersPlottersStairsPatternsCandleStairs() {
const MODULE_NAME = "Masters Plotters Stairs Patterns Candle Stairs";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Main functions and properties.
container: undefined,
initialize: initialize,
finalize: finalize,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
draw: draw,
// Secondary functions and properties.
currentStair: undefined
};
/* 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 coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
/* these are module specific variables: */
let stairsArray = []; // Here we keep the candle-stairs to be ploted every time the Draw() function is called by the AAWebPlatform.
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err.message = " + err.message); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE);
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
function getContainer(point) {
try {
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;
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
let newMarketFile = marketFiles.getFile(pTimeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
try {
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
if (INTENSIVE_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); }
this.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("CandleStairs Changed", stairsArray);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (fileCursor === undefined) { return; } // We need to wait
if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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());
stairsArray = [];
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 stairs = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined,
direction: undefined,
candleCount: 0,
firstMin: 0,
firstMax: 0,
lastMin: 0,
lastMax: 0
};
stairs.open = dailyFile[i][0];
stairs.close = dailyFile[i][1];
stairs.min = dailyFile[i][2];
stairs.max = dailyFile[i][3];
stairs.begin = dailyFile[i][4];
stairs.end = dailyFile[i][5];
stairs.direction = dailyFile[i][6];
stairs.candleCount = dailyFile[i][7];
stairs.firstMin = dailyFile[i][8];
stairs.firstMax = dailyFile[i][9];
stairs.lastMin = dailyFile[i][10];
stairs.lastMax = dailyFile[i][11];
if (
(stairs.begin >= farLeftDate.valueOf() && stairs.end <= farRightDate.valueOf()) &&
(stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x)
) {
stairsArray.push(stairs);
if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.end) {
thisObject.currentStair = stairs;
thisObject.container.eventHandler.raiseEvent("Current Candle-Stairs Changed", thisObject.currentStair);
}
}
}
}
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
}
/* Lests check if all the visible screen is going to be covered by candle-stairs. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
if (stairsArray.length > 0) {
if (stairsArray[0].begin > lowerEnd || stairsArray[stairsArray.length - 1].end < upperEnd) {
setTimeout(recalculate, 2000);
//console.log("File missing while calculating candle-stairs, scheduling a recalculation in 2 seconds.");
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
stairsArray = [];
for (let i = 0; i < marketFile.length; i++) {
let stairs = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined,
direction: undefined,
candleCount: 0,
firstMin: 0,
firstMax: 0,
lastMin: 0,
lastMax: 0
};
stairs.open = marketFile[i][0];
stairs.close = marketFile[i][1];
stairs.min = marketFile[i][2];
stairs.max = marketFile[i][3];
stairs.begin = marketFile[i][4];
stairs.end = marketFile[i][5];
stairs.direction = marketFile[i][6];
stairs.candleCount = marketFile[i][7];
stairs.firstMin = marketFile[i][8];
stairs.firstMax = marketFile[i][9];
stairs.lastMin = marketFile[i][10];
stairs.lastMax = marketFile[i][11];
if (
(stairs.begin >= leftDate.valueOf() && stairs.end <= rightDate.valueOf()) &&
(stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x)
) {
stairsArray.push(stairs);
if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.end) {
thisObject.currentStair = stairs;
thisObject.container.eventHandler.raiseEvent("Current Candle-Stairs Changed", thisObject.currentStair);
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
if (stairsArray.length > 0) {
for (var i = 0; i < stairsArray.length; i++) {
stairs = stairsArray[i];
let stairsPoint1;
let stairsPoint2;
let stairsPoint3;
let stairsPoint4;
if (stairs.direction === 'up') {
stairsPoint1 = {
x: stairs.begin + timeFrame / 7 * 5.5,
y: stairs.firstMin
};
stairsPoint2 = {
x: stairs.end - timeFrame / 7 * 1.5,
y: stairs.lastMin
};
stairsPoint3 = {
x: stairs.end - timeFrame / 7 * 5.5,
y: stairs.lastMax
};
stairsPoint4 = {
x: stairs.begin + timeFrame / 7 * 1.5,
y: stairs.firstMax
};
} else {
stairsPoint1 = {
x: stairs.begin + timeFrame / 7 * 1.5,
y: stairs.firstMin
};
stairsPoint2 = {
x: stairs.end - timeFrame / 7 * 5.5,
y: stairs.lastMin
};
stairsPoint3 = {
x: stairs.end - timeFrame / 7 * 1.5,
y: stairs.lastMax
};
stairsPoint4 = {
x: stairs.begin + timeFrame / 7 * 5.5,
y: stairs.firstMax
};
}
stairsPoint1 = coordinateSystem.transformThisPoint(stairsPoint1);
stairsPoint2 = coordinateSystem.transformThisPoint(stairsPoint2);
stairsPoint3 = coordinateSystem.transformThisPoint(stairsPoint3);
stairsPoint4 = coordinateSystem.transformThisPoint(stairsPoint4);
stairsPoint1 = transformThisPoint(stairsPoint1, thisObject.container);
stairsPoint2 = transformThisPoint(stairsPoint2, thisObject.container);
stairsPoint3 = transformThisPoint(stairsPoint3, thisObject.container);
stairsPoint4 = transformThisPoint(stairsPoint4, thisObject.container);
if (stairsPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || stairsPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue;
}
stairsPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint1);
stairsPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint2);
stairsPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint3);
stairsPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(stairsPoint4);
stairsPoint1 = thisObject.fitFunction(stairsPoint1);
stairsPoint2 = thisObject.fitFunction(stairsPoint2);
stairsPoint3 = thisObject.fitFunction(stairsPoint3);
stairsPoint4 = thisObject.fitFunction(stairsPoint4);
/* Contributing to Auto-Scale*/
coordinateSystem.reportYValue(stairs.firstMin)
coordinateSystem.reportYValue(stairs.firstMax)
coordinateSystem.reportYValue(stairs.lastMin)
coordinateSystem.reportYValue(stairs.lastMax)
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(stairsPoint1.x, stairsPoint1.y);
browserCanvasContext.lineTo(stairsPoint2.x, stairsPoint2.y);
browserCanvasContext.lineTo(stairsPoint3.x, stairsPoint3.y);
browserCanvasContext.lineTo(stairsPoint4.x, stairsPoint4.y);
browserCanvasContext.closePath();
let opacity = '0.25';
if (stairs.direction === 'up') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'; }
if (stairs.direction === 'down') { browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')'; }
if (stairs.direction === 'up') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')'; }
if (stairs.direction === 'down') { browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'; }
if (userPositionDate >= stairs.begin && userPositionDate <= stairs.end) {
/* highlight the current stairs */
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.1)'; // Current stairs accroding to time
}
browserCanvasContext.fill();
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
function onViewportPositionChanged(event) {
try {
if (event !== undefined) {
if (event.recalculate === true) {
recalculate()
return
}
}
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,752 @@
function newMastersPlottersStairsPatternsVolumeStairs() {
const MODULE_NAME = "Masters Plotters Stairs Patterns Volume Stairs";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Main functions and properties.
container: undefined,
initialize: initialize,
finalize: finalize,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
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 coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
let scaleFile; // This file is used to calculate the scale.
/* these are module specific variables: */
let stairsArray = []; // Here we keep the stairsArray to be ploted every time the Draw() function is called by the AAWebPlatform.
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err.message = " + err.message); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
scaleFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE);
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
function getContainer(point) {
try {
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;
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
let newMarketFile = marketFiles.getFile(pTimeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
try {
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
this.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Volumes Changed", stairsArray);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (fileCursor === undefined) { return; } // We need to wait
if (fileCursor.files.size === 0) { return; } // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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());
stairsArray = [];
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 stairs = {
type: undefined,
begin: undefined,
end: undefined,
direction: undefined,
barsCount: 0,
firstAmount: 0,
lastAmount: 0
};
stairs.type = dailyFile[i][0];
stairs.begin = dailyFile[i][1];
stairs.end = dailyFile[i][2];
stairs.direction = dailyFile[i][3];
stairs.barsCount = dailyFile[i][4];
stairs.firstAmount = dailyFile[i][5];
stairs.lastAmount = dailyFile[i][6];
if (
(stairs.begin >= farLeftDate.valueOf() && stairs.end <= farRightDate.valueOf()) &&
(stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x)
) {
stairsArray.push(stairs);
if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.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 stairsArray. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
if (stairsArray.length > 0) {
if (stairsArray[0].begin > lowerEnd || stairsArray[stairsArray.length - 1].end < upperEnd) {
setTimeout(recalculate, 2000);
//console.log("File missing while calculating stairsArray, scheduling a recalculation in 2 seconds.");
}
}
//console.log("Olivia > recalculateUsingDailyFiles > total stairsArray generated : " + stairsArray.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
stairsArray = [];
for (let i = 0; i < marketFile.length; i++) {
let stairs = {
type: undefined,
begin: undefined,
end: undefined,
direction: undefined,
barsCount: 0,
firstAmount: 0,
lastAmount: 0
};
stairs.type = marketFile[i][0];
stairs.begin = marketFile[i][1];
stairs.end = marketFile[i][2];
stairs.direction = marketFile[i][3];
stairs.barsCount = marketFile[i][4];
stairs.firstAmount = marketFile[i][5];
stairs.lastAmount = marketFile[i][6];
if (
(stairs.begin >= leftDate.valueOf() && stairs.end <= rightDate.valueOf()) &&
(stairs.begin >= coordinateSystem.min.x && stairs.end <= coordinateSystem.max.x)
) {
stairsArray.push(stairs);
if (datetime.valueOf() >= stairs.begin && datetime.valueOf() <= stairs.end) {
thisObject.container.eventHandler.raiseEvent("Current Volume-Stairs Changed", thisObject.currentCandle);
}
}
}
//console.log("Olivia > recalculateUsingMarketFiles > total stairsArray generated : " + stairsArray.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
let opacity = '0.25';
let visibleHeight = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.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 (stairsArray.length > 0) {
for (var i = 0; i < stairsArray.length; i++) {
stairs = stairsArray[i];
let volumeBarPointA1;
let volumeBarPointA2;
let volumeBarPointA3;
let volumeBarPointA4;
if (stairs.type === 'buy') {
function calculateBuys(plot, height) {
/* Contributing to Auto-Scale*/
coordinateSystem.reportYValue(stairs.firstAmount * 2)
coordinateSystem.reportYValue(stairs.lastAmount * 2)
volumeBarPointA1 = {
x: stairs.begin + timeFrame / 2,
y: 0
};
volumeBarPointA2 = {
x: stairs.begin + timeFrame / 2,
y: stairs.firstAmount * 2
};
volumeBarPointA3 = {
x: stairs.end - timeFrame / 2,
y: stairs.lastAmount * 2
};
volumeBarPointA4 = {
x: stairs.end - timeFrame / 2,
y: 0
};
volumeBarPointA1 = plot.transformThisPoint(volumeBarPointA1);
volumeBarPointA2 = plot.transformThisPoint(volumeBarPointA2);
volumeBarPointA3 = plot.transformThisPoint(volumeBarPointA3);
volumeBarPointA4 = plot.transformThisPoint(volumeBarPointA4);
volumeBarPointA1 = transformThisPoint(volumeBarPointA1, thisObject.container);
volumeBarPointA2 = transformThisPoint(volumeBarPointA2, thisObject.container);
volumeBarPointA3 = transformThisPoint(volumeBarPointA3, thisObject.container);
volumeBarPointA4 = transformThisPoint(volumeBarPointA4, thisObject.container);
if (volumeBarPointA4.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || volumeBarPointA1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
return false;
}
return true;
}
if (calculateBuys(coordinateSystem, thisObject.container.frame.height) === false) { continue; } // We try to see if it fits in the visible area.
if (volumeBarPointA1.y > canvas.chartingSpace.viewport.visibleArea.bottomLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) {
if (calculateBuys(coordinateSystem, visibleHeight) === false) { continue; } // We snap t to the view port.
/* Now we set the real value of y. */
volumeBarPointA1.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y;
volumeBarPointA2.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - stairs.firstAmount * 2 * coordinateSystem.scale.y;
volumeBarPointA3.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - stairs.lastAmount * 2 * coordinateSystem.scale.y;
volumeBarPointA4.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y;
}
}
let volumeBarPointB1;
let volumeBarPointB2;
let volumeBarPointB3;
let volumeBarPointB4;
if (stairs.type === 'sell') {
function calculateSells(plot, height) {
/* Contributing to Auto-Scale*/
coordinateSystem.reportYValue(stairs.firstAmount * 2)
coordinateSystem.reportYValue(stairs.lastAmount * 2)
volumeBarPointB1 = {
x: stairs.begin + timeFrame / 2,
y: height
};
volumeBarPointB2 = {
x: stairs.begin + timeFrame / 2,
y: height - stairs.firstAmount * 2
};
volumeBarPointB3 = {
x: stairs.end - timeFrame / 2,
y: height - stairs.lastAmount * 2
};
volumeBarPointB4 = {
x: stairs.end - timeFrame / 2,
y: height
};
volumeBarPointB1 = plot.transformThisPoint2(volumeBarPointB1);
volumeBarPointB2 = plot.transformThisPoint2(volumeBarPointB2);
volumeBarPointB3 = plot.transformThisPoint2(volumeBarPointB3);
volumeBarPointB4 = plot.transformThisPoint2(volumeBarPointB4);
volumeBarPointB1 = transformThisPoint(volumeBarPointB1, thisObject.container);
volumeBarPointB2 = transformThisPoint(volumeBarPointB2, thisObject.container);
volumeBarPointB3 = transformThisPoint(volumeBarPointB3, thisObject.container);
volumeBarPointB4 = transformThisPoint(volumeBarPointB4, thisObject.container);
}
calculateSells(coordinateSystem, thisObject.container.frame.height); // We try to see if it fits in the visible area.
if (volumeBarPointB1.y < canvas.chartingSpace.viewport.visibleArea.topLeft.y && frameHeightInViewPort > visibleHeight * 2 / 3) {
calculateSells(coordinateSystem, visibleHeight); // We snap it to the view port.
/* Now we set the real value of y. */
volumeBarPointB1.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y;
volumeBarPointB2.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y + stairs.firstAmount * 2 * coordinateSystem.scale.y;
volumeBarPointB3.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y + stairs.lastAmount * 2 * coordinateSystem.scale.y;
volumeBarPointB4.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y;
}
}
/* Everything must fit within the visible area */
if (stairs.type === 'buy') {
volumeBarPointA1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA1);
volumeBarPointA2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA2);
volumeBarPointA3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA3);
volumeBarPointA4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointA4);
volumeBarPointA1 = thisObject.fitFunction(volumeBarPointA1);
volumeBarPointA2 = thisObject.fitFunction(volumeBarPointA2);
volumeBarPointA3 = thisObject.fitFunction(volumeBarPointA3);
volumeBarPointA4 = thisObject.fitFunction(volumeBarPointA4);
} else {
volumeBarPointB1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB1);
volumeBarPointB2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB2);
volumeBarPointB3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB3);
volumeBarPointB4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(volumeBarPointB4);
volumeBarPointB1 = thisObject.fitFunction(volumeBarPointB1);
volumeBarPointB2 = thisObject.fitFunction(volumeBarPointB2);
volumeBarPointB3 = thisObject.fitFunction(volumeBarPointB3);
volumeBarPointB4 = thisObject.fitFunction(volumeBarPointB4);
}
/* Now the drawing */
if (stairs.type === 'buy') {
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(volumeBarPointA1.x, volumeBarPointA1.y);
browserCanvasContext.lineTo(volumeBarPointA2.x, volumeBarPointA2.y);
browserCanvasContext.lineTo(volumeBarPointA3.x, volumeBarPointA3.y);
browserCanvasContext.lineTo(volumeBarPointA4.x, volumeBarPointA4.y);
browserCanvasContext.closePath();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')';
if (userPositionDate >= stairs.begin && userPositionDate <= stairs.end) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; // Current bar accroding to time
}
browserCanvasContext.fill();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
} else {
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(volumeBarPointB1.x, volumeBarPointB1.y);
browserCanvasContext.lineTo(volumeBarPointB2.x, volumeBarPointB2.y);
browserCanvasContext.lineTo(volumeBarPointB3.x, volumeBarPointB3.y);
browserCanvasContext.lineTo(volumeBarPointB4.x, volumeBarPointB4.y);
browserCanvasContext.closePath();
if (userPositionDate >= stairs.begin && userPositionDate <= stairs.end) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; // Current candle accroding to time
}
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
browserCanvasContext.fill();
browserCanvasContext.lineWidth = 1;
browserCanvasContext.stroke();
}
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
recalculate();
}
function onDragFinished() {
recalculate();
}
function onViewportPositionChanged(event) {
try {
if (event !== undefined) {
if (event.recalculate === true) {
recalculate();
return
}
}
if (Math.random() * 100 > 95) {
recalculate();
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,21 @@
{
"displayName": "Plotters Stairs Patterns",
"codeName": "PlottersStairsPatterns",
"dataMine": "Masters",
"modules": [
{
"codeName": "CandleStairs",
"moduleName": "CandleStairs",
"description": "Plots Candle Stairs patterns.",
"profilePicture": "CandleStairs.png",
"panels": []
},
{
"codeName": "VolumeStairs",
"description": "Plots Volume Stairs patterns.",
"moduleName": "VolumeStairs",
"profilePicture": "VolumeStairs.png",
"panels": []
}
]
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,348 @@
function newAAMastersPlottersTradingSimulationFinalResults() {
const MODULE_NAME = "Final Results Plotter";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
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,
draw: draw,
recalculateScale: recalculateScale,
currentRecord: undefined
};
/* 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 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 dataFile; // This is the current Market File being plotted.
let fileCursor; // This is the current File Cursor being used to retrieve Daily Files.
let singleFile; // This object will provide the different Market Files at different Time Periods.
/* these are module specific variables: */
let records = []; // Here we keep the records to be ploted every time the Draw() function is called by the AAWebPlatform.
let imageStopLossPhase;
let imageTakeProfitPhase;
let imageRecord;
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let filesUpdatedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
viewPort.eventHandler.stopListening(zoomChangedEventSubscriptionId);
viewPort.eventHandler.stopListening(offsetChangedEventSubscriptionId);
singleFile.eventHandler.stopListening(filesUpdatedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
/* icons */
imageStopLossPhase = undefined;
imageTakeProfitPhase = undefined;
imageRecord = undefined;
/* Destroy References */
singleFile = undefined;
datetime = undefined;
timePeriod = undefined;
dataFile = undefined;
fileCursor = undefined;
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) {
try {
/* Store the information received. */
singleFile = pStorage.singleFile;
datetime = pDatetime;
timePeriod = pTimePeriod;
dataFile = singleFile.getFile(timePeriod);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = viewPort.eventHandler.listenToEvent("Zoom Changed", onZoomChanged);
offsetChangedEventSubscriptionId = viewPort.eventHandler.listenToEvent("Offset Changed", onOffsetChanged);
filesUpdatedEventSubscriptionId = singleFile.eventHandler.listenToEvent("Files Updated", onFilesUpdated);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
/* Get ready for plotting. */
recalculate();
/* Ready for when dimmension changes. */
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
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 onFilesUpdated() {
try {
dataFile = singleFile.getFile(timePeriod);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onFilesUpdated -> err = " + err.stack); }
}
}
function setTimePeriod(pTimePeriod) {
try {
if (timePeriod !== pTimePeriod) {
timePeriod = pTimePeriod;
dataFile = singleFile.getFile(timePeriod);
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function draw() {
try {
// Nothing to draw for now
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
records = []
if (timePeriod >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Records Changed", records);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculate() {
try {
if (dataFile === undefined) { return; } // Initialization not complete yet.
records = [];
for (let i = 0; i < dataFile.length; i++) {
let record = {};
record.begin = dataFile[i][0];
record.end = dataFile[i][1];
record.type = dataFile[i][2];
record.rate = dataFile[i][3];
record.amount = dataFile[i][4];
record.balanceA = dataFile[i][5];
record.balanceB = dataFile[i][6];
record.profit = dataFile[i][7];
record.lastTradeProfitLoss = dataFile[i][8];
record.stopLoss = dataFile[i][9];
record.roundtrips = dataFile[i][10];
record.hits = dataFile[i][11];
record.fails = dataFile[i][12];
record.hitRatio = dataFile[i][13];
record.ROI = dataFile[i][14];
record.periods = dataFile[i][15];
record.days = dataFile[i][16];
record.anualizedRateOfReturn = dataFile[i][17];
record.sellRate = dataFile[i][18];
record.lastTradeROI = dataFile[i][19];
record.strategy = dataFile[i][20];
record.strategyPhase = dataFile[i][21];
record.takeProfit = dataFile[i][22];
record.stopLossPhase = dataFile[i][23];
record.takeProfitPhase = dataFile[i][24];
record.positionSize = dataFile[i][26]; // 25 is the message for the executor
record.initialBalanceA = dataFile[i][27];
record.minimunBalanceA = dataFile[i][28];
record.maximunBalanceA = dataFile[i][29];
record.initialBalanceB = dataFile[i][30];
record.minimunBalanceB = dataFile[i][31];
record.maximunBalanceB = dataFile[i][32];
record.baseAsset = dataFile[i][33];
record.positionPeriods = dataFile[i][34];
record.positionDays = dataFile[i][35];
thisObject.currentRecord = record;
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentRecord);
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateScale() {
try {
// No scale needed
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateScale -> err = " + err.stack); }
}
}
function plotChart() {
try {
let userPosition = getUserPosition()
let userPositionDate = userPosition.point.x
let record;
/* Now we calculate and plot the records */
for (let i = 0; i < records.length; i++) { // We do not start in 0 so as to be able to read the previous record i - 1
record = records[i];
/* Send the current record to the panel */
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
let eventRecords = {
currentRecord: record,
lastRecord: records[records.length - 1]
};
thisObject.container.eventHandler.raiseEvent("Current Record Changed", eventRecords);
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onZoomChanged(event) {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onZoomChanged -> err = " + err.stack); }
}
}
function onOffsetChanged() {
try {
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onOffsetChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,360 @@
function newAAMastersPlottersTradingSimulationTradingSimulationFinalResultsPanel() {
let thisObject = {
fitFunction: undefined,
onEventRaised: onEventRaised,
container: undefined,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
};
let container = newContainer();
container.initialize();
thisObject.container = container;
container.displacement.containerName = "Final Results";
container.frame.containerName = "Final Results";
let currentRecord;
let lastRecord;
let panelTabButton
return thisObject;
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL * 2;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * 2.5;
thisObject.container.frame.position.x = viewPort.visibleArea.topLeft.x + thisObject.container.frame.width * 1;
thisObject.container.frame.position.y = viewPort.visibleArea.topRight.y;
panelTabButton = newPanelTabButton()
panelTabButton.parentContainer = thisObject.container
panelTabButton.container.frame.parentFrame = thisObject.container.frame
panelTabButton.fitFunction = thisObject.fitFunction
panelTabButton.initialize()
}
function finalize() {
}
function getContainer(point) {
let container;
container = panelTabButton.getContainer(point)
if (container !== undefined) { return container }
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
let checkPoint = {
x: point.x,
y: point.y
}
checkPoint = thisObject.fitFunction(checkPoint)
if (point.x === checkPoint.x && point.y === checkPoint.y) {
return thisObject.container;
}
}
}
function onEventRaised(records) {
currentRecord = records.currentRecord;
lastRecord = records.lastRecord
}
function draw() {
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
plotCurrentRecordInfo();
panelTabButton.draw()
}
function plotCurrentRecordInfo() {
if (currentRecord === undefined) { return; }
if (lastRecord === undefined) { return; }
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
const X_AXIS = thisObject.container.frame.width / 2;
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
/* put the labels with the records values */
browserCanvasContext.beginPath();
let y = 0;
let increment = 0.024;
y = y + increment;
y = y + increment;
printLabel('Current Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14);
y = y + increment;
printLabel('Size', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.positionSize, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Rate', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.sellRate, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Stop', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.stopLoss, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Take Profit', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.takeProfit, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Periods', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.positionPeriods), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Days', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.positionDays).toFixed(2), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
y = y + increment;
printLabel('Latest Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14);
y = y + increment;
printLabel('Profit / Loss', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.lastTradeProfitLoss), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('ROI', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.lastTradeROI).toFixed(2) + ' % ', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
let bifurcationLevel = y
y = y + increment;
y = y + increment;
printLabel('Partial Results', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1', 14);
y = y + increment;
printLabel('Profit / Loss', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.profit, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Trades', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.roundtrips, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Hits', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.hits, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Fails', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.fails, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Hit Ratio', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.hitRatio * 100).toFixed(2) + ' %', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('ROI', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.ROI * 100).toFixed(2) + ' %', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Periods', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.periods, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Days', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.days, X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Anualized Rate of Return', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.anualizedRateOfReturn * 100).toFixed(2) + ' %', X_AXIS - thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = bifurcationLevel
y = y + increment;
y = y + increment;
printLabel('Final Results', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1', 14);
y = y + increment;
printLabel('Profit / Loss', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(lastRecord.profit, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Trades', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(lastRecord.roundtrips, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Hits', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(lastRecord.hits, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Fails', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(lastRecord.fails, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Hit Ratio', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((lastRecord.hitRatio * 100).toFixed(2) + ' %', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('ROI', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((lastRecord.ROI * 100).toFixed(2) + ' %', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Periods', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(lastRecord.periods, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Days', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(lastRecord.days, X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Anualized Rate of Return', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((lastRecord.anualizedRateOfReturn * 100).toFixed(2) + ' %', X_AXIS + thisObject.container.frame.width / 4, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
function printLabel(labelToPrint, x, y, opacity, fontSize) {
let labelPoint;
if (fontSize === undefined) { fontSize = 10 };
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY;
let label = '' + labelToPrint;
if (isNaN(label) === false) {
label = Number(label).toLocaleString();
if ((label === "-0" || label === "0") && (labelToPrint !== 0) && (labelToPrint !== "0.00")) {
label = Number(labelToPrint).toFixed(6);
}
}
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
labelPoint = {
x: x - xOffset,
y: y
};
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint);
labelPoint = thisObject.fitFunction(labelPoint)
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
}
browserCanvasContext.closePath();
browserCanvasContext.fill();
/* Parameters */
let params
let paramsArray = []
let positionTaken = false
if (currentRecord.positionSize > 0) {
positionTaken = true
}
params = {}
params.VALUE = currentRecord.balanceA;
params.INIT_VALUE = currentRecord.initialBalanceA
params.MIN_VALUE = currentRecord.minimunBalanceA
params.MAX_VALUE = currentRecord.maximunBalanceA
params.ASSET_LABEL = 'Asset A'
params.ASSET_NAME = DEFAULT_MARKET.assetB
params.LEFT_OFFSET = 100
params.POSITION_TAKEN = positionTaken
params.BASE_ASSET = currentRecord.baseAsset
params.DECIMALS = 4
paramsArray.push(params)
params = {}
params.VALUE = currentRecord.balanceB;
params.MIN_VALUE = currentRecord.minimunBalanceB
params.INIT_VALUE = currentRecord.initialBalanceB
params.MAX_VALUE = currentRecord.maximunBalanceB
params.ASSET_LABEL = 'Asset B'
params.ASSET_NAME = DEFAULT_MARKET.assetA
params.LEFT_OFFSET = 220
params.POSITION_TAKEN = positionTaken
params.BASE_ASSET = currentRecord.baseAsset
params.DECIMALS = 2
paramsArray.push(params)
canvas.cockpitSpace.assetBalances.setParamsArray(paramsArray)
}
}
@@ -0,0 +1,799 @@
function newMastersPlottersTradingSimulationSimulationExecution() {
const MODULE_NAME = "Simulation Execution Plotter";
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,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
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 coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 previousNotesSetKey;
let onMouseOverEventSuscriptionId
let offsetChangedEventSubscriptionId
let filesUpdatedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
fileSequence.eventHandler.stopListening(filesUpdatedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
/* Destroyd References */
datetime = undefined;
timeFrame = undefined;
fileSequence = undefined;
plotElements = undefined;
plotLines = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> finalize -> err = " + err.stack.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
fileSequence = pStorage.fileSequences[0];
recalculate();
filesUpdatedEventSubscriptionId = fileSequence.eventHandler.listenToEvent("Files Updated", onFilesUpdated); // Only the first sequence is supported right now.
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Ready for when dimmension changes. */
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> initialize -> err = " + err.stack); }
callBackFunction(GLOBAL.CUSTOM_FAIL_RESPONSE);
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
function getContainer(point) {
try {
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;
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> initialize -> err = " + err.stack); }
}
}
function onFilesUpdated() {
recalculate();
}
function setTimeFrame(pTimeFrame) {
timeFrame = pTimeFrame;
recalculate();
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onViewportPositionChanged() {
if (Math.random() * 100 > 95) {
recalculate()
}
}
function recalculate() {
try {
if (fileSequence === undefined) { return; }
/*
We are going to filter the records depending on the Time Frame. 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 = [];
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 = timeFrame / ONE_MIN_IN_MILISECONDS;
step = 1; // For now we dont discard records depending on the zoom level.
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],
datetime: file[i][0]
};
/* Finally we add this History Record to the Array */
history.push(newHistoryRecord);
lastRecordPushed = i;
/* Here we build the lines. */
//if (timeFrame <= 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]
};
history.push(newHistoryRecord);
}
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]
};
}
plotElements.push(history);
plotLines.push(lines);
}
thisObject.container.eventHandler.raiseEvent("History Changed", history);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> recalculate -> err = " + err.stack); }
}
}
function draw() {
plotChart();
}
function plotChart() {
try {
let currentRecord
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];
currentRecord = record
let timestamp = record.date // Math.trunc(record.date / timeFrame) * timeFrame + timeFrame / 2;
point = {
x: timestamp,
y: record.marketRate
};
point = coordinateSystem.transformThisPoint(point);
point = transformThisPoint(point, thisObject.container);
if (point.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) { continue; }
point = canvas.chartingSpace.viewport.fitIntoVisibleArea(point);
point = thisObject.fitFunction(point);
let isCurrentRecord = false;
if (userPositionDate >= record.date - 30000 && userPositionDate <= record.date + 30000 - 1) {
isCurrentRecord = true;
}
let opacity = '1';
let currentRadius = 10
/* Draw a red inverted triangle on exec sell */
if (record.sellExecRate > 0) {
opacity = '1';
let normalSize = 5
let extraSize = 0
if (isCurrentRecord) { extraSize = 5 }
let point1 = {
x: record.date,
y: record.sellExecRate
};
let point2 = {
x: record.date + timeFrame / 7 * (normalSize + extraSize),
y: record.sellExecRate
};
let point3 = {
x: record.date - timeFrame / 7 * (normalSize + extraSize),
y: record.sellExecRate
};
point1 = coordinateSystem.transformThisPoint(point1);
point1 = transformThisPoint(point1, thisObject.container);
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
point1 = thisObject.fitFunction(point1);
point2 = coordinateSystem.transformThisPoint(point2);
point2 = transformThisPoint(point2, thisObject.container);
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point2 = thisObject.fitFunction(point2);
point3 = coordinateSystem.transformThisPoint(point3);
point3 = transformThisPoint(point3, thisObject.container);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y - diff;
point3.y = point3.y - diff;
currentRecord.timeFrame = diff
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.fitFunction(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();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
if (isCurrentRecord === false) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 0.25;
browserCanvasContext.stroke();
}
/* Draw a green triangle on exec buy */
if (record.buyExecRate > 0) {
opacity = '1';
let normalSize = 5
let extraSize = 0
if (isCurrentRecord) { extraSize = 5 }
let point1 = {
x: record.date,
y: record.buyExecRate
};
let point2 = {
x: record.date + timeFrame / 7 * (normalSize + extraSize),
y: record.buyExecRate
};
let point3 = {
x: record.date - timeFrame / 7 * (normalSize + extraSize),
y: record.buyExecRate
};
point1 = coordinateSystem.transformThisPoint(point1);
point1 = transformThisPoint(point1, thisObject.container);
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
point1 = thisObject.fitFunction(point1);
point2 = coordinateSystem.transformThisPoint(point2);
point2 = transformThisPoint(point2, thisObject.container);
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point2 = thisObject.fitFunction(point2);
point3 = coordinateSystem.transformThisPoint(point3);
point3 = transformThisPoint(point3, thisObject.container);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y + diff;
point3.y = point3.y + diff;
currentRecord.timeFrame = diff
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.fitFunction(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();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')';
if (isCurrentRecord === false) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 0.25;
browserCanvasContext.stroke();
}
/* Draw a red inverted triangle on sell */
if (record.lastSellRate > 0) {
opacity = '1';
let normalSize = 5
let extraSize = 0
if (isCurrentRecord) { extraSize = 5 }
let point1 = {
x: record.date,
y: record.lastSellRate
};
let point2 = {
x: record.date + timeFrame / 7 * (normalSize + extraSize),
y: record.lastSellRate
};
let point3 = {
x: record.date - timeFrame / 7 * (normalSize + extraSize),
y: record.lastSellRate
};
point1 = coordinateSystem.transformThisPoint(point1);
point1 = transformThisPoint(point1, thisObject.container);
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
point1 = thisObject.fitFunction(point1);
point2 = coordinateSystem.transformThisPoint(point2);
point2 = transformThisPoint(point2, thisObject.container);
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point2 = thisObject.fitFunction(point2);
point3 = coordinateSystem.transformThisPoint(point3);
point3 = transformThisPoint(point3, thisObject.container);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y - diff;
point3.y = point3.y - diff;
currentRecord.timeFrame = diff
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.fitFunction(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(' + UI_COLOR.WHITE + ', ' + opacity + ')';
} else {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 0.25;
browserCanvasContext.stroke();
}
/* Draw a green triangle on buy */
if (record.lastBuyRate > 0) {
opacity = '1';
let normalSize = 5
let extraSize = 0
if (isCurrentRecord) { extraSize = 5 }
let point1 = {
x: record.date,
y: record.lastBuyRate
};
let point2 = {
x: record.date + timeFrame / 7 * (normalSize + extraSize),
y: record.lastBuyRate
};
let point3 = {
x: record.date - timeFrame / 7 * (normalSize + extraSize),
y: record.lastBuyRate
};
point1 = coordinateSystem.transformThisPoint(point1);
point1 = transformThisPoint(point1, thisObject.container);
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
point1 = thisObject.fitFunction(point1);
point2 = coordinateSystem.transformThisPoint(point2);
point2 = transformThisPoint(point2, thisObject.container);
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point2 = thisObject.fitFunction(point2);
point3 = coordinateSystem.transformThisPoint(point3);
point3 = transformThisPoint(point3, thisObject.container);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y + diff;
point3.y = point3.y + diff;
currentRecord.timeFrame = diff
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point3);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.fitFunction(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(' + UI_COLOR.WHITE + ', ' + opacity + ')';
} else {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 0.25;
browserCanvasContext.stroke();
}
/* Send the current record to the panel */
if (userPositionDate >= record.date - 30000 && userPositionDate < record.date + 30000) {
thisObject.container.eventHandler.raiseEvent("Current Record Changed", currentRecord);
}
/* Circles */
let radius = 2;
currentRadius = 10
/* Outer Circle */
if (isCurrentRecord === true) {
browserCanvasContext.beginPath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
browserCanvasContext.arc(point.x, point.y, currentRadius, 0, Math.PI * 2, true);
browserCanvasContext.closePath();
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
browserCanvasContext.fill();
}
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 0.25;
browserCanvasContext.stroke();
}
/* Inner Circle */
browserCanvasContext.beginPath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + opacity + ')';
browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true);
browserCanvasContext.closePath();
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
browserCanvasContext.fill();
}
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.lineWidth = 0.25;
browserCanvasContext.stroke();
}
/* Draw the lines connecting plot elements. */
let lines = plotLines[j];
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
opacity = '1';
let point1 = {
x: line.x1,
y: line.y1
};
let point2 = {
x: line.x2,
y: line.y2
};
point1 = coordinateSystem.transformThisPoint(point1);
point1 = transformThisPoint(point1, thisObject.container);
point1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point1);
point1 = thisObject.fitFunction(point1);
point2 = coordinateSystem.transformThisPoint(point2);
point2 = transformThisPoint(point2, thisObject.container);
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point2 = thisObject.fitFunction(point2);
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(point1.x, point1.y);
browserCanvasContext.lineTo(point2.x, point2.y);
browserCanvasContext.closePath();
if (line.type === "sell") {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
}
browserCanvasContext.setLineDash([4, 1])
browserCanvasContext.lineWidth = 0.25;
browserCanvasContext.stroke();
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] ' + MODULE_NAME + ' -> plotChart -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,465 @@
function newMastersPlottersTradingSimulationSimulationExecutionSimulationExecutionPanel() {
let thisObject = {
fitFunction: undefined,
container: undefined,
isVisible: true,
onRecordChange: onRecordChange,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
};
let container = newContainer();
container.initialize();
thisObject.container = container;
container.frame.containerName = "Trading Execution";
let currentRecord;
let panelTabButton
return thisObject;
function finalize() {
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.isVisible = undefined
currentRecord = undefined
panelTabButton.finalize()
panelTabButton = undefined
}
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * 2.5;
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topLeft.x + thisObject.container.frame.width * 4;
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y;
panelTabButton = newPanelTabButton()
panelTabButton.parentContainer = thisObject.container
panelTabButton.container.frame.parentFrame = thisObject.container.frame
panelTabButton.fitFunction = thisObject.fitFunction
panelTabButton.initialize()
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = panelTabButton.getContainer(point)
if (container !== undefined) { return container }
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
let checkPoint = {
x: point.x,
y: point.y
}
checkPoint = thisObject.fitFunction(checkPoint)
if (point.x === checkPoint.x && point.y === checkPoint.y) {
return thisObject.container;
}
}
}
function onRecordChange(lastCurrentCandle) {
currentRecord = lastCurrentCandle;
}
function draw() {
if (thisObject.isVisible !== true) { return }
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
plot();
panelTabButton.draw()
}
function plot() {
if (currentRecord === undefined) { return; }
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
const X_AXIS = thisObject.container.frame.width / 2;
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
record = currentRecord
point = {
x: X_AXIS,
y: Y_AXIS
};
point = thisObject.container.frame.frameThisPoint(point);
point = thisObject.fitFunction(point);
let isCurrentRecord = true;
let radius = 2;
let currentRadius = 10
let opacity = '1';
/* Draw a red inverted triangle on exec sell */
if (record.sellExecRate > 0) {
opacity = '1';
let point1 = {
x: X_AXIS,
y: Y_AXIS
};
let point2 = {
x: X_AXIS + record.timeFrame / 4,
y: Y_AXIS
};
let point3 = {
x: X_AXIS - record.timeFrame / 4,
y: Y_AXIS
};
point1 = thisObject.container.frame.frameThisPoint(point1);
point1 = thisObject.fitFunction(point1);
point2 = thisObject.container.frame.frameThisPoint(point2);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.container.frame.frameThisPoint(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y - diff;
point3.y = point3.y - diff;
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.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();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
if (isCurrentRecord === false) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
/* Draw a green triangle on exec buy */
if (record.buyExecRate > 0) {
opacity = '1';
let point1 = {
x: X_AXIS,
y: Y_AXIS
};
let point2 = {
x: X_AXIS + record.timeFrame / 4,
y: Y_AXIS
};
let point3 = {
x: X_AXIS - record.timeFrame / 4,
y: Y_AXIS
};
point1 = thisObject.container.frame.frameThisPoint(point1);
point1 = thisObject.fitFunction(point1);
point2 = thisObject.container.frame.frameThisPoint(point2);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.container.frame.frameThisPoint(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y + diff;
point3.y = point3.y + diff;
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.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();
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')';
if (isCurrentRecord === false) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
/* Draw a red inverted triangle on sell */
if (record.lastSellRate > 0) {
opacity = '1';
let point1 = {
x: X_AXIS,
y: Y_AXIS
};
let point2 = {
x: X_AXIS + record.timeFrame / 4,
y: Y_AXIS
};
let point3 = {
x: X_AXIS - record.timeFrame / 4,
y: Y_AXIS
};
point1 = thisObject.container.frame.frameThisPoint(point1);
point1 = thisObject.fitFunction(point1);
point2 = thisObject.container.frame.frameThisPoint(point2);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.container.frame.frameThisPoint(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y - diff;
point3.y = point3.y - diff;
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.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(' + UI_COLOR.WHITE + ', ' + opacity + ')';
} else {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
/* Draw a green triangle on buy */
if (record.lastBuyRate > 0) {
opacity = '1';
let point1 = {
x: X_AXIS,
y: Y_AXIS
};
let point2 = {
x: X_AXIS + record.timeFrame / 4,
y: Y_AXIS
};
let point3 = {
x: X_AXIS - record.timeFrame / 4,
y: Y_AXIS
};
point1 = thisObject.container.frame.frameThisPoint(point1);
point1 = thisObject.fitFunction(point1);
point2 = thisObject.container.frame.frameThisPoint(point2);
point2 = thisObject.fitFunction(point2);
point3 = thisObject.container.frame.frameThisPoint(point3);
point3 = thisObject.fitFunction(point3);
let diff = point2.x - point3.x;
point2.y = point2.y + diff;
point3.y = point3.y + diff;
point2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(point2);
point3 = canvas.chartingSpace.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(' + UI_COLOR.WHITE + ', ' + opacity + ')';
} else {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
}
browserCanvasContext.fill();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
browserCanvasContext.lineWidth = 0.25;
/* Outer Circle */
if (isCurrentRecord === true) {
browserCanvasContext.beginPath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')'; /* highlight the current record */
browserCanvasContext.arc(point.x, point.y, currentRadius, 0, Math.PI * 2, true);
browserCanvasContext.closePath();
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
browserCanvasContext.fill();
}
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
}
/* Inner Circle */
browserCanvasContext.beginPath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + opacity + ')';
browserCanvasContext.arc(point.x, point.y, radius, 0, Math.PI * 2, true);
browserCanvasContext.closePath();
if (point.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 50 || point.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 50) {/*we leave this history without fill. */ } else {
browserCanvasContext.fill();
}
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
/* put the labels */
browserCanvasContext.beginPath();
let y;
let recordDate = new Date(currentRecord.datetime)
printLabel('Market Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.05 / 2, '1');
printLabel(currentRecord.marketRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10 / 2, '0.50');
printLabel('Sell Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.15 / 2, '1');
printLabel(currentRecord.lastSellRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.20 / 2, '0.50');
printLabel('Sell Executed Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.25 / 2, '1');
printLabel(currentRecord.sellExecRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.30 / 2, '0.50');
printLabel('Buy Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.35 / 2, '1');
printLabel(currentRecord.lastBuyRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.40 / 2, '0.40');
printLabel('Buy Executed Rate', X_AXIS, frameTitleHeight + frameBodyHeight * 0.45 / 2, '1');
printLabel(currentRecord.buyExecRate, X_AXIS, frameTitleHeight + frameBodyHeight * 0.50 / 2, '0.50');
printLabel('ROI Asset A', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.25 / 2), '1');
printLabel((currentRecord.combinedProfitsB).toFixed(2) + '%', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.30 / 2), '0.50');
printLabel('ROI Asset B', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.15 / 2), '1');
printLabel((currentRecord.combinedProfitsA).toFixed(2) + '%', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.20 / 2), '0.50');
printLabel('Datetime', X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.05 / 2), '1');
printLabel(recordDate.toUTCString(), X_AXIS, frameTitleHeight + frameBodyHeight * (1 - 0.1 / 2), '0.50');
function printLabel(labelToPrint, x, y, opacity) {
let labelPoint;
let fontSize = 10;
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY + ' Saira';
let label = '' + labelToPrint;
if (isNaN(label) === false) {
label = Number(label).toLocaleString();
}
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
labelPoint = {
x: x - xOffset,
y: y
};
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint);
labelPoint = thisObject.fitFunction(labelPoint)
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
}
browserCanvasContext.closePath();
browserCanvasContext.fill();
}
}
@@ -0,0 +1,686 @@
function newMastersPlottersTradingSimulationStrategies() {
const MODULE_NAME = "Strategies Plotter";
const INFO_LOG = false;
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
// Main functions and properties.
initialize: initialize,
finalize: finalize,
container: undefined,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
draw: draw,
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Secondary functions and properties.
currentRecord: undefined
};
/* 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 coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
/* these are module specific variables: */
let strategies = [];
let strategyImages = [];
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
if (INFO_LOG === true) { logger.write("[INFO] finalize -> Entering function."); }
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
strategyImages = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); }
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
/* Ready for when dimmension changes. */
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
for (let i = 1; i < 15; i++) {
let strategyImage = canvas.designSpace.iconByUiObjectType.get('Strategy');
strategyImages.push(strategyImage);
}
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
function getContainer(point) {
try {
if (INFO_LOG === true) { logger.write("[INFO] getContainer -> Entering function."); }
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;
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
marketFile = marketFiles.getFile(pTimeFrame);
recalculate();
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing.
recalculate();
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
try {
if (INFO_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); }
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
this.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
strategies = []
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Strategies Changed", strategies);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingDailyFiles -> Entering function."); }
if (fileCursor === undefined) {
strategies = [];
return;
} // We need to wait
if (fileCursor.files.size === 0) {
strategies = [];
return;
} // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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());
strategies = [];
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 record = {
begin: undefined,
end: undefined,
status: undefined,
number: undefined,
beginRate: undefined,
endRate: undefined
};
record.begin = dailyFile[i][0];
record.end = dailyFile[i][1];
record.status = dailyFile[i][2];
record.number = dailyFile[i][3];
record.beginRate = dailyFile[i][4];
record.endRate = dailyFile[i][5];
if (
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
) {
strategies.push(record);
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
thisObject.currentRecord = record;
thisObject.container.eventHandler.raiseEvent("Current Strategy Changed", thisObject.currentRecord);
}
}
}
}
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
}
/* Lests check if all the visible screen is going to be covered by strategies. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
setTimeout(recalculate, 2000);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingMarketFiles -> Entering function."); }
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
strategies = [];
for (let i = 0; i < marketFile.length; i++) {
let record = {
begin: undefined,
end: undefined,
status: undefined,
number: undefined,
beginRate: undefined,
endRate: undefined
};
record.begin = marketFile[i][0];
record.end = marketFile[i][1];
record.status = marketFile[i][2];
record.number = marketFile[i][3];
record.beginRate = marketFile[i][4];
record.endRate = marketFile[i][5];
if (record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) {
strategies.push(record);
if (
(record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) &&
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
) {
thisObject.currentRecord = record;
thisObject.container.eventHandler.raiseEvent("Current Strategy Changed", thisObject.currentRecord);
}
}
}
//console.log("Olivia > recalculateUsingMarketFiles > total strategies generated : " + strategies.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
thisObject.container.eventHandler.raiseEvent("Current Strategy Record Changed", undefined);
let record;
for (let i = 0; i < strategies.length; i++) {
record = strategies[i];
let recordPoint1 = {
x: record.begin,
y: record.beginRate
};
let recordPoint2 = {
x: record.end,
y: record.endRate
};
let recordPoint3 = {
x: record.begin,
y: record.beginRate
};
let recordPoint4 = {
x: record.end,
y: record.endRate
};
recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1);
recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2);
recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3);
recordPoint4 = coordinateSystem.transformThisPoint(recordPoint4);
recordPoint1 = transformThisPoint(recordPoint1, thisObject.container);
recordPoint2 = transformThisPoint(recordPoint2, thisObject.container);
recordPoint3 = transformThisPoint(recordPoint3, thisObject.container);
recordPoint4 = transformThisPoint(recordPoint4, thisObject.container);
if (recordPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue;
}
recordPoint3.y = recordPoint1.y + 2000;
recordPoint4.y = recordPoint1.y + 2000;
recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1);
recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2);
recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3);
recordPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint4);
recordPoint1 = thisObject.fitFunction(recordPoint1, undefined, 30);
recordPoint2 = thisObject.fitFunction(recordPoint2, undefined, 30);
recordPoint3 = thisObject.fitFunction(recordPoint3, undefined, 30);
recordPoint4 = thisObject.fitFunction(recordPoint4, undefined, 30);
let imageSize = 20;
let imageToDraw = strategyImages[record.number];
/* Draw the line that represents the duration of closed strategy */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(recordPoint3.x + imageSize / 2, recordPoint3.y);
browserCanvasContext.lineTo(recordPoint4.x - imageSize / 2, recordPoint4.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', 1)';
browserCanvasContext.lineWidth = 1
if (record.status === 1) {
browserCanvasContext.setLineDash([0, 0])
} else {
browserCanvasContext.setLineDash([2, 4])
}
browserCanvasContext.stroke()
drawStick(recordPoint1, recordPoint3);
drawStick(recordPoint2, recordPoint4);
if (imageToDraw.canDrawIcon === true) {
browserCanvasContext.drawImage(imageToDraw, recordPoint3.x - imageSize / 2, recordPoint3.y - imageSize / 2, imageSize, imageSize);
if (record.status === 1) {
browserCanvasContext.drawImage(imageToDraw, recordPoint4.x - imageSize / 2, recordPoint4.y - imageSize / 2, imageSize, imageSize);
}
}
/* Send the current record to the panel */
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
let currentRecord = {
};
thisObject.container.eventHandler.raiseEvent("Current Strategy Record Changed", currentRecord);
}
function drawStick(point1, point2) {
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(point1.x, point1.y);
browserCanvasContext.lineTo(point2.x, point2.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', 0.25)';
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
/* highlight the current record */
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
}
browserCanvasContext.setLineDash([4, 3])
browserCanvasContext.lineWidth = 0.5
browserCanvasContext.stroke()
browserCanvasContext.setLineDash([0, 0])
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
try {
if (INFO_LOG === true) { logger.write("[INFO] onViewportZoomChanged -> Entering function."); }
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
}
}
function onViewportPositionChanged(event) {
try {
if (INFO_LOG === true) { logger.write("[INFO] onViewportPositionChanged -> Entering function."); }
if (event !== undefined) {
if (event.recalculate === true) {
recalculate()
return
}
}
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); }
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,683 @@
function newMastersPlottersTradingSimulationTrades() {
const MODULE_NAME = "Trades Plotter";
const INFO_LOG = false;
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
// Main functions and properties.
initialize: initialize,
finalize: finalize,
container: undefined,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
draw: draw,
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Secondary functions and properties.
currentRecord: undefined
};
/* 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 coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
/* these are module specific variables: */
let trades = [];
let headers;
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
if (INFO_LOG === true) { logger.write("[INFO] finalize -> Entering function."); }
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* Destroyd References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); }
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
/* Ready for when dimmension changes. */
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
function getContainer(point) {
try {
if (INFO_LOG === true) { logger.write("[INFO] getContainer -> Entering function."); }
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;
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] getContainer -> err = " + err.stack); }
}
}
function onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
marketFile = marketFiles.getFile(pTimeFrame);
recalculate();
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing.
recalculate();
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
try {
if (INFO_LOG === true) { logger.write("[INFO] onDailyFileLoaded -> Entering function."); }
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
this.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
trades = []
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Trades Changed", trades);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingDailyFiles -> Entering function."); }
if (fileCursor === undefined) {
trades = [];
return;
} // We need to wait
if (fileCursor.files.size === 0) {
trades = [];
return;
} // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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());
trades = [];
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 record = {};
record.begin = dailyFile[i][0];
record.end = dailyFile[i][1];
record.status = dailyFile[i][2];
record.lastTradeROI = dailyFile[i][3];
record.beginRate = dailyFile[i][4];
record.endRate = dailyFile[i][5];
record.exitType = dailyFile[i][6];
if (
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
) {
trades.push(record);
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
thisObject.currentRecord = record;
thisObject.container.eventHandler.raiseEvent("Current Trade Changed", thisObject.currentRecord);
}
}
}
}
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
}
/* Lests check if all the visible screen is going to be covered by trades. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
setTimeout(recalculate, 2000);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (INFO_LOG === true) { logger.write("[INFO] recalculateUsingMarketFiles -> Entering function."); }
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
trades = [];
for (let i = 0; i < marketFile.length; i++) {
let record = {};
record.begin = marketFile[i][0];
record.end = marketFile[i][1];
record.status = marketFile[i][2];
record.lastTradeROI = marketFile[i][3];
record.beginRate = marketFile[i][4];
record.endRate = marketFile[i][5];
record.exitType = marketFile[i][6];
if (
(record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) &&
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
) {
trades.push(record);
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
thisObject.currentRecord = record;
thisObject.container.eventHandler.raiseEvent("Current Trade Changed", thisObject.currentRecord);
}
}
}
//console.log("Olivia > recalculateUsingMarketFiles > total trades generated : " + trades.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
thisObject.container.eventHandler.raiseEvent("Current Trade Record Changed", undefined);
let record;
for (let i = 0; i < trades.length; i++) {
record = trades[i];
/* Send the current record to the panel */
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
let currentRecord = {
};
thisObject.container.eventHandler.raiseEvent("Current Trade Record Changed", currentRecord);
}
let recordPoint1 = {
x: record.begin,
y: record.beginRate
};
let recordPoint2 = {
x: record.end,
y: record.beginRate
};
let recordPoint3 = {
x: record.end,
y: record.endRate
};
recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1);
recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2);
recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3);
recordPoint1 = transformThisPoint(recordPoint1, thisObject.container);
recordPoint2 = transformThisPoint(recordPoint2, thisObject.container);
recordPoint3 = transformThisPoint(recordPoint3, thisObject.container);
if (recordPoint2.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint1.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue;
}
recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1);
recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2);
recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3);
recordPoint1 = thisObject.fitFunction(recordPoint1);
recordPoint2 = thisObject.fitFunction(recordPoint2);
recordPoint3 = thisObject.fitFunction(recordPoint3);
let line1 = '';
let line2 = '';
if (record.status === 1) {
switch (record.exitType) {
case 1: {
line1 = 'Exit: STOP';
break;
}
case 2: {
line1 = 'Exit: TP';
break;
}
}
} else {
line1 = 'Open Position';
}
if (record.lastTradeROI < 0) {
line2 = 'ROI:' + (record.lastTradeROI).toFixed(2) + ' %';
} else {
line2 = 'ROI:' + (record.lastTradeROI).toFixed(2) + ' %';
}
/* Draw the triangle that represents the trade. */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(recordPoint1.x, recordPoint1.y);
browserCanvasContext.lineTo(recordPoint2.x, recordPoint2.y);
browserCanvasContext.lineTo(recordPoint3.x, recordPoint3.y);
browserCanvasContext.closePath();
let opacity = '0.25';
if (record.lastTradeROI > 0) {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + ')';
} else {
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + opacity + ')';
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')';
}
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + opacity + ')';
}
browserCanvasContext.fill();
browserCanvasContext.lineWidth = 1;
browserCanvasContext.setLineDash([0, 0])
browserCanvasContext.stroke();
let point = {}
if (record.beginRate > record.endRate) {
point.x = recordPoint3.x
point.y = recordPoint3.y + 100
} else {
point.x = recordPoint3.x
point.y = recordPoint3.y - 100
}
point = canvas.chartingSpace.viewport.fitIntoVisibleArea(point);
point = thisObject.fitFunction(point, undefined, 50);
if (
recordPoint3.x < canvas.chartingSpace.viewport.visibleArea.topLeft.x + 250
||
recordPoint3.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x - 250
||
recordPoint3.y > canvas.chartingSpace.viewport.visibleArea.bottomRight.y - 150
||
recordPoint3.y < canvas.chartingSpace.viewport.visibleArea.topLeft.y + 150
) {
// we do not write any text
} else {
printLabel(line1, recordPoint2.x - (recordPoint2.x - recordPoint1.x) / 2 - line1.length * FONT_ASPECT_RATIO, point.y, '1', 12);
printLabel(line2, recordPoint2.x - (recordPoint2.x - recordPoint1.x) / 2 - line2.length * FONT_ASPECT_RATIO, point.y + 15, '1', 12);
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
try {
if (INFO_LOG === true) { logger.write("[INFO] onViewportZoomChanged -> Entering function."); }
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
}
}
function onViewportPositionChanged(event) {
try {
if (INFO_LOG === true) { logger.write("[INFO] onViewportPositionChanged -> Entering function."); }
if (event !== undefined) {
if (event.recalculate === true) {
recalculate()
return
}
}
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); }
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,859 @@
function newMastersPlottersTradingSimulationTradingSimulation() {
const MODULE_NAME = "Simulation Plotter";
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
// Main functions and properties.
initialize: initialize,
finalize: finalize,
container: undefined,
fitFunction: undefined,
getContainer: getContainer,
setTimeFrame: setTimeFrame,
setDatetime: setDatetime,
setCoordinateSystem: setCoordinateSystem,
draw: draw,
/* Events declared outside the plotter. */
onDailyFileLoaded: onDailyFileLoaded,
// Secondary functions and properties.
currentRecord: undefined
};
/* 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 coordinateSystem
let timeFrame; // This will hold the current Time Frame 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 marketFiles; // This object will provide the different Market Files at different Time Frames.
let dailyFiles; // This object will provide the different File Cursors at different Time Frames.
/* these are module specific variables: */
let records = []; // Here we keep the records to be ploted every time the Draw() function is called by the AAWebPlatform.
let imageStopLossPhase;
let imageTakeProfitPhase;
let imageRecord;
let onMouseOverEventSuscriptionId
let zoomChangedEventSubscriptionId
let offsetChangedEventSubscriptionId
let dragFinishedEventSubscriptionId
let dimmensionsChangedEventSubscriptionId
let marketFilesUpdatedEventSubscriptionId
let dailyFilesUpdatedEventSubscriptionId
let scaleChangedEventSubscriptionId
let userPositionDate
return thisObject;
function finalize() {
try {
/* Stop listening to the necesary events. */
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId);
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId);
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId);
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId);
/* icons */
imageStopLossPhase = undefined;
imageTakeProfitPhase = undefined;
imageRecord = undefined;
/* Destroy References */
marketFiles = undefined;
dailyFiles = undefined;
datetime = undefined;
timeFrame = undefined;
marketFile = undefined;
fileCursor = undefined;
thisObject.fitFunction = undefined
finalizeCoordinateSystem()
coordinateSystem = undefined
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] finalize -> err = " + err.stack); }
}
}
function initialize(pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction) {
try {
/* Store the information received. */
marketFiles = pStorage.marketFiles[0];
dailyFiles = pStorage.dailyFiles[0];
datetime = pDatetime;
timeFrame = pTimeFrame;
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS); // This file is the one processed faster.
/* Now we set the right files according to current Period. */
marketFile = marketFiles.getFile(pTimeFrame);
fileCursor = dailyFiles.getFileCursor(pTimeFrame);
/* Listen to the necesary events. */
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent("Position Changed", onViewportPositionChanged);
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished);
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent("Files Updated", onMarketFilesUpdated);
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent("Files Updated", onDailyFilesUpdated);
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
/* Get ready for plotting. */
recalculate();
/* Ready for when dimmension changes. */
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
recalculate();
})
/* Loading a few icons */
imageStopLossPhase = canvas.designSpace.iconByUiObjectType.get('Stop');
imageTakeProfitPhase = canvas.designSpace.iconByUiObjectType.get('Phase');
imageRecord = canvas.designSpace.iconByUiObjectType.get('Trading System');
callBackFunction();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err.stack); }
}
}
function initializeCoordinateSystem() {
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
}
function finalizeCoordinateSystem() {
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
}
function onScaleChanged() {
recalculate();
}
function onMouseOver(event) {
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
userPositionDate = userPosition.valueOf()
}
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 onMarketFilesUpdated() {
try {
let newMarketFile = marketFiles.getFile(timeFrame);
if (newMarketFile !== undefined) {
marketFile = newMarketFile;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onMarketFilesUpdated -> err = " + err.stack); }
}
}
function onDailyFilesUpdated() {
try {
let newFileCursor = dailyFiles.getFileCursor(timeFrame);
if (newFileCursor !== undefined) {
fileCursor = newFileCursor;
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFilesUpdated -> err = " + err.stack); }
}
}
function setTimeFrame(pTimeFrame) {
try {
if (timeFrame !== pTimeFrame) {
timeFrame = pTimeFrame;
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
marketFile = marketFiles.getFile(pTimeFrame);
recalculate();
} else {
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame);
fileCursor = newFileCursor; // In this case, we explicitly want that if there is no valid cursor, we invalidate the data and show nothing.
recalculate();
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] setTimeFrame -> err = " + err.stack); }
}
}
function setDatetime(pDatetime) {
datetime = pDatetime;
}
function setCoordinateSystem(pCoordinateSystem) {
finalizeCoordinateSystem()
coordinateSystem = pCoordinateSystem
initializeCoordinateSystem()
}
function onDailyFileLoaded(event) {
try {
if (event.currentValue === event.totalValue) {
/* This happens only when all of the files in the cursor have been loaded. */
recalculate();
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDailyFileLoaded -> err = " + err.stack); }
}
}
function draw() {
try {
if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); }
this.container.frame.draw();
plotChart();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] draw -> err = " + err.stack); }
}
}
function recalculate() {
try {
records = []
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
recalculateUsingMarketFiles();
} else {
recalculateUsingDailyFiles();
}
thisObject.container.eventHandler.raiseEvent("Records Changed", records);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculate -> err = " + err.stack); }
}
}
function recalculateUsingDailyFiles() {
try {
if (fileCursor === undefined) {
records = [];
return;
} // We need to wait
if (fileCursor.files.size === 0) {
records = [];
return;
} // We need to wait until there are files in the cursor
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
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());
records = [];
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 record = {};
record.begin = dailyFile[i][0];
record.end = dailyFile[i][1];
record.type = dailyFile[i][2];
record.rate = dailyFile[i][3];
record.amount = dailyFile[i][4];
record.balanceA = dailyFile[i][5];
record.balanceB = dailyFile[i][6];
record.profit = dailyFile[i][7];
record.lastTradeProfitLoss = dailyFile[i][8];
record.stopLoss = dailyFile[i][9];
record.roundtrips = dailyFile[i][10];
record.hits = dailyFile[i][11];
record.fails = dailyFile[i][12];
record.hitRatio = dailyFile[i][13];
record.ROI = dailyFile[i][14];
record.periods = dailyFile[i][15];
record.days = dailyFile[i][16];
record.anualizedRateOfReturn = dailyFile[i][17];
record.sellRate = dailyFile[i][18];
record.lastTradeROI = dailyFile[i][19];
record.strategy = dailyFile[i][20];
record.strategyPhase = dailyFile[i][21];
record.takeProfit = dailyFile[i][22];
record.stopLossPhase = dailyFile[i][23];
record.takeProfitPhase = dailyFile[i][24];
record.positionSize = dailyFile[i][26]; // 25 is the message for the executor
record.initialBalanceA = dailyFile[i][27];
record.minimunBalanceA = dailyFile[i][28];
record.maximunBalanceA = dailyFile[i][29];
record.initialBalanceB = dailyFile[i][30];
record.minimunBalanceB = dailyFile[i][31];
record.maximunBalanceB = dailyFile[i][32];
record.baseAsset = dailyFile[i][33];
record.positionPeriods = dailyFile[i][34];
record.positionDays = dailyFile[i][35];
if (
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
) {
records.push(record);
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
thisObject.currentRecord = record;
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentRecord);
}
}
}
}
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS);
}
/* Lests check if all the visible screen is going to be covered by records. */
let lowerEnd = leftDate.valueOf();
let upperEnd = rightDate.valueOf();
setTimeout(recalculate, 2000);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingDailyFiles -> err = " + err.stack); }
}
}
function recalculateUsingMarketFiles() {
try {
if (marketFile === undefined) { return; } // Initialization not complete yet.
let daysOnSides = getSideDays(timeFrame);
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem);
let dateDiff = rightDate.valueOf() - leftDate.valueOf();
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5);
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5);
records = [];
for (let i = 0; i < marketFile.length; i++) {
let record = {};
record.begin = marketFile[i][0];
record.end = marketFile[i][1];
record.type = marketFile[i][2];
record.rate = marketFile[i][3];
record.amount = marketFile[i][4];
record.balanceA = marketFile[i][5];
record.balanceB = marketFile[i][6];
record.profit = marketFile[i][7];
record.lastTradeProfitLoss = marketFile[i][8];
record.stopLoss = marketFile[i][9];
record.roundtrips = marketFile[i][10];
record.hits = marketFile[i][11];
record.fails = marketFile[i][12];
record.hitRatio = marketFile[i][13];
record.ROI = marketFile[i][14];
record.periods = marketFile[i][15];
record.days = marketFile[i][16];
record.anualizedRateOfReturn = marketFile[i][17];
record.sellRate = marketFile[i][18];
record.lastTradeROI = marketFile[i][19];
record.strategy = marketFile[i][20];
record.strategyPhase = marketFile[i][21];
record.takeProfit = marketFile[i][22];
record.stopLossPhase = marketFile[i][23];
record.takeProfitPhase = marketFile[i][24];
record.positionSize = marketFile[i][26]; // 25 is the message for the executor
record.initialBalanceA = marketFile[i][27];
record.minimunBalanceA = marketFile[i][28];
record.maximunBalanceA = marketFile[i][29];
record.initialBalanceB = marketFile[i][30];
record.minimunBalanceB = marketFile[i][31];
record.maximunBalanceB = marketFile[i][32];
record.baseAsset = marketFile[i][33];
record.positionPeriods = marketFile[i][34];
record.positionDays = marketFile[i][35];
if (
(record.begin >= leftDate.valueOf() && record.end <= rightDate.valueOf()) &&
(record.begin >= coordinateSystem.min.x && record.end <= coordinateSystem.max.x)
) {
records.push(record);
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
thisObject.currentRecord = record;
thisObject.container.eventHandler.raiseEvent("Current Record Changed", thisObject.currentRecord);
}
}
}
//console.log("Olivia > recalculateUsingMarketFiles > total records generated : " + records.length);
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] recalculateUsingMarketFiles -> err = " + err.stack); }
}
}
function plotChart() {
try {
let record;
/* Now we calculate and plot the records */
for (let i = 0; i < records.length; i++) { // We do not start in 0 so as to be able to read the previous record i - 1
record = records[i];
/* Send the current record to the panel */
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
let eventRecords = {
currentRecord: record
};
thisObject.container.eventHandler.raiseEvent("Current Record Changed", eventRecords);
}
let stopLossPhase = -1;
let takeProfitPhase = -1;
if (i > 0) {
if (record.stopLossPhase !== records[i - 1].stopLossPhase) {
stopLossPhase = record.stopLossPhase;
}
if (record.takeProfitPhase !== records[i - 1].takeProfitPhase) {
takeProfitPhase = record.takeProfitPhase;
}
}
let recordPoint1 = {
x: record.begin + timeFrame / 7 * 1.5,
y: thisObject.container.frame.height
};
let recordPoint2 = {
x: record.end + timeFrame / 7 * 5.5,
y: thisObject.container.frame.height
};
let recordPoint3 = {
x: recordPoint1.x + (recordPoint2.x - recordPoint1.x) / 2,
y: 0
};
let recordPoint4 = {
x: record.begin + timeFrame / 7 * 1.5,
y: thisObject.container.frame.height * 100
};
let recordPoint5 = {
x: record.begin,
y: record.stopLoss
};
let recordPoint6 = {
x: record.end,
y: record.stopLoss
};
if (record.stopLoss === 0) { // Put these points out of range if stopLoss is zero.
recordPoint5.x = 0;
recordPoint6.x = 0;
}
let recordPoint7 = {
x: record.begin,
y: record.sellRate
};
let recordPoint8 = {
x: record.end,
y: record.sellRate
};
if (record.sellRate === 0) { // Put these points out of range if sellRate is zero.
recordPoint7.x = 0;
recordPoint8.x = 0;
}
let recordPoint9 = {
x: record.begin,
y: record.takeProfit
};
let recordPoint10 = {
x: record.end,
y: record.takeProfit
};
if (record.takeProfit === 0) { // Put these points out of range if takeProfit is zero.
recordPoint9.x = 0;
recordPoint10.x = 0;
}
recordPoint1 = coordinateSystem.transformThisPoint(recordPoint1);
recordPoint2 = coordinateSystem.transformThisPoint(recordPoint2);
recordPoint3 = coordinateSystem.transformThisPoint(recordPoint3);
recordPoint4 = coordinateSystem.transformThisPoint(recordPoint4);
recordPoint5 = coordinateSystem.transformThisPoint(recordPoint5);
recordPoint6 = coordinateSystem.transformThisPoint(recordPoint6);
recordPoint7 = coordinateSystem.transformThisPoint(recordPoint7);
recordPoint8 = coordinateSystem.transformThisPoint(recordPoint8);
recordPoint9 = coordinateSystem.transformThisPoint(recordPoint9);
recordPoint10 = coordinateSystem.transformThisPoint(recordPoint10);
recordPoint1 = transformThisPoint(recordPoint1, thisObject.container);
recordPoint2 = transformThisPoint(recordPoint2, thisObject.container);
recordPoint3 = transformThisPoint(recordPoint3, thisObject.container);
recordPoint4 = transformThisPoint(recordPoint4, thisObject.container);
recordPoint5 = transformThisPoint(recordPoint5, thisObject.container);
recordPoint6 = transformThisPoint(recordPoint6, thisObject.container);
recordPoint7 = transformThisPoint(recordPoint7, thisObject.container);
recordPoint8 = transformThisPoint(recordPoint8, thisObject.container);
recordPoint9 = transformThisPoint(recordPoint9, thisObject.container);
recordPoint10 = transformThisPoint(recordPoint10, thisObject.container);
recordPoint1 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint1);
recordPoint2 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint2);
recordPoint3 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint3);
recordPoint4 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint4);
recordPoint5 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint5);
recordPoint6 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint6);
recordPoint7 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint7);
recordPoint8 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint8);
recordPoint9 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint9);
recordPoint10 = canvas.chartingSpace.viewport.fitIntoVisibleArea(recordPoint10);
recordPoint1 = thisObject.fitFunction(recordPoint1);
recordPoint2 = thisObject.fitFunction(recordPoint2);
recordPoint3 = thisObject.fitFunction(recordPoint3);
recordPoint4 = thisObject.fitFunction(recordPoint4);
recordPoint5 = thisObject.fitFunction(recordPoint5);
recordPoint6 = thisObject.fitFunction(recordPoint6);
recordPoint7 = thisObject.fitFunction(recordPoint7);
recordPoint8 = thisObject.fitFunction(recordPoint8);
recordPoint9 = thisObject.fitFunction(recordPoint9);
recordPoint10 = thisObject.fitFunction(recordPoint10);
/* Image representing a Record */
let imageOffset = {
x: 0,
y: -30
}
let imageSize = (recordPoint2.x - recordPoint1.x) / 2;
let recordLabel = record.periods.toLocaleString()
if (imageSize < 8) {
if (recordLabel[recordLabel.length - 1] !== '0' || imageSize < 4)
recordLabel = ''
}
let labelOffset = {
x: 0 - imageSize / 2 + 3 + imageOffset.x,
y: -2 - imageOffset.y
}
let imagePosition = {
x: recordPoint4.x + imageOffset.x,
y: 0
}
imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition)
imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize)
if (imageRecord.canDrawIcon === true) {
browserCanvasContext.drawImage(imageRecord, imagePosition.x, imagePosition.y, imageSize, imageSize);
browserCanvasContext.save();
browserCanvasContext.translate(recordPoint3.x + labelOffset.x, recordPoint3.y + labelOffset.y);
browserCanvasContext.rotate(-Math.PI / 2);
printLabel(recordLabel, 0, 0, '1', 8);
browserCanvasContext.restore();
}
if (recordPoint5.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || recordPoint5.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue;
}
/* Next we are drawing the position Rate */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(recordPoint7.x, recordPoint7.y);
browserCanvasContext.lineTo(recordPoint8.x, recordPoint8.y);
browserCanvasContext.closePath();
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', 1)';
browserCanvasContext.lineWidth = 1
browserCanvasContext.setLineDash([1, 4])
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
browserCanvasContext.lineWidth = 5
browserCanvasContext.setLineDash([3, 4])
/* highlight the current record */
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
}
browserCanvasContext.stroke()
browserCanvasContext.setLineDash([0, 0])
imageSize = 20;
let imageToDraw;
/* Depending on what is on top, it is also the text and image of the take profit / stop loss */
let yOffset = 0
if (record.takeProfit > record.stopLoss) { yOffset = imageSize * 2 }
/* Next we are drawing the stopLoss floor / ceilling */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(recordPoint5.x, recordPoint5.y);
browserCanvasContext.lineTo(recordPoint6.x, recordPoint6.y);
browserCanvasContext.closePath();
let opacity = 1
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + '';
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
/* highlight the current record */
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
}
browserCanvasContext.lineWidth = 1
browserCanvasContext.stroke()
imagePosition = {
x: recordPoint6.x - imageSize,
y: recordPoint6.y - imageSize * 1.25 + yOffset
}
imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition)
imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize)
let labelPosition = {
x: recordPoint6.x - imageSize / 2 - 3,
y: recordPoint6.y - imageSize * 1.5 + yOffset * 2
}
labelPosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(labelPosition)
labelPosition = thisObject.fitFunction(labelPosition, undefined, 30)
if (imageStopLossPhase.canDrawIcon === true && stopLossPhase > -1) {
browserCanvasContext.drawImage(imageStopLossPhase, imagePosition.x, imagePosition.y, imageSize, imageSize);
printLabel(stopLossPhase, labelPosition.x, labelPosition.y, '1', 9);
}
/* Next we are drawing the Take Profit */
browserCanvasContext.beginPath();
browserCanvasContext.moveTo(recordPoint9.x, recordPoint9.y);
browserCanvasContext.lineTo(recordPoint10.x, recordPoint10.y);
browserCanvasContext.closePath();
opacity = 1
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + opacity + '';
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
/* highlight the current record */
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'; // Current record accroding to time
}
browserCanvasContext.lineWidth = 1
browserCanvasContext.stroke()
imagePosition = {
x: recordPoint10.x - imageSize * 2 / 3,
y: recordPoint10.y + imageSize / 4 - yOffset
}
imagePosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(imagePosition)
imagePosition = thisObject.fitFunction(imagePosition, undefined, 0, 0, imageSize)
labelPosition = {
x: recordPoint10.x - imageSize * 1 / 3,
y: recordPoint10.y + imageSize * 1.9 - yOffset * 2
}
labelPosition = canvas.chartingSpace.viewport.fitIntoVisibleArea(labelPosition)
labelPosition = thisObject.fitFunction(labelPosition, undefined, 30)
if (imageTakeProfitPhase.canDrawIcon === true && takeProfitPhase > -1) {
browserCanvasContext.drawImage(imageTakeProfitPhase, imagePosition.x, imagePosition.y, imageSize, imageSize);
printLabel(takeProfitPhase, labelPosition.x, labelPosition.y, '1', 9);
}
}
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] plotChart -> err = " + err.stack); }
}
}
function onViewportZoomChanged(event) {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportZoomChanged -> err = " + err.stack); }
}
}
function onViewportPositionChanged(event) {
try {
if (event !== undefined) {
if (event.recalculate === true) {
recalculate()
return
}
}
if (Math.random() * 100 > 95) {
recalculate()
};
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onViewportPositionChanged -> err = " + err.stack); }
}
}
function onDragFinished() {
try {
recalculate();
} catch (err) {
if (ERROR_LOG === true) { logger.write("[ERROR] onDragFinished -> err = " + err.stack); }
}
}
}
@@ -0,0 +1,324 @@
function newMastersPlottersTradingSimulationTradingSimulationTradingSimulationPanel() {
let thisObject = {
fitFunction: undefined,
onRecordChange: onRecordChange,
container: undefined,
session: undefined,
isVisible: true,
draw: draw,
getContainer: getContainer,
initialize: initialize,
finalize: finalize
};
let container = newContainer();
container.initialize();
thisObject.container = container;
container.frame.containerName = "Simulation";
let currentRecord;
let lastRecord;
let panelTabButton
return thisObject;
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL * 1;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * 2.5;
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 1;
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topRight.y;
panelTabButton = newPanelTabButton()
panelTabButton.parentContainer = thisObject.container
panelTabButton.container.frame.parentFrame = thisObject.container.frame
panelTabButton.fitFunction = thisObject.fitFunction
panelTabButton.initialize()
}
function finalize() {
thisObject.session = undefined
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.onRecordChange = undefined
thisObject.isVisible = undefined
currentRecord = undefined
lastRecord = undefined
panelTabButton = undefined
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = panelTabButton.getContainer(point)
if (container !== undefined) { return container }
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
let checkPoint = {
x: point.x,
y: point.y
}
checkPoint = thisObject.fitFunction(checkPoint)
if (point.x === checkPoint.x && point.y === checkPoint.y) {
return thisObject.container;
}
}
}
function onRecordChange(records) {
currentRecord = records.currentRecord;
lastRecord = records.lastRecord
}
function draw() {
if (thisObject.isVisible !== true) { return }
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction);
plotCurrentRecordInfo();
panelTabButton.draw()
/* Define panel name */
if (thisObject.session !== undefined) {
const MAX_LABEL_LENGTH = 25
if (thisObject.session.name.length > MAX_LABEL_LENGTH) {
container.frame.containerName = thisObject.session.name.substring(0, MAX_LABEL_LENGTH) + '...'
} else {
container.frame.containerName = thisObject.session.name
}
}
}
function plotCurrentRecordInfo() {
if (currentRecord === undefined) { return; }
const frameBodyHeight = thisObject.container.frame.getBodyHeight();
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight;
const X_AXIS = thisObject.container.frame.width / 2;
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2;
/* put the labels with the records values */
browserCanvasContext.beginPath();
let y = 0;
let increment = 0.024;
y = y + increment;
y = y + increment;
printLabel('Current Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14);
y = y + increment;
printLabel('Size', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.positionSize, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Rate', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.sellRate, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Stop', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.stopLoss, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Take Profit', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.takeProfit, X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Periods', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.positionPeriods), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Days', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.positionDays).toFixed(2), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
y = y + increment;
printLabel('Latest Position', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1', 14);
y = y + increment;
printLabel('Profit / Loss', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.lastTradeProfitLoss), X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('ROI', X_AXIS, frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.lastTradeROI).toFixed(2) + ' % ', X_AXIS, frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
y = y + increment;
printLabel('Partial Results', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1', 14);
y = y + increment;
printLabel('Profit / Loss', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.profit, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Trades', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.roundtrips, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Hits', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.hits, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Fails', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.fails, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Hit Ratio', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.hitRatio * 100).toFixed(2) + ' %', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('ROI', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.ROI * 100).toFixed(2) + ' %', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Periods', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.periods, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Days', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel(currentRecord.days, X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
y = y + increment;
printLabel('Anualized Rate of Return', X_AXIS , frameTitleHeight + frameBodyHeight * y, '0.60');
y = y + increment;
printLabel((currentRecord.anualizedRateOfReturn * 100).toFixed(2) + ' %', X_AXIS , frameTitleHeight + frameBodyHeight * y, '1.00', 14);
function printLabel(labelToPrint, x, y, opacity, fontSize) {
let labelPoint;
if (fontSize === undefined) { fontSize = 10 };
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY;
let label = '' + labelToPrint;
if (isNaN(label) === false) {
label = Number(label).toLocaleString();
if ((label === "-0" || label === "0") && (labelToPrint !== 0) && (labelToPrint !== "0.00")) {
label = Number(labelToPrint).toFixed(6);
}
}
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
labelPoint = {
x: x - xOffset,
y: y
};
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint);
labelPoint = thisObject.fitFunction(labelPoint)
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')';
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
}
browserCanvasContext.closePath();
browserCanvasContext.fill();
/* Parameters */
let params
let paramsArray = []
let positionTaken = false
if (currentRecord.positionSize > 0) {
positionTaken = true
}
params = {}
params.VALUE = currentRecord.balanceA;
params.INIT_VALUE = currentRecord.initialBalanceA
params.MIN_VALUE = currentRecord.minimunBalanceA
params.MAX_VALUE = currentRecord.maximunBalanceA
params.ASSET_LABEL = 'Asset A'
params.ASSET_NAME = DEFAULT_MARKET.assetB
params.LEFT_OFFSET = 100
params.POSITION_TAKEN = positionTaken
params.BASE_ASSET = currentRecord.baseAsset
params.DECIMALS = 4
paramsArray.push(params)
params = {}
params.VALUE = currentRecord.balanceB;
params.MIN_VALUE = currentRecord.minimunBalanceB
params.INIT_VALUE = currentRecord.initialBalanceB
params.MAX_VALUE = currentRecord.maximunBalanceB
params.ASSET_LABEL = 'Asset B'
params.ASSET_NAME = DEFAULT_MARKET.assetA
params.LEFT_OFFSET = 220
params.POSITION_TAKEN = positionTaken
params.BASE_ASSET = currentRecord.baseAsset
params.DECIMALS = 2
paramsArray.push(params)
canvas.cockpitSpace.assetBalances.setParamsArray(paramsArray)
}
}
@@ -0,0 +1,56 @@
{
"displayName": "Plotters Trading Simulation",
"codeName": "PlottersTradingSimulation",
"dataMine": "Masters",
"modules": [
{
"codeName": "TradingSimulation",
"moduleName": "TradingSimulation",
"description": "Plots Bollinger Records.",
"profilePicture": "TradingSimulation.png",
"panels": [
{
"codeName": "TradingSimulationPanel",
"moduleName": "TradingSimulationPanel",
"event": "Current Record Changed"
}
]
},
{
"codeName": "Conditions",
"moduleName": "Conditions",
"description": "Plots Simulation Conditions.",
"profilePicture": "Conditions.png",
"panels": []
},
{
"codeName": "Strategies",
"moduleName": "Strategies",
"description": "Plots Strategies.",
"profilePicture": "Strategies.png",
"panels": [
]
},
{
"codeName": "Trades",
"moduleName": "Trades",
"description": "Plots Trades.",
"profilePicture": "Trades.png",
"panels": [
]
},
{
"codeName": "SimulationExecution",
"moduleName": "SimulationExecution",
"description": "Plots Simulation Execution.",
"profilePicture": "SimulationExecution.png",
"panels": [
{
"codeName": "SimulationExecutionPanel",
"moduleName": "SimulationExecutionPanel",
"event": "Current Record Changed"
}
]
}
]
}