Current Folder

This commit is contained in:
Luis Fernando Molina committed 2020-11-20 14:05:59 +01:00
1 parent 8b3382289b
commit cfcada880d
4379 files changed

No files matched your search

@@ -1 +0,0 @@
// Nothing here to run
@@ -1,891 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const INTENSIVE_LOG = false;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands";
const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles from Candles Volumes and produce daily files with bollinger bands.
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
/* Context Variables */
let contextVariables = {
lastBandFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxBandFile: undefined // Datetime of the last file available to be used as an input of this process.
};
let previousDay; // Holds the date of the previous day relative to the processing date.
let processDate; // Holds the processing date.
getContextVariables();
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); }
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS);
/* Second, we get the report from Candles Volumes, to know when the marted ends. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxBandFile = new Date(thisReport.lastFile.valueOf());
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Daily"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastBandFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastBandFile = new Date(contextVariables.lastBandFile.valueOf() + ONE_DAY_IN_MILISECONDS);
buildBands();
return;
}
contextVariables.lastBandFile = new Date(thisReport.lastFile);
/*
The bands objects needs to be calculated over more than one day in some situations. In order to simplify the calculations we will
always load the last 2 days of candles, and run the calculations on that array.
1. So first we will load the candles file of processDay -1.
2. Secondly we will load the candles file of processDay.
After reading these two files we will add all candles to a unique array.
*/
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile !== undefined"); }
buildBands();
return;
} else {
/*
In the case when there is no status report, we take the date of the file with the first trades as the begining of the market. Then we will
go one day further in time, so that the previous day does fine a file at the begining of the market.
*/
contextVariables.lastBandFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastBandFile = new Date(contextVariables.lastBandFile.valueOf() + ONE_DAY_IN_MILISECONDS);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile === undefined"); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> contextVariables.lastBandFile = " + contextVariables.lastBandFile); }
buildBands();
return;
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> Entering function."); }
let n;
processDate = new Date(contextVariables.lastBandFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop.
let fromDate = new Date(processDate.valueOf())
let lastDate = new Date()
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> processDate = " + processDate); }
advanceTime();
function advanceTime() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> Entering function."); }
processDate = new Date(processDate.valueOf() + ONE_DAY_IN_MILISECONDS);
previousDay = new Date(processDate.valueOf() - ONE_DAY_IN_MILISECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> processDate = " + processDate); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> previousDay = " + previousDay); }
/* Validation that we are not going past the head of the market. */
if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) {
const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + ".";
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> " + logText); }
callBackFunction(global.DEFAULT_OK_RESPONSE);
return;
}
/* Telling the world we are alive and doing well */
let currentDateString = processDate.getUTCFullYear() + '-' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processDate.getUTCDate(), 2);
let currentDate = new Date(processDate)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(currentDateString, percentage)
if (global.areEqualDates(currentDate, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
}
periodsLoop();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> advanceTime -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function periodsLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> periodsLoop -> Entering function."); }
/*
We will iterate through all posible timeFrames.
*/
n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function loopBody() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> Entering function."); }
const timeFrame = global.dailyFilePeriods[n][1];
let candles = []; // Here we will put all the candles of the 2 files read.
let previousDayFile;
let processDayFile;
getPreviousDayFile();
function getPreviousDayFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> Entering function."); }
let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Candles-Volumes" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onCurrentDayFileReceived);
function onCurrentDayFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> text = " + text); }
previousDayFile = JSON.parse(text);
getProcessDayFile()
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function getProcessDayFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> Entering function."); }
let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Candles-Volumes" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onCurrentDayFileReceived);
function onCurrentDayFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> text = " + text); }
processDayFile = JSON.parse(text);
buildBands();
} catch (err) {
if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) {
processDayFile = []; // we are past the head of the market, then no worries if this file is non existent.
buildBands();
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> buildBands -> Entering function."); }
addCandlesToSingleArray(previousDayFile);
addCandlesToSingleArray(processDayFile);
calculateBands();
function addCandlesToSingleArray(candlesFile) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> buildBands -> addCandlesToSingleArray -> Entering function."); }
for (let i = 0; i < candlesFile.length; i++) {
let candle = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined,
direction: undefined
};
candle.min = candlesFile[i][0];
candle.max = candlesFile[i][1];
candle.open = candlesFile[i][2];
candle.close = candlesFile[i][3];
candle.begin = candlesFile[i][4];
candle.end = candlesFile[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'; }
candles.push(candle);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> addCandlesToSingleArray -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function calculateBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> calculateBands -> Entering function."); }
let bandsArray = [];
let pBArray = [];
let numberOfPeriodsBB = 20;
let numberOfStandardDeviations = 2;
let numberOfPeriodsPB = 10;
/* Building bands */
let band;
for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them.
/* Calculating the bollinger bands. */
let movingAverage = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the moving average.
movingAverage = movingAverage + candles[j].close;
}
movingAverage = movingAverage / numberOfPeriodsBB;
let standardDeviation = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the standard deviation.
standardDeviation = standardDeviation + Math.pow(candles[j].close - movingAverage, 2);
}
standardDeviation = standardDeviation / numberOfPeriodsBB;
standardDeviation = Math.sqrt(standardDeviation);
if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later.
band = {
begin: candles[i].begin,
end: candles[i].end,
movingAverage: movingAverage,
standardDeviation: standardDeviation,
deviation: standardDeviation * numberOfStandardDeviations
};
/* Will only add to the array the bands of the current day */
if (band.begin >= processDate.valueOf()) { bandsArray.push(band); }
/* Calculating %B */
let lowerBB;
let upperBB;
lowerBB = band.movingAverage - band.deviation;
upperBB = band.movingAverage + band.deviation;
let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100;
/* Moving Average Calculation */
let numberOfPreviousPeriods;
let currentPosition = pBArray.length;
if (currentPosition < numberOfPeriodsPB) { // Avoinding to get into negative array indexes
numberOfPreviousPeriods = currentPosition;
} else {
numberOfPreviousPeriods = numberOfPeriodsPB;
}
movingAverage = 0;
for (let j = currentPosition - numberOfPreviousPeriods; j < currentPosition; j++) { // go through the last numberOfPeriodsPBs to calculate the moving average.
movingAverage = movingAverage + pBArray[j].value;
}
movingAverage = movingAverage + value;
movingAverage = movingAverage / (numberOfPreviousPeriods + 1);
let bandwidth = (upperBB - lowerBB) / band.movingAverage;
let percentageBandwidth = {
begin: candles[i].begin,
end: candles[i].end,
value: value,
movingAverage: movingAverage,
bandwidth: bandwidth
};
/* Will only add to the array the pBs of the current day */
if (percentageBandwidth.begin >= processDate.valueOf()) { pBArray.push(percentageBandwidth); }
}
writeBandsFile(bandsArray, pBArray);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> calculateBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function writeBandsFile(pBands, pPB) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < pBands.length; i++) {
let band = pBands[i];
fileContent = fileContent + separator + '[' +
band.begin + "," +
band.end + "," +
band.movingAverage + "," +
band.standardDeviation + "," +
band.deviation + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2);
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + BOLLINGER_BANDS_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> Content written = " + fileContent);
}
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + "";
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> " + logText); }
writePBFile(pPB);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writePBFile(pPercentageBandwidths) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < pPercentageBandwidths.length; i++) {
let pB = pPercentageBandwidths[i];
fileContent = fileContent + separator + '[' +
pB.begin + "," +
pB.end + "," +
pB.value + "," +
pB.movingAverage + "," +
pB.bandwidth + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2);
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> Content written = " + fileContent);
}
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + "";
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> " + logText); }
controlLoop();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function controlLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> Entering function."); }
n++;
if (n < global.dailyFilePeriods.length) {
loopBody();
} else {
n = 0;
writeDataRanges(onWritten);
function onWritten(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> onWritten -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> controlLoop -> onWritten -> err = " + err.stack);
callBackFunction(err);
return;
}
writeStatusReport(processDate, advanceTime);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> onWritten -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> err.message = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRanges(callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
writeDataRange(contextVariables.firstTradeFile, processDate, BOLLINGER_BANDS_FOLDER_NAME, onBandsBandsDataRangeWritten);
function onBandsBandsDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
writeDataRange(contextVariables.firstTradeFile, processDate, PERCENTAGE_BANDWIDTH_FOLDER_NAME, onPercentageBandwidthDataRangeWritten);
function onPercentageBandwidthDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> onPercentageBandwidthDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
callBack(global.DEFAULT_OK_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); }
let dataRange = {
begin: pBegin.valueOf(),
end: pEnd.valueOf() + ONE_DAY_IN_MILISECONDS
};
let fileContent = JSON.stringify(dataRange);
let fileName = 'Data.Range.json';
let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack);
callBack(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent);
}
callBack(global.DEFAULT_OK_RESPONSE);
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(lastFileDate, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); }
try {
let reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Daily"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err.message = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,495 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands";
const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth";
thisObject = {
initialize: initialize,
start: start
};
let statusDependencies;
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles from Candles Volumes and produce the bollinger bands out of them.
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
buildBands();
function buildBands() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> Entering function."); }
try {
let n;
periodsLoop();
function periodsLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> periodsLoop -> Entering function."); }
/*
We will iterate through all posible periods.
*/
n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function loopBody() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> Entering function."); }
const outputPeriod = global.marketFilesPeriods[n][0];
const timeFrame = global.marketFilesPeriods[n][1];
nextCandleFile();
function nextCandleFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> Entering function."); }
let fileName = "Data.json";
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Candles-Volumes" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
function onFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
return;
}
let marketFile = JSON.parse(text);
let candles = [];
buildCandles();
function buildCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> Entering function."); }
try {
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];
candles.push(candle);
}
buildBands();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> Entering function."); }
let bandsArray = [];
let pBArray = [];
let numberOfPeriodsBB = 20;
let numberOfStandardDeviations = 2;
let numberOfPeriodsPB = 5;
/* Building bands */
let band;
for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them.
let movingAverage = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the moving average.
movingAverage = movingAverage + candles[j].close;
}
movingAverage = movingAverage / numberOfPeriodsBB;
let standardDeviation = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the standard deviation.
standardDeviation = standardDeviation + Math.pow (candles[j].close - movingAverage, 2);
}
standardDeviation = standardDeviation / numberOfPeriodsBB;
standardDeviation = Math.sqrt(standardDeviation);
if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later.
band = {
begin: candles[i].begin,
end: candles[i].end,
movingAverage: movingAverage,
standardDeviation: standardDeviation,
deviation: standardDeviation * numberOfStandardDeviations
};
bandsArray.push(band);
/* Calculating %B */
let lowerBB;
let upperBB;
lowerBB = band.movingAverage - band.deviation;
upperBB = band.movingAverage + band.deviation;
let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100;
/* Moving Average Calculation */
let numberOfPreviousPeriods;
let currentPosition = pBArray.length;
if (currentPosition < numberOfPeriodsPB) { // Avoinding to get into negative array indexes
numberOfPreviousPeriods = currentPosition;
} else {
numberOfPreviousPeriods = numberOfPeriodsPB;
}
movingAverage = 0;
for (let j = currentPosition - numberOfPreviousPeriods; j < currentPosition; j++) { // go through the last numberOfPeriodsPBs to calculate the moving average.
movingAverage = movingAverage + pBArray[j].value;
}
movingAverage = movingAverage + value;
movingAverage = movingAverage / (numberOfPreviousPeriods + 1);
let bandwidth = (upperBB- lowerBB) / band.movingAverage;
let percentageBandwidth = {
begin: candles[i].begin,
end: candles[i].end,
value: value,
movingAverage: movingAverage,
bandwidth: bandwidth
};
pBArray.push(percentageBandwidth);
}
writeBandsFile(bandsArray, pBArray);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeBandsFile(pBands, pPB) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < pBands.length; i++) {
let band = pBands[i];
fileContent = fileContent + separator + '[' +
band.begin + "," +
band.end + "," +
band.movingAverage + "," +
band.standardDeviation + "," +
band.deviation + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + BOLLINGER_BANDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset);
callBackFunction(err);
return;
}
writePBFile(pPB);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writePBFile(pPercentageBandwidths) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < pPercentageBandwidths.length; i++) {
let pB = pPercentageBandwidths[i];
fileContent = fileContent + separator + '[' +
pB.begin + "," +
pB.end + "," +
pB.value + "," +
pB.movingAverage + "," +
pB.bandwidth + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset);
callBackFunction(err);
return;
}
controlLoop();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function controlLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> Entering function."); }
n++;
if (n < global.marketFilesPeriods.length) {
loopBody();
} else {
writeStatusReport(callBackFunction);
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> controlLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
try {
let reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Market"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.save(callBack);
if (global.areEqualDates(bot.processDatetime, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, bot.processDatetime);
}
/* Telling the world we are alive and doing well */
let currentDateString = bot.processDatetime.getUTCFullYear() + '-' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '-' + utilities.pad(bot.processDatetime.getUTCDate(), 2);
let currentDate = new Date(bot.processDatetime)
let lastDate = new Date()
bot.processHeartBeat(currentDateString, global.getPercentage(currentDate, currentDate, lastDate))
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,13 +0,0 @@
{
"name": "AAToma",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Luis-Fernando-Molina",
"license": "ISC",
"dependencies": {
}
}
@@ -1,206 +0,0 @@
{
"displayName": "Bollinger Bands",
"codeName": "Bollinger-Bands",
"type": "Indicator Bot",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"dataMine": "Masters",
"profilePicture": "Bollinger Bands.png",
"processes": [
{
"name": "Multi-Period-Market",
"description": "Produces a single file for each Time Frame covering all the market.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Bollinger-Bands",
"dataSet": "Multi-Period-Market"
},
{
"product": "Percentage-Bandwidth",
"dataSet": "Multi-Period-Market"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Bollinger-Bands",
"process": "Multi-Period-Market"
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Market",
"waitUntilNextUpdate": true
}
]
},
{
"name": "Multi-Period-Daily",
"description": "Produces a Daily File at each Time Frame.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Bollinger-Bands",
"dataSet": "Multi-Period-Daily"
},
{
"product": "Percentage-Bandwidth",
"dataSet": "Multi-Period-Daily"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-Trades"
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Daily",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Bollinger-Bands",
"process": "Multi-Period-Daily"
}
]
}
],
"products": [
{
"codeName": "Bollinger-Bands",
"displayName": "Bollinger Bands",
"description": "Bollinger Bands",
"storageAccount": "Bollinger-Bands",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersBollingerBands",
"moduleName": "BollingerBands"
}
},
{
"codeName": "Percentage-Bandwidth",
"displayName": "Percentage Bandwidth",
"description": "Percentage Bandwidth",
"storageAccount": "Bollinger-Bands",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersBollingerBands",
"moduleName": "PercentageBandwidth"
}
}
]
}
@@ -1 +0,0 @@
// Nothing here to run
@@ -1,812 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const GMT_MILI_SECONDS = '.000 GMT+0000';
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const CANDLES_ONE_MIN = "One-Min";
const VOLUMES_FOLDER_NAME = "Volumes";
const VOLUMES_ONE_MIN = "One-Min";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles and volumes from Exchange Raw Data and produce a file for each day and for each period with candles and volumes.
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
/* Context Variables */
let contextVariables = {
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
getContextVariables();
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); }
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS);
/* Second, we get the report from Exchange Raw Data, to know when the marted ends. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + "00:00" + GMT_SECONDS);
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS);
buildCandles();
return;
} else {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildCandles() {
try {
let fromDate = new Date(contextVariables.lastCandleFile.valueOf())
let lastDate = new Date()
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> Entering function."); }
let outputCandles;
let outputVolumes;
advanceTime();
function advanceTime() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Entering function."); }
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
/* Validation that we are not going past the head of the market. */
if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
callBackFunction(global.DEFAULT_OK_RESPONSE); // Here is where we finish processing and wait for the platform to run this module again.
return;
}
/* Telling the world we are alive and doing well */
let currentDateString = contextVariables.lastCandleFile.getUTCFullYear() + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let currentDate = new Date(contextVariables.lastCandleFile)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(currentDateString, percentage)
if (global.areEqualDates(currentDate, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
}
/*
We prepere the arrays that will accumulate all the information for each output file.
*/
outputCandles = [];
outputVolumes = [];
for (let n = 0; n < global.dailyFilePeriods.length; n++) {
const emptyArray1 = [];
const emptyArray2 = [];
outputCandles.push(emptyArray1);
outputVolumes.push(emptyArray2);
}
periodsLoop();
}
function periodsLoop() {
/*
We will iterate through all posible periods.
*/
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
function loopBody() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Entering function."); }
const outputPeriod = global.dailyFilePeriods[n][0];
const timeFrame = global.dailyFilePeriods[n][1];
nextCandleFile();
function nextCandleFile() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); }
let candlesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') {
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + err.stack);
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
}
const inputCandlesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputCandles = inputFilePeriod / outputPeriod;
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputCandles; i++) {
let outputCandle = {
open: 0,
close: 0,
min: 0,
max: 0,
begin: 0,
end: 0
};
let saveCandle = false;
outputCandle.begin = beginingOutputTime + i * outputPeriod;
outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < candlesFile.length; j++) {
let candle = {
open: candlesFile[j][2],
close: candlesFile[j][3],
min: candlesFile[j][0],
max: candlesFile[j][1],
begin: candlesFile[j][4],
end: candlesFile[j][5]
};
/* Here we discard all the candles out of range. */
if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) {
if (saveCandle === false) { // this will set the value only once.
outputCandle.open = candle.open;
outputCandle.min = candle.min;
outputCandle.max = candle.max;
}
saveCandle = true;
outputCandle.close = candle.close; // only the last one will be saved
if (candle.min < outputCandle.min) {
outputCandle.min = candle.min;
}
if (candle.max > outputCandle.max) {
outputCandle.max = candle.max;
}
}
}
if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range.
outputCandles[n].push(outputCandle);
}
}
nextVolumeFile();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function nextVolumeFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> text = " + text); }
let volumesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
const inputVolumesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputVolumes; i++) {
let outputVolume = {
buy: 0,
sell: 0,
begin: 0,
end: 0
};
let saveVolume = false;
outputVolume.begin = beginingOutputTime + i * outputPeriod;
outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < volumesFile.length; j++) {
let volume = {
buy: volumesFile[j][0],
sell: volumesFile[j][1],
begin: volumesFile[j][2],
end: volumesFile[j][3]
};
/* Here we discard all the Volumes out of range. */
if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) {
saveVolume = true;
outputVolume.buy = outputVolume.buy + volume.buy;
outputVolume.sell = outputVolume.sell + volume.sell;
}
}
if (saveVolume === true) {
outputVolumes[n].push(outputVolume);
}
}
writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> Entering function."); }
n++;
if (n < global.dailyFilePeriods.length) {
loopBody();
} else {
writeDataRanges(onWritten);
function onWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> onWritten -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
writeStatusReport(contextVariables.lastCandleFile, advanceTime);
}
}
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeFiles(candles, volumes, timeFrame, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> Entering function."); }
/*
Here we will write the contents of the Candles and Volumens files.
*/
try {
writeCandles();
function writeCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < candles.length; i++) {
let candle = candles[i];
fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
writeVolumes();
}
}
function writeVolumes() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < volumes.length; i++) {
let candle = volumes[i];
fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
callBack();
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRanges(callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, CANDLES_FOLDER_NAME, onCandlesDataRangeWritten);
function onCandlesDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, VOLUMES_FOLDER_NAME, onVolumeDataRangeWritten);
function onVolumeDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] writeDataRanges -> writeDataRanges -> onVolumeDataRangeWritten -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onVolumeDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
callBack(global.DEFAULT_OK_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); }
let dataRange = {
begin: pBegin.valueOf(),
end:pEnd.valueOf() + ONE_DAY_IN_MILISECONDS
};
let fileContent = JSON.stringify(dataRange);
let fileName = 'Data.Range.json';
let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack);
callBack(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent);
}
let key = bot.dataMine + "-" + bot.codeName + "-" + pProductFolder + "-" + bot.exchange + "-" + bot.market.baseAsset + '/' + bot.market.quotedAsset
let event = {
dateRange: dataRange
}
global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Data Range Updated', event)
callBack(global.DEFAULT_OK_RESPONSE);
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(lastFileDate, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); }
try {
let reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] 'Start' - ERROR : " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,929 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const GMT_MILI_SECONDS = '.000 GMT+0000';
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const CANDLES_ONE_MIN = "One-Min";
const VOLUMES_FOLDER_NAME = "Volumes";
const VOLUMES_ONE_MIN = "One-Min";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles and volumes from Exchange Raw Data and produce a single Index File for Market Period. But this is the situation:
Exchange Raw Data has a dataset organized with daily files with candles of 1 min. Candles Volumes is writting in this process a single file for each timeFrame for the whole market.
Everytime this process run, must be able to resume its job and process everything pending until reaching the head of the market. So the tactic to do this is the
following:
1. First we need to read the last file written by this process, and load all the information into in-memory arrays. We will then append to this arrays the new
information we will get from Exchange Raw Data.
2. We know from out status report which was the last DAY we processed from Exchange Raw Data, but we must be carefull, because that day mightn not have been complete, if the
last run found the head of the market. That means that we have to be carefull not to append candles that are already there. To simplify what we do is to discard
all candles of the last processed day, and then we can process that full day again adding all the candles.
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
/* Context Variables */
let contextVariables = {
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
getContextVariables();
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); }
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS);
/* Second, we get the report from Exchange Raw Data, to know when the marted ends. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + "00:00" + GMT_SECONDS);
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Market"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS);
findPreviousContent();
return;
} else {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> Double check that you are not running a process that only can be run at noTime mode at a certain month when it is not prepared to do so.");
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function findPreviousContent() {
/*
This is where we read the current files we have produced at previous runs of this same process. We just read all the content and organize it
in arrays and keep them in memory.
*/
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> Entering function."); }
let n = 0 // loop Variable representing each possible period as defined at the periods array.
let allPreviousCandles = [];
let allPreviousVolumes = [];
loopBody();
function loopBody() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> Entering function."); }
let timeFrame = global.marketFilesPeriods[n][1];
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> timeFrame = " + timeFrame); }
let previousCandles;
let previousVolumes;
getCandles();
function getCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> Entering function."); }
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> getting file.");
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> text = " + text); }
let candlesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
previousCandles = candlesFile;
getVolumes();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> fileName = " + fileName);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
}
}
}
function getVolumes() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> Entering function."); }
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> getting file.");
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> text = " + text); }
let volumesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
previousVolumes = volumesFile;
allPreviousCandles.push(previousCandles);
allPreviousVolumes.push(previousVolumes);
controlLoop();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> fileName = " + fileName);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
}
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> controlLoop -> Entering function."); }
n++;
if (n < global.marketFilesPeriods.length) {
loopBody();
} else {
buildCandles(allPreviousCandles, allPreviousVolumes);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildCandles(allPreviousCandles, allPreviousVolumes) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> Entering function."); }
let fromDate = new Date(contextVariables.lastCandleFile.valueOf())
let lastDate = new Date()
/*
Firstly we prepere the arrays that will accumulate all the information for each output file.
*/
let outputCandles = [];
let outputVolumes = [];
for (let n = 0; n < global.marketFilesPeriods.length; n++) {
const emptyArray1 = [];
const emptyArray2 = [];
outputCandles.push(emptyArray1);
outputVolumes.push(emptyArray2);
}
advanceTime();
function advanceTime() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Entering function."); }
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
/* Validation that we are not going past the head of the market. */
if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
callBackFunction(global.DEFAULT_OK_RESPONSE); // Here is where we finish processing and wait for the platform to run this module again.
return;
}
/* Telling the world we are alive and doing well */
let currentDateString = contextVariables.lastCandleFile.getUTCFullYear() + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let currentDate = new Date(contextVariables.lastCandleFile)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(currentDateString, percentage)
if (global.areEqualDates(currentDate, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
}
periodsLoop();
}
function periodsLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> Entering function."); }
/*
We will iterate through all posible periods.
*/
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
function loopBody() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Entering function."); }
let previousCandles;
let previousVolumes;
if (allPreviousCandles !== undefined) {
previousCandles = allPreviousCandles[n];
previousVolumes = allPreviousVolumes[n];
}
const outputPeriod = global.marketFilesPeriods[n][0];
const timeFrame = global.marketFilesPeriods[n][1];
/*
Here we are inside a Loop that is going to advance 1 day at the time, at each pass, will ready one of Exchange Raw Data's daily files and
add all its candles to our in memory arrays. At the first iteration of this loop, we will add the candles that we are carrying
from our previous run, the ones we already have in-memory. You can see below how we discard from those candles the ones that
are belonging to the first day we are processing at this run, that it is exactly the same as the last day processed the privious
run. By discarding these candles, we are ready to run after that standard function that will just add ALL the candles found each
day at Exchange Raw Data.
*/
if (previousCandles !== undefined) {
for (let i = 0; i < previousCandles.length; i++) {
let candle = {
open: previousCandles[i][2],
close: previousCandles[i][3],
min: previousCandles[i][0],
max: previousCandles[i][1],
begin: previousCandles[i][4],
end: previousCandles[i][5]
};
if (candle.end < contextVariables.lastCandleFile.valueOf()) {
outputCandles[n].push(candle);
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Candle # " + i + " @ " + timeFrame + " discarded for closing past the current process time."); }
}
}
allPreviousCandles[n] = []; // erasing these so as not to duplicate them.
}
if (previousVolumes !== undefined) {
for (let i = 0; i < previousVolumes.length; i++) {
let volume = {
begin: previousVolumes[i][2],
end: previousVolumes[i][3],
buy: previousVolumes[i][0],
sell: previousVolumes[i][1]
};
if (volume.end < contextVariables.lastCandleFile.valueOf()) {
outputVolumes[n].push(volume);
} else {
if (FULL_LOG === true) {logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Volume # " + i + " @ " + timeFrame + " discarded for closing past the current process time."); }
}
}
allPreviousVolumes[n] = []; // erasing these so as not to duplicate them.
}
/*
From here on is where every iteration of the loop fully runs. Here is where we read Exchange Raw Data's files and add their content to whatever
we already have in our arrays in-memory. In this way the process will run as many days needed and it should only stop when it reaches
the head of the market.
*/
nextCandleFile();
function nextCandleFile() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); }
let candlesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') {
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + JSON.stringify(err));
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
}
const inputCandlesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputCandles = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
/*
The algorithm that follows is going to agregate candles of 1 min timeFrame read from Exchange Raw Data, into candles of each timeFrame
that Candles Volumes generates. For market files those timePediods goes from 1h to 24hs.
*/
for (let i = 0; i < totalOutputCandles; i++) {
let outputCandle = {
open: 0,
close: 0,
min: 0,
max: 0,
begin: 0,
end: 0
};
let saveCandle = false;
outputCandle.begin = beginingOutputTime + i * outputPeriod;
outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < candlesFile.length; j++) {
let candle = {
open: candlesFile[j][2],
close: candlesFile[j][3],
min: candlesFile[j][0],
max: candlesFile[j][1],
begin: candlesFile[j][4],
end: candlesFile[j][5]
};
/* Here we discard all the candles out of range. */
if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) {
if (saveCandle === false) { // this will set the value only once.
outputCandle.open = candle.open;
outputCandle.min = candle.min;
outputCandle.max = candle.max;
}
saveCandle = true;
outputCandle.close = candle.close; // only the last one will be saved
if (candle.min < outputCandle.min) {
outputCandle.min = candle.min;
}
if (candle.max > outputCandle.max) {
outputCandle.max = candle.max;
}
}
}
if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range.
outputCandles[n].push(outputCandle);
}
}
nextVolumeFile();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function nextVolumeFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> text = " + text); }
let volumesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
const inputVolumesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputVolumes; i++) {
let outputVolume = {
buy: 0,
sell: 0,
begin: 0,
end: 0
};
let saveVolume = false;
outputVolume.begin = beginingOutputTime + i * outputPeriod;
outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < volumesFile.length; j++) {
let volume = {
buy: volumesFile[j][0],
sell: volumesFile[j][1],
begin: volumesFile[j][2],
end: volumesFile[j][3]
};
/* Here we discard all the Volumes out of range. */
if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) {
saveVolume = true;
outputVolume.buy = outputVolume.buy + volume.buy;
outputVolume.sell = outputVolume.sell + volume.sell;
}
}
if (saveVolume === true) {
outputVolumes[n].push(outputVolume);
}
}
writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> Entering function."); }
n++;
if (n < global.marketFilesPeriods.length) {
loopBody();
} else {
writeStatusReport(contextVariables.lastCandleFile, advanceTime);
}
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeFiles(candles, volumes, timeFrame, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> Entering function."); }
/*
Here we will write the contents of the Candles and Volumens files.
*/
try {
writeCandles();
function writeCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < candles.length; i++) {
let candle = candles[i];
fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName );
writeVolumes();
}
}
function writeVolumes() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < volumes.length; i++) {
let candle = volumes[i];
fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
callBack();
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(lastFileDate, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); }
try {
let reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Market"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,12 +0,0 @@
{
"name": "AAOlivia",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Luis-Fernando-Molina",
"license": "ISC",
"dependencies": {}
}
@@ -1,204 +0,0 @@
{
"displayName": "Candles Volumes",
"codeName": "Candles-Volumes",
"type": "Indicator Bot",
"dataMine": "Masters",
"processes": [
{
"name": "Multi-Period-Market",
"description": "Produces different files with multi-period candles and volumes for entire markets.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Candles",
"dataSet": "Multi-Period-Market"
},
{
"product": "Volumes",
"dataSet": "Multi-Period-Market"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs",
"dataSetSection": "Month",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Market"
}
]
},
{
"name": "Multi-Period-Daily",
"description": "Produces different files with multi-period candles and volumes on a daily format.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Candles",
"dataSet": "Multi-Period-Daily"
},
{
"product": "Volumes",
"dataSet": "Multi-Period-Daily"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs",
"dataSetSection": "Month",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Daily"
}
]
}
],
"products": [
{
"codeName": "Candles",
"displayName": "Candles",
"description": "Two sets of files per period that contains candles.",
"storageAccount": "Candles-Volumes",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersCandlesVolumes",
"moduleName": "Candles"
}
},
{
"codeName": "Volumes",
"displayName": "Buy and Sell Volumes",
"description": "Two sets of files per period that contains buy and sell volumes.",
"storageAccount": "Candles-Volumes",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersCandlesVolumes",
"moduleName": "Volumes"
}
}
]
}
@@ -1,8 +0,0 @@
exports.newCommons = function newCommons(bot, logger, UTILITIES) {
let thisObject = {
};
return thisObject;
};
@@ -1,694 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE, STATUS_REPORT, EXCHANGE_API) {
const FULL_LOG = true;
const GMT_SECONDS = ':00.000 GMT+0000';
const MODULE_NAME = "User Bot";
const CANDLES_FOLDER_NAME = "Candles/One-Min";
const VOLUMES_FOLDER_NAME = "Volumes/One-Min";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger)
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies
const ONE_MIN = 60000
const ONE_DAY = ONE_MIN * 60 * 24
const MAX_OHLCVs_PER_EXECUTION = 10000000
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
const ccxt = require('ccxt')
let fetchType = "by Time"
let lastId
let firstId
let allOHLCVs = []
let thisReport;
let since
let initialProcessTimestamp
let beginingOfMarket
let lastFile
let exchangeId
let options = {}
let rateLimit = 500
let exchange
let uiStartDate = new Date(bot.uiStartDate)
let fisrtTimeThisProcessRun = false
let limit = 1000 // This is the default value
let hostname
let lastCandleOfTheDay
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
let exchangeClass
try {
logger.fileName = MODULE_NAME;
logger.initialize();
statusDependencies = pStatusDependencies;
exchangeId = bot.exchange.toLowerCase().replace(' ', '')
/* Applying the parameters defined by the user at the Exchange Node Config */
if (bot.exchangeNode.config.API !== undefined) {
for (let i = 0; i < bot.exchangeNode.config.API.length; i++) {
if (bot.exchangeNode.config.API[i].method === 'fetch_ohlcv') {
if (bot.exchangeNode.config.API[i].class !== undefined) {
exchangeId = bot.exchangeNode.config.API[i].class
}
if (bot.exchangeNode.config.API[i].fetchOHLCVsMethod !== undefined) {
options = {
'fetchOHLCVsMethod': bot.exchangeNode.config.API[i].fetchOHLCVsMethod
}
}
if (bot.exchangeNode.config.API[i].firstId !== undefined) {
firstId = bot.exchangeNode.config.API[i].firstId
}
if (bot.exchangeNode.config.API[i].rateLimit !== undefined) {
rateLimit = bot.exchangeNode.config.API[i].rateLimit
}
if (bot.exchangeNode.config.API[i].hostname !== undefined) {
hostname = bot.exchangeNode.config.API[i].hostname
}
if (bot.exchangeNode.config.API[i].fetchType !== undefined) {
fetchType = bot.exchangeNode.config.API[i].fetchType
}
}
}
}
if (bot.config.fetchLimit !== undefined) {
limit = bot.config.fetchLimit
}
let key = process.env.KEY
let secret = process.env.SECRET
if (key === "undefined") { key = undefined }
if (secret === "undefined") { secret = undefined }
exchangeClass = ccxt[exchangeId]
const exchangeConstructorParams = {
'apiKey': key,
'secret': secret,
'timeout': 30000,
'enableRateLimit': true,
verbose: false,
options: options
}
if (rateLimit !== undefined) {
exchangeConstructorParams.rateLimit = rateLimit
}
if (hostname !== undefined) {
exchangeConstructorParams.hostname = hostname
}
exchange = new exchangeClass(exchangeConstructorParams)
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
/* CCXT Supported Exchanges */
console.log('CCXT Library current supported exchanges:')
for (const property in ccxt) {
console.log(`${property}`);
}
console.log('For more info please check: https://github.com/ccxt/ccxt/wiki/Manual')
console.log('Exchange Class ' + exchangeId)
console.log(exchangeClass)
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function start(callBackFunction) {
try {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
let abort = false
begin()
async function begin() {
getContextVariables()
if (abort === true) { return }
await getFirstId()
await getOHLCVs()
if (abort === true) { return }
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
await saveOHLCVs()
}
function getContextVariables() {
try {
let reportKey;
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey)
if (thisReport.file.beginingOfMarket !== undefined) { // This means this is not the first time this process run.
beginingOfMarket = new Date(thisReport.file.beginingOfMarket.year + "-" + thisReport.file.beginingOfMarket.month + "-" + thisReport.file.beginingOfMarket.days + " " + thisReport.file.beginingOfMarket.hours + ":" + thisReport.file.beginingOfMarket.minutes + GMT_SECONDS);
lastFile = new Date(thisReport.file.lastFile.year + "-" + thisReport.file.lastFile.month + "-" + thisReport.file.lastFile.days + " " + thisReport.file.lastFile.hours + ":" + thisReport.file.lastFile.minutes + GMT_SECONDS);
lastId = thisReport.file.lastId
lastCandleOfTheDay = thisReport.file.lastCandleOfTheDay
} else { // This means this is the first time this process run.
fisrtTimeThisProcessRun = true
beginingOfMarket = new Date(uiStartDate.valueOf())
}
defineSince()
function defineSince() {
if (thisReport.file.uiStartDate === undefined) {
thisReport.file.uiStartDate = uiStartDate
} else {
thisReport.file.uiStartDate = new Date(thisReport.file.uiStartDate)
}
if (uiStartDate.valueOf() !== thisReport.file.uiStartDate.valueOf()) {
since = uiStartDate.valueOf()
initialProcessTimestamp = since
fisrtTimeThisProcessRun = true
beginingOfMarket = new Date(uiStartDate.valueOf())
} else {
if (lastFile !== undefined) {
since = lastFile.valueOf()
initialProcessTimestamp = lastFile.valueOf()
} else {
since = uiStartDate.valueOf()
initialProcessTimestamp = uiStartDate.valueOf()
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot Status Dependencies. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
async function getFirstId() {
try {
/* We need the first id only when we are going to fetch trades based on id and it is the first time the process runs*/
if (fetchType !== "by Id") { return }
if (lastId !== undefined) { return }
lastId = 0
return
/*
const limit = 1
const exchangeClass = ccxt[exchangeId]
const exchange = new exchangeClass({
'timeout': 30000,
'enableRateLimit': true,
verbose: false
})
const OHLCVs = await exchange.fetchOHLCV(symbol, '1m', since, limit, undefined)
let lastRecord = OHLCVs[OHLCVs.length - 1]
lastId = lastRecord.info[firstId]
*/
} catch (err) {
/* If something fails trying to get an id close to since, we just will continue without an id.*/
}
}
async function getOHLCVs() {
try {
let lastOHLCVKey = ''
let params = undefined
let previousSince
let fromDate = new Date(since)
let lastDate = new Date()
while (true) {
/* Reporting we are doing well */
function heartBeat(noNewInternalLoop) {
let processingDate = new Date(since)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> Fetching OHLCVs @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit) }
let heartBeatText = "Fetching " + allOHLCVs.length.toFixed(0) + " OHLCVs from " + bot.exchange + " " + symbol + " @ " + processingDate
let currentDate = new Date(since)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(heartBeatText, percentage) // tell the world we are alive and doing well
if (global.areEqualDates(currentDate, new Date()) === false) {
if (noNewInternalLoop !== true) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
}
}
}
heartBeat()
/* Defining if we will query the exchange by Date or Id */
if (fetchType === "by Id") {
/*
params = {
'fromId': lastId
}
*/
since = lastId
}
/* Fetching the OHLCVs from the exchange.*/
await new Promise(resolve => setTimeout(resolve, rateLimit)) // rate limit
const OHLCVs = await exchange.fetchOHLCV(symbol, '1m', since, limit, params)
/*
OHLCV Structure
The fetchOHLCV method shown above returns a list (a flat array) of OHLCV candles represented by the following structure:
[
[
1504541580000, // UTC timestamp in milliseconds, integer
4235.4, // (O)pen price, float
4240.6, // (H)ighest price, float
4230.0, // (L)owest price, float
4230.7, // (C)losing price, float
37.72941911 // (V)olume (in terms of the base currency), float
],
...
]
*/
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> OHLCVs Fetched = " + OHLCVs.length) }
if (OHLCVs.length > 0) {
let beginDate = new Date(OHLCVs[0].timestamp)
let endDate = new Date(OHLCVs[OHLCVs.length - 1].timestamp)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> OHLCVs Fetched From " + beginDate + " -> timestamp = " + OHLCVs[0].timestamp) }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> OHLCVs Fetched to " + endDate + " -> timestamp = " + OHLCVs[OHLCVs.length - 1].timestamp) }
if (fisrtTimeThisProcessRun === true) {
let OHLCV = OHLCVs[0]
initialProcessTimestamp = OHLCV[0] // 'timestamp'
beginingOfMarket = new Date(Math.trunc(OHLCV[0] / ONE_DAY) * ONE_DAY) // 'timestamp'
fromDate = new Date(beginingOfMarket.valueOf())
fisrtTimeThisProcessRun = false
}
}
if (OHLCVs.length > 1) {
previousSince = since
since = OHLCVs[OHLCVs.length - 1][0] // 'timestamp'
if (since === previousSince) {
since++ // this prevents requesting in a loop OHLCVs with the same timestamp, that can happen when all the records fetched comes with exactly the same timestamp.
}
lastId = OHLCVs[OHLCVs.length - 1]['id']
for (let i = 0; i < OHLCVs.length; i++) {
let OHLCV = OHLCVs[i]
let OHLCVKey = OHLCV[0] + '-' + OHLCV[1].toFixed(16) + '-' + OHLCV[2].toFixed(16) + '-' + OHLCV[3].toFixed(16) + '-' + OHLCV[4].toFixed(16) + '-' + OHLCV[5].toFixed(16)
if (OHLCVKey !== lastOHLCVKey) {
allOHLCVs.push(OHLCV)
}
lastOHLCVKey = OHLCVKey
}
heartBeat(true)
}
if (
OHLCVs.length < limit - 1 ||
global.STOP_TASK_GRACEFULLY === true ||
allOHLCVs.length >= MAX_OHLCVs_PER_EXECUTION
) {
break
}
}
} catch (err) {
if (err.stack.toString().indexOf('ERR_RATE_LIMIT') >= 0) {
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + bot.exchange + " is saying you are requesting data too often. I will retry the request later, no action is required. To avoid this happening again please increase the rateLimit at the Exchange node config. You might continue seeing this if you are retrieving data from multiple markets at the same time. In this case I tried to get 1 min OHLCVs from " + symbol);
return
}
if (err.stack.toString().indexOf('RequestTimeout') >= 0) {
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + bot.exchange + " is not responding at the moment. I will save the data already fetched and try to reconnect later to fetch the rest of the missing data.");
return
}
if (err.stack.toString().indexOf('ExchangeNotAvailable') >= 0) {
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + bot.exchange + " is not available at the moment. I will save the data already fetched and try to reconnect later to fetch the rest of the missing data.");
return
}
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> err = " + err.stack);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
abort = true
return
}
}
async function saveOHLCVs() {
try {
let candlesFileContent = '['
let volumesFileContent = '['
let previousDay = Math.trunc((initialProcessTimestamp - ONE_DAY) / ONE_DAY)
let currentDay = Math.trunc((initialProcessTimestamp - ONE_DAY) / ONE_DAY)
let needSeparator = false
let error
let separator
let heartBeatCounter = 0
let headOfTheMarketReached = false
let lastCandle = {
begin: 0,
end: 0,
open: 0,
close: 0,
min: 0,
max: 0
}
let lastVolume = {
begin: 0,
end: 0,
buy: 0,
sell: 0
}
let i = 0
lastId = undefined
if (lastCandleOfTheDay !== undefined) {
lastCandle = JSON.parse(JSON.stringify(lastCandleOfTheDay))
}
controlLoop()
function loop() {
let filesToCreate = 0
let filesCreated = 0
/* Go through all possible 1 min candles of a day. */
for (let j = 0; j < 60 * 24; j++) {
let candle = {
begin: currentDay * ONE_DAY + ONE_MIN * j,
end: currentDay * ONE_DAY + ONE_MIN * j + ONE_MIN - 1,
open: lastCandle.close,
close: lastCandle.close,
min: lastCandle.close,
max: lastCandle.close
}
let volume = {
begin: currentDay * ONE_DAY + ONE_MIN * j,
end: currentDay * ONE_DAY + ONE_MIN * j + ONE_MIN - 1,
buy: lastVolume.buy,
sell: lastVolume.sell
}
let processingDate = new Date(candle.begin)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
if (candle.begin > (new Date()).valueOf()) {
/* We stop when the current candle is pointing to a time in the future.*/
headOfTheMarketReached = true
saveFile(currentDay)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> Before Fetch -> Saving OHLCVs @ " + processingDate + " -> i = " + i + " -> total = " + allOHLCVs.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " / " + allOHLCVs.length + " OHLCVs from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
/* We exit the loop and we aint comming back*/
return
}
let OHLCV = { // these values should be overrided unless there are no OHLVCs fetched from the exchange.
timestamp: (new Date()).valueOf() + ONE_MIN,
open: 0,
hight: 0,
low: 0,
close: 0,
volume: 0
}
let record = allOHLCVs[i]
if (record !== undefined) { // can be undefined if there is no candle fetched from the exchange
OHLCV = {
timestamp: record[0],
open: record[1],
hight: record[2],
low: record[3],
close: record[4],
volume: record[5]
}
}
let candleMinute = Math.trunc(candle.begin / ONE_MIN)
let OHLCVMinute
checkOHLCVMinute()
function checkOHLCVMinute() {
/*
Some exchanges return inconsistent data. It is not guaranteed that each candle will have a timeStamp exactly at the begining of an
UTC minute. It is also not guaranteed that the distance between timestamps will be the same. To fix this, we will do this.
*/
OHLCVMinute = Math.trunc(OHLCV.timestamp / ONE_MIN)
if (OHLCVMinute < candleMinute) {
if (i >= allOHLCVs.length - 1) {
/* We run out of OHLCVs, we can not move to the next OHLCV, we leave this function. */
return
}
i++
record = allOHLCVs[i]
OHLCV = {
timestamp: record[0],
open: record[1],
hight: record[2],
low: record[3],
close: record[4],
volume: record[5],
id: record[6]
}
checkOHLCVMinute()
}
}
/* Reporting we are doing well */
heartBeatCounter--
if (heartBeatCounter <= 0) {
heartBeatCounter = 1440
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> After Fetch -> Saving OHLCVs @ " + processingDate + " -> i = " + i + " -> total = " + allOHLCVs.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " / " + allOHLCVs.length + " OHLCVs from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
}
/* End Reporting */
if (candleMinute === OHLCVMinute) {
candle.open = OHLCV.open
candle.close = OHLCV.close
candle.min = OHLCV.low
candle.max = OHLCV.hight
volume.buy = OHLCV.volume / 2
volume.sell = OHLCV.volume / 2
if (i < allOHLCVs.length - 1) {
i++
}
lastId = OHLCV.id
}
lastCandle = candle
if (needSeparator === false) {
needSeparator = true;
separator = '';
} else {
separator = ',';
}
/* Add the candle to the file content.*/
candlesFileContent = candlesFileContent + separator + '[' + candle.min + "," + candle.max + "," + candle.open + "," + candle.close + "," + candle.begin + "," + candle.end + "]";
volumesFileContent = volumesFileContent + separator + '[' + volume.buy + "," + volume.sell + "," + volume.begin + "," + volume.end + "]";
/* We store the last candle of the day in order to have a previous candles during next execution. */
if (j === 1440 - 1) {
lastCandleOfTheDay = JSON.parse(JSON.stringify(candle))
}
}
previousDay = currentDay
if (i > 0) { // Only start saving at the day of the first candle.
saveFile(currentDay)
return
}
controlLoop()
function saveFile(day) {
candlesFileContent = candlesFileContent + ']'
volumesFileContent = volumesFileContent + ']'
let fileName = 'Data.json'
filesToCreate++
fileStorage.createTextFile(getFilePath(day * ONE_DAY, CANDLES_FOLDER_NAME) + '/' + fileName, candlesFileContent + '\n', onFileCreated);
filesToCreate++
fileStorage.createTextFile(getFilePath(day * ONE_DAY, VOLUMES_FOLDER_NAME) + '/' + fileName, volumesFileContent + '\n', onFileCreated);
candlesFileContent = '['
volumesFileContent = '['
needSeparator = false
}
function onFileCreated(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> OHLCVsReadyToBeSaved -> onFileBCreated -> err = " + JSON.stringify(err));
error = err // This allows the loop to be breaked.
return;
}
filesCreated++
lastFile = new Date((currentDay * ONE_DAY))
if (filesCreated === filesToCreate) {
controlLoop()
}
}
function getFilePath(timestamp, folderName) {
let datetime = new Date(timestamp)
let dateForPath = datetime.getUTCFullYear() + '/' +
utilities.pad(datetime.getUTCMonth() + 1, 2) + '/' +
utilities.pad(datetime.getUTCDate(), 2)
let filePath = bot.filePathRoot + "/Output/" + folderName + '/' + dateForPath;
return filePath
}
}
function controlLoop() {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
if (error) {
callBackFunction(error);
return;
}
currentDay++
/*
One possible exit is when we reached the amount of candles downloaded. This not necesary happens at the end of the market
if the process was canceled for any reason at the middle.
*/
if (i >= allOHLCVs.length - 1) {
writeStatusReport()
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> controlLoop -> Exit because i reached the end of the candles array. ") }
return
}
/* The natural exit is at the end of the market */
if (headOfTheMarketReached === false) {
setImmediate(loop)
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> controlLoop -> Exit because we reached the end of the market. ") }
writeStatusReport()
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> saveOHLCVs -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
function writeStatusReport() {
try {
if (lastFile === undefined) { return }
thisReport.file = {
lastFile: {
year: lastFile.getUTCFullYear(),
month: (lastFile.getUTCMonth() + 1),
days: lastFile.getUTCDate(),
hours: lastFile.getUTCHours(),
minutes: lastFile.getUTCMinutes()
},
beginingOfMarket: {
year: beginingOfMarket.getUTCFullYear(),
month: (beginingOfMarket.getUTCMonth() + 1),
days: beginingOfMarket.getUTCDate(),
hours: beginingOfMarket.getUTCHours(),
minutes: beginingOfMarket.getUTCMinutes()
},
uiStartDate: uiStartDate.toUTCString(),
lastCandleOfTheDay: lastCandleOfTheDay
};
if (fetchType === "by Id") {
thisReport.file.lastId = lastId
}
thisReport.save(onSaved);
function onSaved(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,501 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE, STATUS_REPORT, EXCHANGE_API) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const GMT_MILI_SECONDS = '.000 GMT+0000';
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger)
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies
const ONE_MINUTE = 60000
const MAX_TRADES_PER_EXECUTION = 100000
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
const ccxt = require('ccxt')
let allTrades = []
let thisReport;
let since
let initialProcessTimestamp
let beginingOfMarket
let lastFileSaved
let exchangeId
let options = {}
let fetchType = "by Time"
let lastId
let firstId
let rateLimit
let exchange
let uiStartDate = new Date(bot.uiStartDate)
const limit = 1000
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
statusDependencies = pStatusDependencies;
exchangeId = bot.exchange.toLowerCase()
/* Applying the parameters defined by the user at the Exchange node */
if (bot.exchangeNode.config.API !== undefined) {
for (let i = 0; i < bot.exchangeNode.config.API.length; i++) {
if (bot.exchangeNode.config.API[i].method === 'fetchTrades') {
if (bot.exchangeNode.config.API[i].class !== undefined) {
exchangeId = bot.exchangeNode.config.API[i].class
}
if (bot.exchangeNode.config.API[i].fetchType !== undefined) {
fetchType = bot.exchangeNode.config.API[i].fetchType
}
if (bot.exchangeNode.config.API[i].fetchTradesMethod !== undefined) {
options = {
'fetchTradesMethod': bot.exchangeNode.config.API[i].fetchTradesMethod
}
}
if (bot.exchangeNode.config.API[i].firstId !== undefined) {
firstId = bot.exchangeNode.config.API[i].firstId
}
if (bot.exchangeNode.config.API[i].rateLimit !== undefined) {
rateLimit = bot.exchangeNode.config.API[i].rateLimit
}
}
}
}
let key = process.env.KEY
let secret = process.env.SECRET
if (key === "undefined") { key = undefined }
if (secret === "undefined") { secret = undefined }
const exchangeClass = ccxt[exchangeId]
const exchangeConstructorParams = {
'apiKey': key,
'secret': secret,
'timeout': 30000,
'enableRateLimit': true,
verbose: false,
options: options
}
if (rateLimit !== undefined) {
exchangeConstructorParams.rateLimit = rateLimit
}
exchange = new exchangeClass(exchangeConstructorParams)
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function start(callBackFunction) {
try {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
let abort = false
begin()
async function begin() {
getContextVariables()
if (abort === true) { return }
await getFirstId()
await getTrades()
if (abort === true) { return}
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
await saveTrades()
}
function getContextVariables() {
try {
let reportKey;
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-Trades"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey)
if (thisReport.file.beginingOfMarket !== undefined) { // This means this is not the first time this process run.
beginingOfMarket = new Date(thisReport.file.beginingOfMarket.year + "-" + thisReport.file.beginingOfMarket.month + "-" + thisReport.file.beginingOfMarket.days + " " + thisReport.file.beginingOfMarket.hours + ":" + thisReport.file.beginingOfMarket.minutes + GMT_SECONDS);
lastFileSaved = new Date(thisReport.file.lastFileSaved.year + "-" + thisReport.file.lastFileSaved.month + "-" + thisReport.file.lastFileSaved.days + " " + thisReport.file.lastFileSaved.hours + ":" + thisReport.file.lastFileSaved.minutes + GMT_SECONDS);
lastId = thisReport.file.lastId
} else { // This means this is the first time this process run.
beginingOfMarket = new Date(uiStartDate.valueOf())
}
defineSince()
function defineSince() {
if (thisReport.file.uiStartDate === undefined) {
thisReport.file.uiStartDate = uiStartDate
} else {
thisReport.file.uiStartDate = new Date(thisReport.file.uiStartDate)
}
if (uiStartDate.valueOf() !== thisReport.file.uiStartDate.valueOf()) {
since = uiStartDate.valueOf()
initialProcessTimestamp = since
beginingOfMarket = new Date(uiStartDate.valueOf())
} else {
if (lastFileSaved !== undefined) {
since = lastFileSaved.valueOf()
initialProcessTimestamp = lastFileSaved.valueOf()
} else {
since = uiStartDate.valueOf()
initialProcessTimestamp = uiStartDate.valueOf()
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot Status Dependencies. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
async function getFirstId() {
try {
/* We need the first id only when we are going to fetch trades based on id and it is the first time the process runs*/
if (fetchType !== "by Id") { return }
if (lastId !== undefined) { return }
const limit = 1
const exchangeClass = ccxt[exchangeId]
const exchange = new exchangeClass({
'timeout': 30000,
'enableRateLimit': true,
verbose: false
})
const trades = await exchange.fetchTrades(symbol, since, limit, undefined)
let lastRecord = trades[trades.length - 1]
lastId = lastRecord.info[firstId]
} catch (err) {
/* If something fails trying to get an id close to since, we just will continue without an id.*/
}
}
async function getTrades() {
try {
let lastTradeKey = ''
let params = undefined
let previousSince
while (true) {
/* Reporting we are doing well */
let processingDate = new Date(since)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Fetching Trades @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit ) }
bot.processHeartBeat("Fetching " + allTrades.length.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
if (fetchType === "by Id") {
params = {
'fromId': lastId
}
since = undefined
}
/* Fetching the trades from the exchange.*/
const trades = await exchange.fetchTrades(symbol, since, limit, params)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched = " + trades.length) }
if (trades.length > 0) {
let beginDate = new Date(trades[0].timestamp)
let endDate = new Date(trades[trades.length - 1].timestamp)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched From " + beginDate) }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched to " + endDate) }
}
if (trades.length > 1) {
previousSince = since
since = trades[trades.length - 1]['timestamp']
if (since === previousSince) {
since++ // this prevents requesting in a loop trades with the same timestamp, that can happen when all the records fetched comes with exactly the same timestamp.
}
lastId = trades[trades.length - 1]['id']
for (let i = 0; i < trades.length; i++) {
let trade = trades[i]
let tradeKey = trade.id + '-' + trade.timestamp + '-' + trade.side + '-' + trade.price.toFixed(16) + '-' + trade.amount.toFixed(16)
if (tradeKey !== lastTradeKey) {
allTrades.push([trade.timestamp, trade.side, trade.price, trade.amount, trade.id])
}
lastTradeKey = tradeKey
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Fetching Trades @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit) }
bot.processHeartBeat("Fetching " + allTrades.length.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
}
if (
trades.length < limit - 1 ||
global.STOP_TASK_GRACEFULLY === true ||
allTrades.length >= MAX_TRADES_PER_EXECUTION
) {
break
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getTrades -> Retrying Later -> err = " + err.stack);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
abort = true
}
}
async function saveTrades() {
try {
let fileContent = '['
let previousRecordMinute = Math.trunc((initialProcessTimestamp - ONE_MINUTE) / ONE_MINUTE)
let currentRecordMinute
let needSeparator = false
let error
let separator
let heartBeatCounter = 0
let i = -1
lastId = undefined
controlLoop()
function loop() {
let filesToCreate = 0
let filesCreated = 0
let record = allTrades[i]
let trade = {
timestamp: record[0],
side: record[1],
price: record[2],
amount: record[3]
}
if (lastId === undefined) {
lastId = record[4]
}
let processingDate = new Date(trade.timestamp)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
/* Reporting we are doing well */
heartBeatCounter--
if (heartBeatCounter <= 0) {
heartBeatCounter = 1000
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveTrades -> Saving Trades @ " + processingDate + " -> i = " + i + " -> total = " + allTrades.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
}
/* Saving the trades in Files*/
currentRecordMinute = Math.trunc(trade.timestamp / ONE_MINUTE)
if (
currentRecordMinute !== previousRecordMinute
) {
/* There are no more trades at this minute or it is the last trade, so we save the file.*/
saveFile(previousRecordMinute)
lastId = undefined
}
if (needSeparator === false) {
needSeparator = true;
separator = '';
} else {
separator = ',';
}
/* Add the trade to the file content.*/
fileContent = fileContent + separator + '[' + trade.timestamp + ',"' + trade.side + '",' + trade.price + ',' + trade.amount + ']';
if (i === allTrades.length - 1) {
/* This is the last trade, so we save the file.*/
saveFile(currentRecordMinute)
/* It might happen that there are several minutes after the last trade without trades. We need to record empty files for them.*/
if (allTrades.length < MAX_TRADES_PER_EXECUTION) {
let currentTimeMinute = Math.trunc((new Date()).valueOf() / ONE_MINUTE)
if (currentTimeMinute - currentRecordMinute > 1) {
createMissingEmptyFiles(currentRecordMinute, currentTimeMinute)
}
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveTrades -> Saving Trades @ " + processingDate + " -> i = " + i + " -> total = " + allTrades.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
return
}
previousRecordMinute = currentRecordMinute
if (error) {
callBackFunction(error);
return;
}
if (filesToCreate === 0) {
controlLoop()
}
function saveFile(minute) {
fileContent = fileContent + ']'
if (currentRecordMinute - previousRecordMinute > 1) {
createMissingEmptyFiles(previousRecordMinute, currentRecordMinute)
}
let fileName = 'Data.json'
if (previousRecordMinute >= initialProcessTimestamp / ONE_MINUTE) {
filesToCreate++
fileStorage.createTextFile(getFilePath(minute * ONE_MINUTE) + '/' + fileName, fileContent + '\n', onFileCreated);
}
fileContent = '['
needSeparator = false
}
function createMissingEmptyFiles(begin, end) {
/*
If this range is too wide, we will consider this means that the begin is before the begining of this market at this exchange.
In that case we will change the begin and the beginingOfMarket
*/
if ((end - begin) / 60 / 24 > 7) {
begin = end
beginingOfMarket = new Date(end * ONE_MINUTE)
}
for (let j = begin + 1; j < end; j++) {
let fileName = 'Data.json'
filesToCreate++
fileStorage.createTextFile(getFilePath(j * ONE_MINUTE) + '/' + fileName, "[]" + '\n', onFileCreated);
}
}
function onFileCreated(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> tradesReadyToBeSaved -> onFileBCreated -> err = " + JSON.stringify(err));
error = err // This allows the loop to be breaked.
return;
}
filesCreated++
lastFileSaved = new Date((currentRecordMinute * ONE_MINUTE))
if (filesCreated === filesToCreate) {
controlLoop()
}
}
function getFilePath(timestamp) {
let datetime = new Date(timestamp)
let dateForPath = datetime.getUTCFullYear() + '/' +
utilities.pad(datetime.getUTCMonth() + 1, 2) + '/' +
utilities.pad(datetime.getUTCDate(), 2) + '/' +
utilities.pad(datetime.getUTCHours(), 2) + '/' +
utilities.pad(datetime.getUTCMinutes(), 2)
let filePath = bot.filePathRoot + "/Output/" + TRADES_FOLDER_NAME + '/' + dateForPath;
return filePath
}
}
function controlLoop() {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
i++
if (i < allTrades.length) {
setImmediate(loop)
} else {
writeStatusReport()
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> saveTrades -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
function writeStatusReport() {
try {
if (lastFileSaved === undefined) {return}
thisReport.file = {
lastFileSaved: {
year: lastFileSaved.getUTCFullYear(),
month: (lastFileSaved.getUTCMonth() + 1),
days: lastFileSaved.getUTCDate(),
hours: lastFileSaved.getUTCHours(),
minutes: lastFileSaved.getUTCMinutes()
},
beginingOfMarket: {
year: beginingOfMarket.getUTCFullYear(),
month: (beginingOfMarket.getUTCMonth() + 1),
days: beginingOfMarket.getUTCDate(),
hours: beginingOfMarket.getUTCHours(),
minutes: beginingOfMarket.getUTCMinutes()
},
uiStartDate: uiStartDate.toUTCString()
};
if (fetchType === "by Id") {
thisReport.file.lastId = lastId
}
thisReport.save(onSaved);
function onSaved(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,5 +0,0 @@
{
"name": "AACharly",
"version": "1.0.0",
"lockfileVersion": 1
}
@@ -1,12 +0,0 @@
{
"name": "AACharly",
"version": "1.0.0",
"description": "Processes that get the trades from Poloniex.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Luis-Fernando-Molina",
"license": "ISC",
"dependencies": {}
}
@@ -1,139 +0,0 @@
{
"displayName": "Exchange Raw Data",
"codeName": "Exchange-Raw-Data",
"type": "Sensor Bot",
"dataMine": "Masters",
"profilePicture": "Exchange Raw Data.png",
"processes": [
{
"name": "Live-Trades",
"description": "Retrieves the trades done at the current and the previous minute and saves them at the storage account.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "true",
"interval": 60000
}
},
"deadWaitTime": 0,
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Live-Trades"
}
]
},
{
"name": "Historic-Trades",
"description": "Retrieves and saves the historical trades in batches going backwards from the current time until reaching the begining of the market.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "2014",
"maxYear": "2020"
},
"oneMonth": {
"run": "true",
"year": "2018",
"month": "01"
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Live-Trades"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-Trades"
}
]
},
{
"name": "Hole-Fixing",
"description": "Scans the trades saved by the Live Trades and Historic Trades processes searching for missing records. Once a hole is found on the data set, it patches it by retrieving the missing records from the exchange.",
"startMode": {
"allMonths": {
"run": "true",
"minYear": "2019",
"maxYear": "2019"
},
"oneMonth": {
"run": "false",
"year": "2019",
"month": "10"
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Live-Trades",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-Trades"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Hole-Fixing",
"dataSetSection": "Month"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Hole-Fixing"
}
]
}
]
}
@@ -1,190 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE, STATUS_REPORT, EXCHANGE_API) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const MODULE_NAME = "User Bot";
const FOLDER_NAME = "External-Signals";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger)
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function start(callBackFunction) {
try {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
let fileContent
getContextVariables(saveMessages)
function getContextVariables(callBack) {
try {
let reportKey;
reportKey = "Masters" + "-" + "Webhooks" + "-" + "Check-Webhook"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey)
if (thisReport.file.lasrRun !== undefined) {
let fileName = 'Data.json'
let filePath = bot.filePathRoot + "/Output/" + FOLDER_NAME + "/" + 'Single-File'
fileStorage.getTextFile(filePath + '/' + fileName, onFileReceived);
function onFileReceived(err, text) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> onFileReceived -> Could read file. -> filePath = " + filePath + "/" + fileName);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
} else {
fileContent = text
callBack()
}
}
} else { // If there is no status report, we assume there is no previous file or that if there is we will override it.
callBack()
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot Status Dependencies. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
function saveMessages() {
try {
let http = require('http');
let url = 'http://' + process.env.WEB_SERVER_URL +
':' + process.env.HTTP_INTERFACE_PORT +
'/Webhook/Fetch-Messages/' +
bot.exchange + "/" +
bot.market.baseAsset + "-" + bot.market.quotedAsset
http.get(url, onResponse);
function onResponse(response) {
const chunks = []
response.on('data', onMessegesArrived)
response.on('end', onEnd)
function onMessegesArrived(chunk) {
chunks.push(chunk)
}
function onEnd() {
let messages = Buffer.concat(chunks).toString('utf8')
if (fileContent !== undefined) {
// we are going to append the curernt messages to the existing file.
let fileContentArray = JSON.parse(fileContent)
let messagesArray = JSON.parse(messages)
for (let i = 0; i < messagesArray.length; i++) {
let message = messagesArray[i]
fileContentArray.push(message)
}
fileContent = JSON.stringify(fileContentArray)
} else {
// we are going to save the current messages.
fileContent = messages
}
let fileName = 'Data.json'
let filePath = bot.filePathRoot + "/Output/" + FOLDER_NAME + "/" + 'Single-File'
fileStorage.createTextFile(filePath + '/' + fileName, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> saveMessages -> onResponse -> onEnd -> onFileCreated -> Could not save file. -> filePath = " + filePath + "/" + fileName);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
} else {
writeStatusReport()
}
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> saveMessages -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
function writeStatusReport() {
try {
thisReport.file = {
lasrRun: (new Date()).toISOString()
};
thisReport.save(onSaved);
function onSaved(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,8 +0,0 @@
exports.newCommons = function newCommons(bot, logger, UTILITIES) {
let thisObject = {
};
return thisObject;
};
@@ -1,5 +0,0 @@
{
"name": "AACharly",
"version": "1.0.0",
"lockfileVersion": 1
}
@@ -1,9 +0,0 @@
{
"name": "webhooks-sensor-bot",
"version": "1.0.0",
"description": "",
"main": "",
"author": "Luis-Fernando-Molina",
"license": "",
"dependencies": {}
}
@@ -1,51 +0,0 @@
{
"displayName": "Webhooks",
"codeName": "Webhooks",
"type": "Sensor Bot",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"dataMine": "Masters",
"processes": [
{
"name": "Check-Webhook",
"description": "",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "true",
"interval": 60000
}
},
"deadWaitTime": 0,
"normalWaitTime": 30000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Webhooks",
"process": "Check-Webhook"
}
]
}
]
}
@@ -1,726 +0,0 @@
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)
UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = UI.projects.superalgos.spaces.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 = UI.projects.superalgos.utilities.dateRateTransformations.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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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 = [];
let lastBand
while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) {
let stringDate = currentDate.getUTCFullYear() + '-' + pad(currentDate.getUTCMonth() + 1, 2) + '-' + pad(currentDate.getUTCDate(), 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 (lastBand !== undefined) {
if (lastBand.movingAverage > band.movingAverage) { band.direction = "Down" }
if (lastBand.movingAverage < band.movingAverage) { band.direction = "Up" }
if (lastBand.movingAverage === band.movingAverage) { band.direction = "Side" }
}
lastBand = band
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);
}
}
} 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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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 = [];
let lastBand
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 (lastBand !== undefined) {
if (lastBand.movingAverage > band.movingAverage) { band.direction = "Down" }
if (lastBand.movingAverage < band.movingAverage) { band.direction = "Up" }
if (lastBand.movingAverage === band.movingAverage) { band.direction = "Side" }
}
lastBand = band
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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(bandPoint1, thisObject.container);
bandPoint2 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(bandPoint2, thisObject.container);
bandPoint3 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(bandPoint3, thisObject.container);
bandPoint4 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(bandPoint4, thisObject.container);
bandPoint5 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(bandPoint5, thisObject.container);
bandPoint6 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(bandPoint6, thisObject.container);
if (bandPoint2.x < UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomLeft.x || bandPoint1.x > UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue;
}
if (UI.projects.superalgos.spaces.chartingSpace.viewport.zoomTargetLevel < UI.projects.superalgos.globals.zoom.ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT) {
let diffA = bandPoint1.y - UI.projects.superalgos.spaces.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 - UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(bandPoint1);
bandPoint2 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(bandPoint2);
bandPoint3 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(bandPoint3);
bandPoint4 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(bandPoint4);
bandPoint5 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(bandPoint5);
bandPoint6 = UI.projects.superalgos.spaces.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 < UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft.x + 150
||
bandPoint1.x > UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.x - 150
) {
// 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); }
}
}
}
@@ -1,220 +0,0 @@
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 upDownButton
return thisObject;
function finalize() {
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.isVisible = undefined
currentBand = undefined
upDownButton.finalize()
upDownButton = undefined
}
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL;
thisObject.container.frame.position.x = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 3;
thisObject.container.frame.position.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height;
upDownButton = newUpDownButton()
upDownButton.parentContainer = thisObject.container
upDownButton.container.frame.parentFrame = thisObject.container.frame
upDownButton.fitFunction = thisObject.fitFunction
upDownButton.initialize()
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = upDownButton.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();
upDownButton.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.25;
const downLimit = frameTitleHeight + frameBodyHeight * 0.75;
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; }
const leftLimit = thisObject.container.frame.width * 0.40;
const rightLimit = thisObject.container.frame.width * 0.60;
if (bandPoint1.x < leftLimit) { bandPoint1.x = leftLimit; }
if (bandPoint2.x < leftLimit) { bandPoint2.x = leftLimit; }
if (bandPoint3.x > rightLimit) { bandPoint3.x = rightLimit; }
if (bandPoint4.x > rightLimit) { bandPoint4.x = rightLimit; }
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();
/* put the labels with the bands values */
let y;
if (currentBand.innerBand.direction !== undefined) {
UI.projects.superalgos.utilities.drawPrint.printLabel('Direction', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentBand.innerBand.direction, undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction);
}
UI.projects.superalgos.utilities.drawPrint.printLabel('Moving Average', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.15, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentBand.innerBand.movingAverage, undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.20, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction, undefined, undefined, 10);
UI.projects.superalgos.utilities.drawPrint.printLabel('Deviation', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.85, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentBand.innerBand.deviation, X_AXIS, frameTitleHeight + frameBodyHeight * 0.80, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction, undefined, undefined, 10);
UI.projects.superalgos.utilities.drawPrint.printLabel('Standard Deviation', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentBand.innerBand.standardDeviation, undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction, undefined, undefined, 10);
}
}
@@ -1,921 +0,0 @@
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. */
UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = UI.projects.superalgos.spaces.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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, plotAreaFrame);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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.getUTCFullYear() + '-' + pad(currentDate.getUTCMonth() + 1, 2) + '-' + pad(currentDate.getUTCDate(), 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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, plotAreaFrame);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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,
UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - UI.projects.superalgos.spaces.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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(frameCorner1, thisObject.container.frame.container);
frameCorner2 = UI.projects.superalgos.utilities.coordinateTransformations.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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint1, thisObject.container);
pbPoint2 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint2, thisObject.container);
pbPoint3 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint3, thisObject.container);
pbPoint4 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint4, thisObject.container);
pbPoint5 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint5, thisObject.container);
pbPoint6 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint6, thisObject.container);
pbPoint7 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint7, thisObject.container);
pbPoint8 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint8, thisObject.container);
pbPoint9 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint9, thisObject.container);
pbPoint10 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(pbPoint10, thisObject.container);
if (pbPoint1.x < UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomLeft.x || pbPoint2.x > UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (previousPercentBandwidth + pbOffset) * plotAreaViewport.scale.y;
pbPoint2.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (currentPercentBandwidth + pbOffset) * plotAreaViewport.scale.y;
pbPoint3.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (0 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint4.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (30 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint5.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (70 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint6.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (100 * pbChartHeight / 100 + pbOffset) * plotAreaViewport.scale.y;
pbPoint7.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (previousMovingAverage + pbOffset) * plotAreaViewport.scale.y;
pbPoint8.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (currentMovingAverage + pbOffset) * plotAreaViewport.scale.y;
pbPoint9.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (previousBandwidth + pbOffset) * plotAreaViewport.scale.y;
pbPoint10.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - (currentBandwidth + pbOffset) * plotAreaViewport.scale.y;
}
/* Everything must fit within the visible area */
pbPoint1 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint1);
pbPoint2 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint2);
pbPoint3 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint3);
pbPoint4 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint4);
pbPoint5 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint5);
pbPoint6 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint6);
pbPoint7 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint7);
pbPoint8 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint8);
pbPoint9 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(pbPoint9);
pbPoint10 = UI.projects.superalgos.spaces.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); }
}
}
}
@@ -1,28 +0,0 @@
{
"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": [
]
}
]
}
@@ -1,352 +0,0 @@
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 upDownButton
return thisObject;
function finalize() {
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.isVisible = undefined
currentCandle = undefined
upDownButton.finalize()
upDownButton = undefined
}
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL;
thisObject.container.frame.position.x = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width * 2;
thisObject.container.frame.position.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height;
upDownButton = newUpDownButton()
upDownButton.parentContainer = thisObject.container
upDownButton.container.frame.parentFrame = thisObject.container.frame
upDownButton.fitFunction = thisObject.fitFunction
upDownButton.initialize()
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = upDownButton.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();
upDownButton.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;
}
const leftLimit = thisObject.container.frame.width * 0.42;
const rightLimit = thisObject.container.frame.width * 0.58;
if (candlePoint1.x < leftLimit) {
candlePoint1.x = leftLimit;
candlePoint4.x = leftLimit;
}
if (stickPoint1.x < leftLimit) {
stickPoint1.x = leftLimit;
stickPoint4.x = leftLimit;
}
if (candlePoint4.x < leftLimit) {
candlePoint4.x = leftLimit;
candlePoint1.x = leftLimit;
}
if (stickPoint4.x < leftLimit) {
stickPoint4.x = leftLimit;
stickPoint1.x = leftLimit;
}
if (candlePoint2.x > rightLimit) {
candlePoint2.x = rightLimit;
candlePoint3.x = rightLimit;
}
if (stickPoint2.x > rightLimit) {
stickPoint2.x = rightLimit;
stickPoint3.x = rightLimit;
}
if (candlePoint3.x > rightLimit) {
candlePoint3.x = rightLimit;
candlePoint2.x = rightLimit;
}
if (stickPoint3.x > rightLimit) {
stickPoint3.x = rightLimit;
stickPoint2.x = rightLimit;
}
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();
/* put the labels with the candles values */
let y;
UI.projects.superalgos.utilities.drawPrint.printLabel('Max', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentCandle.innerCandle.max, undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel('Min', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentCandle.innerCandle.min, undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction);
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; }
UI.projects.superalgos.utilities.drawPrint.printLabel('Open', undefined, undefined, X_AXIS * 2 / 5, y, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentCandle.innerCandle.open, undefined, undefined, X_AXIS * 2 / 5, y + frameBodyHeight * 0.05, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction);
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; }
UI.projects.superalgos.utilities.drawPrint.printLabel('Close', undefined, undefined, X_AXIS * 7 / 5, y, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentCandle.innerCandle.close, undefined, undefined, X_AXIS * 7 / 5, y + frameBodyHeight * 0.05, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction);
}
}
@@ -1,810 +0,0 @@
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)
UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = UI.projects.superalgos.spaces.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 = UI.projects.superalgos.utilities.dateRateTransformations.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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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.getUTCFullYear() + '-' + pad(currentDate.getUTCMonth() + 1, 2) + '-' + pad(currentDate.getUTCDate(), 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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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 (UI.projects.superalgos.spaces.chartingSpace.viewport.zoomTargetLevel < UI.projects.superalgos.globals.zoom.ZOOM_OUT_THRESHOLD_FOR_PLOTTING_IN_LOW_RESOLUTION) {
if (candles.length > 100) {
lowResolution = true
}
}
if (candles.length > 0) {
/* On screen candles */
let onScreenCandles = []
let mouseCandle
/* 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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.candlePoint1, thisObject.container);
candle.candlePoint2 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.candlePoint2, thisObject.container);
candle.candlePoint3 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.candlePoint3, thisObject.container);
candle.candlePoint4 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.candlePoint4, thisObject.container);
/* Skip the first candle with zero at open */
if (candle.open === 0) { continue }
if (candle.candlePoint2.x < UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomLeft.x || candle.candlePoint1.x > UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.x) {
continue
}
addToOnScreenCandles(candle)
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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.stickPoint1, thisObject.container);
candle.stickPoint2 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.stickPoint2, thisObject.container);
candle.stickPoint3 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.stickPoint3, thisObject.container);
candle.stickPoint4 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(candle.stickPoint4, thisObject.container);
if (UI.projects.superalgos.spaces.chartingSpace.viewport.zoomTargetLevel < UI.projects.superalgos.globals.zoom.ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT) {
let diffA = candle.stickPoint3.y - UI.projects.superalgos.spaces.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 - UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint1);
candle.candlePoint2 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint2);
candle.candlePoint3 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(candle.candlePoint3);
candle.candlePoint4 = UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint1);
candle.stickPoint2 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint2);
candle.stickPoint3 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(candle.stickPoint3);
candle.stickPoint4 = UI.projects.superalgos.spaces.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);
}
function addToOnScreenCandles(candle) {
/* 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); }
}
}
}
@@ -1,256 +0,0 @@
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 upDownButton
return thisObject;
function finalize() {
thisObject.container.finalize()
thisObject.container = undefined
thisObject.fitFunction = undefined
thisObject.isVisible = undefined
currentVolume = undefined
upDownButton.finalize()
upDownButton = undefined
}
function initialize() {
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL;
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL;
thisObject.container.frame.position.x = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width;
thisObject.container.frame.position.y = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height;
upDownButton = newUpDownButton()
upDownButton.parentContainer = thisObject.container
upDownButton.container.frame.parentFrame = thisObject.container.frame
upDownButton.fitFunction = thisObject.fitFunction
upDownButton.initialize()
}
function getContainer(point) {
if (thisObject.isVisible !== true) { return }
let container;
container = upDownButton.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();
upDownButton.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
};
/* Extra bounderies due to constrained space */
const upperLimit = frameTitleHeight + frameBodyHeight * 0.15;
const downLimit = frameTitleHeight + frameBodyHeight * 0.85;
const leftLimit = thisObject.container.frame.width * 0.40;
const rightLimit = thisObject.container.frame.width * 0.60;
if (buyVolumePoint1.y < downLimit) { buyVolumePoint1.y = downLimit; }
if (buyVolumePoint4.y < downLimit) { buyVolumePoint4.y = downLimit; }
if (buyVolumePoint2.y < upperLimit) { buyVolumePoint2.y = upperLimit; }
if (buyVolumePoint3.y < upperLimit) { buyVolumePoint3.y = upperLimit; }
if (buyVolumePoint1.x < leftLimit) { buyVolumePoint1.x = leftLimit; }
if (buyVolumePoint2.x < leftLimit) { buyVolumePoint2.x = leftLimit; }
if (buyVolumePoint3.x > rightLimit) { buyVolumePoint3.x = rightLimit; }
if (buyVolumePoint4.x > rightLimit) { buyVolumePoint4.x = rightLimit; }
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
};
if (sellVolumePoint1.y < upperLimit) { sellVolumePoint1.y = upperLimit; }
if (sellVolumePoint4.y < upperLimit) { sellVolumePoint4.y = upperLimit; }
if (sellVolumePoint2.y > downLimit) { sellVolumePoint2.y = downLimit; }
if (sellVolumePoint3.y > downLimit) { sellVolumePoint3.y = downLimit; }
if (sellVolumePoint1.x < leftLimit) { sellVolumePoint1.x = leftLimit; }
if (sellVolumePoint2.x < leftLimit) { sellVolumePoint2.x = leftLimit; }
if (sellVolumePoint3.x > rightLimit) { sellVolumePoint3.x = rightLimit; }
if (sellVolumePoint4.x > rightLimit) { sellVolumePoint4.x = rightLimit; }
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 */
UI.projects.superalgos.utilities.drawPrint.printLabel('Buy Volume', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.95, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentVolume.innerVolumeBar.amountBuy, undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.90, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel('Sell Volume', undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.05, '0.60', undefined, undefined, 'Center', thisObject.container, thisObject.fitFunction);
UI.projects.superalgos.utilities.drawPrint.printLabel(currentVolume.innerVolumeBar.amountSell, undefined, undefined, X_AXIS, frameTitleHeight + frameBodyHeight * 0.10, '1.00', 15, undefined, 'Center', thisObject.container, thisObject.fitFunction);
}
}
@@ -1,755 +0,0 @@
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)
UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId);
UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.eventHandler.listenToEvent("Zoom Changed", onViewportZoomChanged);
offsetChangedEventSubscriptionId = UI.projects.superalgos.spaces.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 = UI.projects.superalgos.utilities.dateRateTransformations.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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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.getUTCFullYear() + '-' + pad(currentDate.getUTCMonth() + 1, 2) + '-' + pad(currentDate.getUTCDate(), 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 = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem);
let rightDate = UI.projects.superalgos.utilities.dateRateTransformations.getDateFromPointAtBrowserCanvas(UI.projects.superalgos.spaces.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 (UI.projects.superalgos.spaces.chartingSpace.viewport.zoomTargetLevel < UI.projects.superalgos.globals.zoom.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomRight.y - UI.projects.superalgos.spaces.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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(frameCorner1, thisObject.container.frame.container);
frameCorner2 = UI.projects.superalgos.utilities.coordinateTransformations.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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(volumePointA1, thisObject.container);
volumePointA2 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(volumePointA2, thisObject.container);
volumePointA3 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(volumePointA3, thisObject.container);
volumePointA4 = UI.projects.superalgos.utilities.coordinateTransformations.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 < UI.projects.superalgos.spaces.chartingSpace.viewport.visibleArea.bottomLeft.x || volumePointA1.x > UI.projects.superalgos.spaces.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 - UI.projects.superalgos.spaces.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 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(volumePointB1, thisObject.container);
volumePointB2 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(volumePointB2, thisObject.container);
volumePointB3 = UI.projects.superalgos.utilities.coordinateTransformations.transformThisPoint(volumePointB3, thisObject.container);
volumePointB4 = UI.projects.superalgos.utilities.coordinateTransformations.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 - UI.projects.superalgos.spaces.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 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(volumePointA1);
volumePointA2 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(volumePointA2);
volumePointA3 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(volumePointA3);
volumePointA4 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(volumePointA4);
volumePointB1 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(volumePointB1);
volumePointB2 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(volumePointB2);
volumePointB3 = UI.projects.superalgos.spaces.chartingSpace.viewport.fitIntoVisibleArea(volumePointB3);
volumePointB4 = UI.projects.superalgos.spaces.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();
}
}
@@ -1,33 +0,0 @@
{
"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"
}
]
}
]
}