Old bot names refactored
This commit is contained in:
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user