From 136be168a52bda0396c473f3f748265167191a46 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 25 Oct 2019 12:51:54 +0200 Subject: [PATCH 01/38] Indicator Bot Market Daily and Common Implemented --- IndicatorBotCommons.js | 377 ++++++++++++++++++++++++++++++ IndicatorBotDaily.js | 405 +++++++++++++++++++++++++++++++++ IndicatorBotMarket.js | 369 ++++++++++++++++++++++++++++++ IndicatorBotProcessMainLoop.js | 4 +- MultiPeriodDaily.js | 8 +- MultiPeriodMarket.js | 11 +- Project.njsproj | 9 + server.js | 2 + 8 files changed, 1174 insertions(+), 11 deletions(-) create mode 100644 IndicatorBotCommons.js create mode 100644 IndicatorBotDaily.js create mode 100644 IndicatorBotMarket.js diff --git a/IndicatorBotCommons.js b/IndicatorBotCommons.js new file mode 100644 index 000000000..fcadfdbb2 --- /dev/null +++ b/IndicatorBotCommons.js @@ -0,0 +1,377 @@ +exports.newIndicatorBotCommons = function newIndicatorBotCommons(bot, logger, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "Commons"; + const GMT_SECONDS = ':00.000 GMT+0000'; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + let thisObject = { + buildBandsArray: buildBandsArray, + buildChannels: buildChannels, + buildStandardChannels: buildStandardChannels, + buildSubChannels: buildSubChannels, + buildStandardSubChannels: buildStandardSubChannels + }; + + return thisObject; + + function buildBandsArray(dataFile, bands, timePeriod, callBackFunction) { + + try { + + let lastMovingAverage = 0; + const SIDE_TOLERANCE = 0.5 * timePeriod / ONE_DAY_IN_MILISECONDS; + const SMALL_SLOPE = 1.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + const MEDIUM_SLOPE = 2.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + const HIGH_SLOPE = 4.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + + for (let i = 0; i < dataFile.length; i++) { + + let band = { + begin: undefined, + end: undefined, + movingAverage: 10000000000000, + standardDeviation: 0, + deviation: undefined + }; + + band.begin = dataFile[i][0]; + band.end = dataFile[i][1]; + + band.movingAverage = dataFile[i][2]; + band.standardDeviation = dataFile[i][3]; + band.deviation = dataFile[i][4]; + + if (lastMovingAverage > band.movingAverage) { band.direction = 'Down'; } + if (lastMovingAverage < band.movingAverage) { band.direction = 'Up'; } + if (lastMovingAverage === band.movingAverage) { band.direction = 'Side'; } + + let delta = Math.abs(band.movingAverage - lastMovingAverage); + + band.slope = 'Extreme'; + if (delta < band.movingAverage * HIGH_SLOPE / 100) { band.slope = 'Steep'; } + if (delta < band.movingAverage * MEDIUM_SLOPE / 100) { band.slope = 'Medium'; } + if (delta < band.movingAverage * SMALL_SLOPE / 100) { band.slope = 'Gentle'; } + if (delta < band.movingAverage * SIDE_TOLERANCE / 100) { band.slope = 'Side'; } + + bands.push(band); + + lastMovingAverage = band.movingAverage; + } + } + catch (err) { + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildChannels(bands, channels, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildChannels -> Entering function."); } + + try { + + let channel; + + for (let i = 0; i < bands.length; i++) { + + let currentBand = bands[i]; + let nextBand; + + if (i < bands.length - 1) { + nextBand = bands[i + 1]; + + if ( + currentBand.direction === nextBand.direction) { + + if (channel === undefined) { + + channel = { + begin: undefined, + end: undefined, + direction: undefined, + period: 0, + firstMovingAverage: 0, + lastMovingAverage: 0, + firstDeviation: 0, + lastDeviation: 0 + }; + + channel.direction = currentBand.direction; + channel.period = 2; + + channel.begin = currentBand.begin; + channel.end = nextBand.end; + + channel.firstMovingAverage = currentBand.movingAverage; + channel.lastMovingAverage = nextBand.movingAverage; + + channel.firstDeviation = currentBand.deviation; + channel.lastDeviation = nextBand.deviation; + + } else { + + channel.period++; + channel.end = nextBand.end; + channel.lastMovingAverage = nextBand.movingAverage; + channel.lastDeviation = nextBand.deviation; + + } + } else { + + if (channel !== undefined) { + channels.push(channel); + channel = undefined; + } else { + /* The channel has only one period */ + + channel = {}; + + channel.direction = currentBand.direction; + channel.period = 1; + + channel.begin = currentBand.begin; + channel.end = currentBand.end; + + channel.firstMovingAverage = currentBand.movingAverage; + channel.lastMovingAverage = currentBand.movingAverage; + + channel.firstDeviation = currentBand.deviation; + channel.lastDeviation = currentBand.deviation; + + channels.push(channel); + channel = undefined; + } + } + } else { + if (channel !== undefined) { + channel.period++; + channel.end = currentBand.end; + channel.lastMovingAverage = currentBand.movingAverage; + channel.lastDeviation = currentBand.deviation; + channels.push(channel); + channel = undefined; + } else { + /* The channel has only one period */ + + channel = {}; + + channel.direction = currentBand.direction; + channel.period = 1; + + channel.begin = currentBand.begin; + channel.end = currentBand.end; + + channel.firstMovingAverage = currentBand.movingAverage; + channel.lastMovingAverage = currentBand.movingAverage; + + channel.firstDeviation = currentBand.deviation; + channel.lastDeviation = currentBand.deviation; + + channels.push(channel); + channel = undefined; + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildChannels -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildStandardChannels(bands, standardChannels, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildStandardChannels -> Entering function."); } + + try { + + let period = 1 + + for (let i = 1; i < bands.length; i++) { + + let channel; + let currentBand = bands[i]; + let previousBand = bands[i - 1]; + + if (currentBand.direction === previousBand.direction) { + period++ + } else { + period = 1 + } + + channel = { + begin: currentBand.begin, + end: currentBand.end, + direction: currentBand.direction, + period: period + }; + + standardChannels.push(channel) + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildStandardChannels -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildSubChannels(bands, subChannels, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildSubChannels -> Entering function."); } + + try { + + let channel; + + for (let i = 0; i < bands.length; i++) { + + let currentBand = bands[i]; + let nextBand; + + if (i < bands.length - 1) { + nextBand = bands[i + 1]; + + if ( + currentBand.direction === nextBand.direction && + currentBand.slope === nextBand.slope) { + + if (channel === undefined) { + + channel = { + begin: undefined, + end: undefined, + direction: undefined, + slope: undefined, + period: 0, + firstMovingAverage: 0, + lastMovingAverage: 0, + firstDeviation: 0, + lastDeviation: 0 + }; + + channel.direction = currentBand.direction; + channel.slope = currentBand.slope; + channel.period = 2; + + channel.begin = currentBand.begin; + channel.end = nextBand.end; + + channel.firstMovingAverage = currentBand.movingAverage; + channel.lastMovingAverage = nextBand.movingAverage; + + channel.firstDeviation = currentBand.deviation; + channel.lastDeviation = nextBand.deviation; + + } else { + + channel.period++; + channel.end = nextBand.end; + channel.lastMovingAverage = nextBand.movingAverage; + channel.lastDeviation = nextBand.deviation; + + } + } else { + + if (channel !== undefined) { + subChannels.push(channel); + channel = undefined; + } else { + /* The channel has only one period */ + + channel = {}; + + channel.direction = currentBand.direction; + channel.slope = currentBand.slope; + channel.period = 1; + + channel.begin = currentBand.begin; + channel.end = currentBand.end; + + channel.firstMovingAverage = currentBand.movingAverage; + channel.lastMovingAverage = currentBand.movingAverage; + + channel.firstDeviation = currentBand.deviation; + channel.lastDeviation = currentBand.deviation; + + subChannels.push(channel); + channel = undefined; + } + } + + } else { + if (channel !== undefined) { + channel.period++; + channel.end = currentBand.end; + channel.lastMovingAverage = currentBand.movingAverage; + channel.lastDeviation = currentBand.deviation; + subChannels.push(channel); + channel = undefined; + } else { + /* The channel has only one period */ + + channel = {}; + + channel.direction = currentBand.direction; + channel.slope = currentBand.slope; + channel.period = 1; + + channel.begin = currentBand.begin; + channel.end = currentBand.end; + + channel.firstMovingAverage = currentBand.movingAverage; + channel.lastMovingAverage = currentBand.movingAverage; + + channel.firstDeviation = currentBand.deviation; + channel.lastDeviation = currentBand.deviation; + + subChannels.push(channel); + channel = undefined; + } + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildSubChannels -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function buildStandardSubChannels(bands, standardSubChannels, callBackFunction) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildStandardSubChannels -> Entering function."); } + + try { + + let period = 1 + + for (let i = 1; i < bands.length; i++) { + + let channel; + let currentBand = bands[i]; + let previousBand = bands[i - 1]; + + if (currentBand.direction === previousBand.direction && currentBand.slope === previousBand.slope) { + period++ + } else { + period = 1 + } + + channel = { + begin: currentBand.begin, + end: currentBand.end, + direction: currentBand.direction, + slope: currentBand.slope, + period: period + }; + + standardSubChannels.push(channel) + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] buildStandardSubChannels -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/IndicatorBotDaily.js b/IndicatorBotDaily.js new file mode 100644 index 000000000..8d9e0f02a --- /dev/null +++ b/IndicatorBotDaily.js @@ -0,0 +1,405 @@ +exports.newIndicatorBotDaily = function newIndicatorBotDaily(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const BOLLINGER_CHANNELS_FOLDER_NAME = "Bollinger-Channels"; + const BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Channels"; + const BOLLINGER_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Sub-Channels"; + const BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Sub-Channels"; + + const commons = COMMONS.newIndicatorBotCommons(bot, logger, UTILITIES); + + thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let dataDependencies; + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + return thisObject; + + function initialize(pDataDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + dataDependencies = pDataDependencies; + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(dataFiles, timePeriod, outputPeriodLabel, currentDay, startDate, endDate, interExecutionMemory, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = global.MARKET; + let dataFile; + + let bands = []; + let channels = []; + let standardChannels = []; + let subChannels = []; + let standardSubChannels = []; + + dataFile = dataFiles[0]; // We only need the bollinger bands. + + commons.buildBandsArray(dataFile, bands, timePeriod, callBackFunction); + commons.buildChannels(bands, channels, callBackFunction); + commons.buildStandardChannels(bands, standardChannels, callBackFunction); + commons.buildSubChannels(bands, subChannels, callBackFunction); + commons.buildStandardSubChannels(bands, standardSubChannels, callBackFunction); + + writeChannelsFile(); + + function writeChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < channels.length; i++) { + + let channel = channels[i]; + + /* + Here we have an special problem that occurs when an object spans several time peridos. If not taken care of + it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of + its properties. + + To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it + at the next day, in whole, even if it starts in the previous day. + */ + + let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + + if (channel.end < currentDay.valueOf() - 1) { continue; } + if (channel.end === lastInstantOdDay) { continue; } + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + channel.period + "," + + channel.firstMovingAverage + "," + + channel.lastMovingAverage + "," + + channel.firstDeviation + "," + + channel.lastDeviation + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeStandardChannelsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStandardChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < standardChannels.length; i++) { + + let channel = standardChannels[i]; + + if (channel.end < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + channel.period + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeSubChannelsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeSubChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < subChannels.length; i++) { + + let channel = subChannels[i]; + + /* + Here we have an special problem that occurs when an object spans several time peridos. If not taken care of + it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of + its properties. + + To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it + at the next day, in whole, even if it starts in the previous day. + */ + + let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + + if (channel.end < currentDay.valueOf() - 1) { continue; } + if (channel.end === lastInstantOdDay) { continue; } + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + '"' + channel.slope + '"' + "," + + channel.period + "," + + channel.firstMovingAverage + "," + + channel.lastMovingAverage + "," + + channel.firstDeviation + "," + + channel.lastDeviation + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeStandardSubChannelsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStandardSubChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < standardSubChannels.length; i++) { + + let channel = standardSubChannels[i]; + + if (channel.end < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + '"' + channel.slope + '"' + "," + + channel.period + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/IndicatorBotMarket.js b/IndicatorBotMarket.js new file mode 100644 index 000000000..d97e78c94 --- /dev/null +++ b/IndicatorBotMarket.js @@ -0,0 +1,369 @@ +exports.newIndicatorBotMarket = function newIndicatorBotMarket(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "User Bot"; + + const BOLLINGER_CHANNELS_FOLDER_NAME = "Bollinger-Channels"; + const BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Channels"; + const BOLLINGER_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Sub-Channels"; + const BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Sub-Channels"; + + const commons = COMMONS.newIndicatorBotCommons(bot, logger, UTILITIES); + + thisObject = { + initialize: initialize, + start: start + }; + + let dataDependencies; + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + return thisObject; + + function initialize(pDataDependencies, callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + dataDependencies = pDataDependencies; + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(dataFiles, timePeriod, outputPeriodLabel, startDate, endDate, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let market = global.MARKET; + let dataFile; + + let bands = []; + let channels = []; + let standardChannels = []; + let subChannels = []; + let standardSubChannels = []; + + dataFile = dataFiles[0]; // We only need the bollinger bands. + + commons.buildBandsArray(dataFile, bands, timePeriod, callBackFunction); + commons.buildChannels(bands, channels, callBackFunction); + commons.buildStandardChannels(bands, standardChannels, callBackFunction); + commons.buildSubChannels(bands, subChannels, callBackFunction); + commons.buildStandardSubChannels(bands, standardSubChannels, callBackFunction); + + writeChannelsFile(); + + function writeChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < channels.length; i++) { + + let channel = channels[i]; + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + channel.period + "," + + channel.firstMovingAverage + "," + + channel.lastMovingAverage + "," + + channel.firstDeviation + "," + + channel.lastDeviation + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeStandardChannelsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStandardChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < standardChannels.length; i++) { + + let channel = standardChannels[i]; + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + channel.period + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeSubChannelsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeSubChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < subChannels.length; i++) { + + let channel = subChannels[i]; + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + '"' + channel.slope + '"' + "," + + channel.period + "," + + channel.firstMovingAverage + "," + + channel.lastMovingAverage + "," + + channel.firstDeviation + "," + + channel.lastDeviation + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeStandardSubChannelsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStandardSubChannelsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (i = 0; i < standardSubChannels.length; i++) { + + let channel = standardSubChannels[i]; + + fileContent = fileContent + separator + '[' + + + channel.begin + "," + + channel.end + "," + + '"' + channel.direction + '"' + "," + + '"' + channel.slope + '"' + "," + + channel.period + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index dd451faec..04f880ba5 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -253,12 +253,12 @@ switch (processConfig.framework.name) { case 'Multi-Period-Market': { - processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); + processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE); intitializeProcessFramework(); break; } case 'Multi-Period-Daily': { - processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); + processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE); intitializeProcessFramework(); break; } diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 4585fcea9..c0245d73a 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -1,4 +1,4 @@ -exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE) { +exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; @@ -21,7 +21,6 @@ let usertBot; - const FILE_STORAGE = require('./FileStorage.js'); let fileStorage = FILE_STORAGE.newFileStorage(logger); let bootstrappingTheProcess = false @@ -61,7 +60,10 @@ } - usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); + let COMMONS_MODULE = require("./IndicatorBotCommons") + let USER_BOT_MODULE = require("./IndicatorBotDaily") + + usertBot = USER_BOT_MODULE.newIndicatorBotDaily(bot, logger, COMMONS_MODULE, UTILITIES, FILE_STORAGE); usertBot.initialize(dataDependencies, callBackFunction, pAssistant); } catch (err) { diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 99870774e..4747c42fb 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -1,4 +1,4 @@ -exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE) { +exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; @@ -19,9 +19,6 @@ let usertBot; - const FILE_STORAGE = require('./FileStorage.js'); - let fileStorage = FILE_STORAGE.newFileStorage(logger); - let processConfig; return thisObject; @@ -57,7 +54,10 @@ } - usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); + let COMMONS_MODULE = require("./IndicatorBotCommons") + let USER_BOT_MODULE = require("./IndicatorBotMarket") + + usertBot = USER_BOT_MODULE.newIndicatorBotMarket(bot, logger, COMMONS_MODULE, UTILITIES, FILE_STORAGE); usertBot.initialize(dataDependencies, callBackFunction, pAssistant); } catch (err) { @@ -72,7 +72,6 @@ statusDependencies = undefined dataDependencies = undefined usertBot = undefined - fileStorage = undefined processConfig = undefined thisObject = undefined } diff --git a/Project.njsproj b/Project.njsproj index 63fc32b8b..696586b5a 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -25,8 +25,17 @@ Code + + Code + + + Code + + + Code + Code diff --git a/server.js b/server.js index 82a8fcdc7..50c47c578 100644 --- a/server.js +++ b/server.js @@ -77,6 +77,8 @@ else { // I use this section to debug in standalone mode. // olivia argument = '{"type":"Task","name":"Generates 1 min to 24 hs Candles & Volumes","bot":{"type":"Indicator","name":"Olivia","processes":[{"type":"Process","name":"Daily","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Daily"},"id":"68cc8e1b-e94a-477e-82d0-15ecc9f1b9e2"},{"type":"Process","name":"Market","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Market"},"id":"e1ac5e3d-d491-4e90-baf4-d4e5b71a8b1a"}]},"id":"efe36e05-75ea-41b5-8d92-c6b27635c834"}' // bruce argument = ' {"type":"Task","name":"Converts Trades into 1 min Candles & Volumes","bot":{"type":"Indicator","name":"Bruce","processes":[{"type":"Process","name":"New Process","code":{"team":"AAMasters","bot":"AABruce","process":"Single-Period-Daily"},"id":"e184744f-5de0-41c1-ad4c-36960f4ced90"}]},"id":"42758590-a7bd-4712-a5c9-80a3a820c5b3"}' */ + argument = '{"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula"},"processes":[{"type":"Process","subType":"Indicator Process","name":"Daily","code":{"process":"Multi-Period-Daily"},"id":"81f0108b-8af9-4b22-94dd-81ef4a5c8065"},{"type":"Process","subType":"Indicator Process","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c"}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' + try { global.TASK_NODE = JSON.parse(argument) } catch (err) { From e3f2e6f06b6c565023cf2c5a68544bb6261e27fb Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 26 Oct 2019 19:41:09 +0200 Subject: [PATCH 02/38] Draft Code of Indicator Bots and Sensor Refactoring --- IndicatorBotCommons.js | 93 ++++++++- IndicatorBotDaily.js | 8 +- IndicatorBotMarket.js | 338 ++++++++------------------------- IndicatorBotProcessMainLoop.js | 2 +- MultiPeriodDaily.js | 4 +- MultiPeriodMarket.js | 4 +- Root.js | 12 +- Session.js | 4 +- 8 files changed, 190 insertions(+), 275 deletions(-) diff --git a/IndicatorBotCommons.js b/IndicatorBotCommons.js index fcadfdbb2..2fef25f43 100644 --- a/IndicatorBotCommons.js +++ b/IndicatorBotCommons.js @@ -12,11 +12,102 @@ buildChannels: buildChannels, buildStandardChannels: buildStandardChannels, buildSubChannels: buildSubChannels, - buildStandardSubChannels: buildStandardSubChannels + buildStandardSubChannels: buildStandardSubChannels, + buildRecords: buildRecords }; return thisObject; + function buildDependecyArray(dataFile, recordDefinition) { + + let dependecyArray = [] + + for (let i = 0; i < dataFile.length; i++) { + + let record = {} + for (let j = 0; j < recordDefinition.length; j++) { + let property = recordDefinition[j] + record[property.name] = dataFile[i][j] + } + + dependecyArray.push(record); + } + + return dependecyArray + } + + function addCalculatedProperties(dependencyArray, variableName, timePeriod) { + + let dependecyArray = [] + + /* This is Initialization Code */ + let lastMovingAverage = 0; + const SIDE_TOLERANCE = 0.5 * timePeriod / ONE_DAY_IN_MILISECONDS; + const SMALL_SLOPE = 1.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + const MEDIUM_SLOPE = 2.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + const HIGH_SLOPE = 4.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + /* This is Initialization Code */ + + for (let i = 0; i < dataFile.length; i++) { + + let indicator = {} + indicator[variableName] = dataFile[i] + + /* This is Add Properties Code */ + if (lastMovingAverage > indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Down'; } + if (lastMovingAverage < indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Up'; } + if (lastMovingAverage === indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Side'; } + + let delta = Math.abs(indicator.bollingerBand.movingAverage - lastMovingAverage); + + indicator.bollingerBand.slope = 'Extreme'; + if (delta < indicator.bollingerBand.movingAverage * HIGH_SLOPE / 100) { indicator.bollingerBand.slope = 'Steep'; } + if (delta < indicator.bollingerBand.movingAverage * MEDIUM_SLOPE / 100) { indicator.bollingerBand.slope = 'Medium'; } + if (delta < indicator.bollingerBand.movingAverage * SMALL_SLOPE / 100) { indicator.bollingerBand.slope = 'Gentle'; } + if (delta < indicator.bollingerBand.movingAverage * SIDE_TOLERANCE / 100) { indicator.bollingerBand.slope = 'Side'; } + + lastMovingAverage = indicator.bollingerBand.movingAverage; + /* This is Add Properties Code */ + + dependecyArray.push(indicator.bollingerBand); + } + + return dependecyArray + } + + function buildRecords(dataDependency, recordDefinition) { + let records = [] + + /* Initialization Code */ + let period = 1 + /* Initialization Code */ + + for (let i = 0; i < dataDependency.bollinerBands.length; i++) { + + /* Calculations Code*/ + if (i === 0) {continue} + let currentBand = dataDependency.bollinerBands[i]; + let previousBand = dataDependency.bollinerBands[i - 1]; + + if (currentBand.direction === previousBand.direction) { + period++ + } else { + period = 1 + } + /* Calculations Code*/ + + let record = { + begin: currentBand.begin, /* Property Formula Code */ + end: currentBand.end, /* Property Formula Code */ + direction: currentBand.direction, /* Property Formula Code */ + period: period /* Property Formula Code */ + }; + records.push(record) + } + return records + } + + function buildBandsArray(dataFile, bands, timePeriod, callBackFunction) { try { diff --git a/IndicatorBotDaily.js b/IndicatorBotDaily.js index 8d9e0f02a..c06f99347 100644 --- a/IndicatorBotDaily.js +++ b/IndicatorBotDaily.js @@ -20,12 +20,12 @@ let utilities = UTILITIES.newCloudUtilities(bot, logger); - let dataDependencies; let fileStorage = FILE_STORAGE.newFileStorage(logger); + let processConfig; return thisObject; - function initialize(pDataDependencies, callBackFunction) { + function initialize(pProcessConfig, callBackFunction) { try { @@ -34,7 +34,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - dataDependencies = pDataDependencies; + processConfig = pProcessConfig; callBackFunction(global.DEFAULT_OK_RESPONSE); } catch (err) { @@ -79,7 +79,7 @@ let fileContent = ""; - for (i = 0; i < channels.length; i++) { + for (let i = 0; i < channels.length; i++) { let channel = channels[i]; diff --git a/IndicatorBotMarket.js b/IndicatorBotMarket.js index d97e78c94..1fa706c52 100644 --- a/IndicatorBotMarket.js +++ b/IndicatorBotMarket.js @@ -18,12 +18,12 @@ start: start }; - let dataDependencies; let fileStorage = FILE_STORAGE.newFileStorage(logger); + let processConfig; return thisObject; - function initialize(pDataDependencies, callBackFunction) { + function initialize(pProcessConfig, callBackFunction) { try { @@ -32,7 +32,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - dataDependencies = pDataDependencies; + processConfig = pProcessConfig; callBackFunction(global.DEFAULT_OK_RESPONSE); } catch (err) { @@ -64,46 +64,88 @@ commons.buildSubChannels(bands, subChannels, callBackFunction); commons.buildStandardSubChannels(bands, standardSubChannels, callBackFunction); - writeChannelsFile(); + let dataDependency = {} + + for (let i = 0; i < processConfig.dataDependencies.length; i++) { + let dependency = processConfig.dataDependencies[i] + let recordDefinition = processConfig.recordDefinition + let dependencyArray = commons.buildDependencyArray(dataFiles[i], recordDefinition); + dependencyArray = commons.addCalculatedProperties(dependencyArray, dependency.singularVariableName, timePeriod); + dataDependency[dependency.pluralVariableName] = dependencyArray + } - function writeChannelsFile() { + for (let i = 0; i < processConfig.updatesDatasets.length; i++) { + let product = processConfig.updatesDatasets[i].product + let dataset = processConfig.updatesDatasets[i].dataset + let recordDefinition = getRecordDefinition(product) + let records = commons.buildRecords(dataDependency, recordDefinition) + let fileContent = generateFileContent(indicatorData, recordDefinition) + writeFile(product, dataset, fileContent) + } + + let totalFilesWritten = 0 + function anotherFileWritten() { + totalFilesWritten++ + if (totalFilesWritten === processConfig.updatesDatasets.length) { + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } + + function getRecordDefinition(product) { + for (let j = 0; j < bot.products.length; j++) { + if (bot.products[j].codeName === product) { + return bot.products[j].recordDefinition + } + } + } + + function generateFileContent(records, recordDefinition) { try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> generateFileContent -> Entering function."); } let fileContent = ""; + let recordSeparator = ""; - for (i = 0; i < channels.length; i++) { - - let channel = channels[i]; - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - channel.period + "," + - channel.firstMovingAverage + "," + - channel.lastMovingAverage + "," + - channel.firstDeviation + "," + - channel.lastDeviation + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - + for (let i = 0; i < records.length; i++) { + let record = records[i]; + fileContent = fileContent + recordSeparator + '[' + let propertySeparator = "" + for (let j = 0; j < recordDefinition.length; j++) { + let property = recordDefinition[j] + fileContent = fileContent + propertySeparator + if (property.isNumeric !== true) { + fileContent = fileContent + '"' + } + fileContent = fileContent + record[property.name] + if (property.isNumeric !== true) { + fileContent = fileContent + '"' + } + if (recordSeparator === "") { propertySeparator = ","; } + } + fileContent = fileContent + ']' + if (recordSeparator === "") { recordSeparator = ","; } } - fileContent = "[" + fileContent + "]"; + return fileContent + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> generateFileContent -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFile(product, dataset, fileContent) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> Entering function."); } let fileName = '' + market.assetA + '_' + market.assetB + '.json'; let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + let filePath = filePathRoot + "/Output/" + product + "/" + dataset + "/" + outputPeriodLabel; filePath += '/' + fileName fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); @@ -112,254 +154,34 @@ try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> fileContent = " + fileContent); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> fileContent = " + fileContent); } if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); callBackFunction(err); return; } - writeStandardChannelsFile(); + anotherFileWritten(); } catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> err = " + err.stack); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } - - function writeStandardChannelsFile() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; - - let fileContent = ""; - - for (i = 0; i < standardChannels.length; i++) { - - let channel = standardChannels[i]; - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - channel.period + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - - } - - fileContent = "[" + fileContent + "]"; - - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; - filePath += '/' + fileName - - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - writeSubChannelsFile(); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeSubChannelsFile() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; - - let fileContent = ""; - - for (i = 0; i < subChannels.length; i++) { - - let channel = subChannels[i]; - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - '"' + channel.slope + '"' + "," + - channel.period + "," + - channel.firstMovingAverage + "," + - channel.lastMovingAverage + "," + - channel.firstDeviation + "," + - channel.lastDeviation + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - - } - - fileContent = "[" + fileContent + "]"; - - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; - filePath += '/' + fileName - - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - writeStandardSubChannelsFile(); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeStandardSubChannelsFile() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; - - let fileContent = ""; - - for (i = 0; i < standardSubChannels.length; i++) { - - let channel = standardSubChannels[i]; - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - '"' + channel.slope + '"' + "," + - channel.period + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - - } - - fileContent = "[" + fileContent + "]"; - - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; - filePath += '/' + fileName - - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - callBackFunction(global.DEFAULT_OK_RESPONSE); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index 04f880ba5..bd1bd766f 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -568,7 +568,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); } - processFramework.initialize(processConfig, statusDependencies, dataDependencies, undefined, onInizialized); + processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized); function onInizialized(err) { diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index c0245d73a..3c2bf0b47 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -29,7 +29,7 @@ return thisObject; - function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, pAssistant, callBackFunction) { + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) { try { @@ -64,7 +64,7 @@ let USER_BOT_MODULE = require("./IndicatorBotDaily") usertBot = USER_BOT_MODULE.newIndicatorBotDaily(bot, logger, COMMONS_MODULE, UTILITIES, FILE_STORAGE); - usertBot.initialize(dataDependencies, callBackFunction, pAssistant); + usertBot.initialize(processConfig, callBackFunction); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 4747c42fb..999b6673f 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -23,7 +23,7 @@ return thisObject; - function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, pAssistant, callBackFunction) { + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) { try { @@ -58,7 +58,7 @@ let USER_BOT_MODULE = require("./IndicatorBotMarket") usertBot = USER_BOT_MODULE.newIndicatorBotMarket(bot, logger, COMMONS_MODULE, UTILITIES, FILE_STORAGE); - usertBot.initialize(dataDependencies, callBackFunction, pAssistant); + usertBot.initialize(processConfig, callBackFunction); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); diff --git a/Root.js b/Root.js index 8c82a8125..87a90ecc6 100644 --- a/Root.js +++ b/Root.js @@ -146,7 +146,8 @@ if (err.result !== global.DEFAULT_OK_RESPONSE.result) { console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); - + console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> filePath = " + filePath); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> team = " + global.TASK_NODE.bot.code.team); return; } @@ -173,7 +174,6 @@ botConfig.process = global.TASK_NODE.bot.processes[processIndex].code.process; botConfig.debug = {}; - botConfig.processIndex = processIndex botConfig.processNode = global.TASK_NODE.bot.processes[processIndex] /* Loop Counter */ @@ -256,7 +256,7 @@ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> startProcess -> Ready to start process."); } switch (botConfig.type) { - case 'Sensor': { + case 'Sensor Bot': { runSensorBot(newInstanceBotConfig, processConfig, padMonth, year); break; } @@ -295,7 +295,7 @@ }; switch (botConfig.type) { - case 'Sensor': { + case 'Sensor Bot': { runSensorBot(botConfig, processConfig, month, year); break; } @@ -325,7 +325,7 @@ } switch (botConfig.type) { - case 'Sensor': { + case 'Sensor Bot': { runSensorBot(botConfig, processConfig, month, year); break; } @@ -355,7 +355,7 @@ let year = (new Date()).getUTCFullYear(); switch (botConfig.type) { - case 'Sensor': { + case 'Sensor Bot': { runSensorBot(botConfig, processConfig, month, year); break; } diff --git a/Session.js b/Session.js index 2f7b77ab2..9a34d5899 100644 --- a/Session.js +++ b/Session.js @@ -44,7 +44,9 @@ return function runSession(message) { - + + console.log(message.event.definition) + /* We are going to run the Definition comming at the event. */ bot.DEFINITION = JSON.parse(message.event.definition) bot.SESSION = JSON.parse(message.event.session) From dae7b5d665fec49c761f3dde7b9d103c666789c7 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 27 Oct 2019 11:31:35 +0100 Subject: [PATCH 03/38] Trading Engine Refactoring --- Root.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Root.js b/Root.js index 87a90ecc6..a0bef40d7 100644 --- a/Root.js +++ b/Root.js @@ -260,7 +260,7 @@ runSensorBot(newInstanceBotConfig, processConfig, padMonth, year); break; } - case 'Indicator': { + case 'Indicator Bot': { runIndicatorBot(newInstanceBotConfig, processConfig, padMonth, year); break; } @@ -299,7 +299,7 @@ runSensorBot(botConfig, processConfig, month, year); break; } - case 'Indicator': { + case 'Indicator Bot': { runIndicatorBot(botConfig, processConfig, month, year); break; } @@ -329,11 +329,11 @@ runSensorBot(botConfig, processConfig, month, year); break; } - case 'Indicator': { + case 'Indicator Bot': { runIndicatorBot(botConfig, processConfig, month, year); break; } - case 'Trading Engine': { + case 'Trading Bot': { runTradingEngine(botConfig, processConfig); break; } @@ -383,7 +383,7 @@ } switch (botConfig.type) { - case 'Trading Engine': { + case 'Trading Bot': { runTradingEngine(botConfig, processConfig); break; } From 0df460f3a55aa6f43951a25df33bb34a1b113384 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 27 Oct 2019 22:09:14 +0100 Subject: [PATCH 04/38] Trading Engine Refactoring --- Project.njsproj | 2 +- Root.js | 42 +++++++++---------- StatusReport.js | 2 +- ...ainLoop.js => TradingBotProcessMainLoop.js | 4 +- server.js | 4 +- 5 files changed, 27 insertions(+), 27 deletions(-) rename TradingEngineProcessMainLoop.js => TradingBotProcessMainLoop.js (99%) diff --git a/Project.njsproj b/Project.njsproj index 696586b5a..69959a255 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -48,7 +48,7 @@ Code - + Code diff --git a/Root.js b/Root.js index a0bef40d7..59e2e34e5 100644 --- a/Root.js +++ b/Root.js @@ -121,7 +121,7 @@ const INDICATOR_BOT_MAIN_LOOP_MODULE = require('./IndicatorBotProcessMainLoop'); const SENSOR_BOT_MAIN_LOOP_MODULE = require('./SensorBotProcessMainLoop'); - const TRADING_ENGINE_MAIN_LOOP_MODULE = require('./TradingEngineProcessMainLoop'); + const TRADING_ENGINE_MAIN_LOOP_MODULE = require('./TradingBotProcessMainLoop'); /* Loop through all the processes configured to be run by this Node.js Instance. */ /* Now we will read the config of the bot from the path we obtained at the Task Server config. */ @@ -334,7 +334,7 @@ break; } case 'Trading Bot': { - runTradingEngine(botConfig, processConfig); + runTradingBot(botConfig, processConfig); break; } default: { @@ -384,7 +384,7 @@ switch (botConfig.type) { case 'Trading Bot': { - runTradingEngine(botConfig, processConfig); + runTradingBot(botConfig, processConfig); break; } default: { @@ -552,7 +552,7 @@ } } - function runTradingEngine(pBotConfig, pProcessConfig) { + function runTradingBot(pBotConfig, pProcessConfig) { global.TOTAL_PROCESS_INSTANCES_CREATED++ @@ -563,16 +563,16 @@ logger = DEBUG_MODULE.newDebugLog(); logger.bot = pBotConfig; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingBot -> Entering function."); } - let tradingEngineMainLoop = TRADING_ENGINE_MAIN_LOOP_MODULE.newTradingEngineProcessMainLoop(pBotConfig, logger); - tradingEngineMainLoop.initialize(pProcessConfig, onInitializeReady); + let tradingBotMainLoop = TRADING_ENGINE_MAIN_LOOP_MODULE.newTradingBotProcessMainLoop(pBotConfig, logger); + tradingBotMainLoop.initialize(pProcessConfig, onInitializeReady); function onInitializeReady(err) { if (err.result === global.DEFAULT_OK_RESPONSE.result) { - tradingEngineMainLoop.run(whenRunFinishes); + tradingBotMainLoop.run(whenRunFinishes); function whenRunFinishes(err) { @@ -584,36 +584,36 @@ if (err.result === global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> botId = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); logger.persist(); } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bye."); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); logger.persist(); } setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> Failed to initialize the bot. "); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> err = " + err.message); logger.persist(); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> err = " + err.stack); + console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingBot -> err = " + err.stack); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } diff --git a/StatusReport.js b/StatusReport.js index 4096976a4..f239e826a 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -50,7 +50,7 @@ month = pMonth; year = pYear; - if (bot.SESSION !== undefined && pOwner.type === "Trading Engine") { + if (bot.SESSION !== undefined && pOwner.type === "Trading Bot Instance") { sessionPath = bot.SESSION.folderName + "/" } diff --git a/TradingEngineProcessMainLoop.js b/TradingBotProcessMainLoop.js similarity index 99% rename from TradingEngineProcessMainLoop.js rename to TradingBotProcessMainLoop.js index efc4c8143..147e67c86 100644 --- a/TradingEngineProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -1,8 +1,8 @@ -exports.newTradingEngineProcessMainLoop = function newTradingEngineProcessMainLoop(bot, parentLogger) { +exports.newTradingBotProcessMainLoop = function newTradingBotProcessMainLoop(bot, parentLogger) { const ROOT_DIR = './'; - const MODULE_NAME = "Trading Engine Process Main Loop"; + const MODULE_NAME = "Trading Process Main Loop"; const FULL_LOG = true; let USER_BOT_MODULE; diff --git a/server.js b/server.js index 50c47c578..728c7eeed 100644 --- a/server.js +++ b/server.js @@ -72,7 +72,7 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. - let argument = '{"type":"Task","name":"Runs Backtests","bot":{"type":"Trading Engine","code":{"team":"AAMasters","bot":"AAJason","repo":"AAJason-Trading-Engine-Bot"},"processes":[{"type":"Process","subType":"Trading Engine Process","name":"Multi Period","code":{"process":"Multi-Period"},"session":{"type":"Backtesting Session","name":"Backtest 01","code":{"folderName":"Backtest-01"},"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Base Asset","code":{"name":"BTC","initialBalance":0.001,"minimumBalance":0.0001,"maximumBalance":1},"id":"666f6f10-2f17-4f5e-951c-b532bdb574d2"},"timeRange":{"type":"Time Range","name":"Time Range","code":{"initialDatetime":"2019-09-24T00:00:00.000Z","finalDatetime":"2019-09-27T00:00:00.000Z"},"id":"172958dd-b268-4444-8163-70979538ea6e"},"timePeriod":{"type":"Time Period","name":"Time Period","code":"10-min","id":"37ee839d-9a7c-410c-9251-c33b8f1b99f7"},"slippage":{"type":"Slippage","name":"Slippage","code":{"positionRate":0.1,"stopLoss":0.1,"takeProfit":0.1},"id":"86ebd973-6ad9-4070-aece-a87623f24c2e"},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":{"maker":0.15,"taker":0.25},"id":"a0ec3f6f-6047-4740-87fe-393b1c714adb"},"key":{"type":"Exchange Account Key","name":"New Key","code":"Paste your exchange API secret key here and the put the key name as this key object title. Secret keys are filtered out and NOT exported when using the SHARE menu option on any object at your workspace. Secret keys ARE downloaded when using the download button.","id":"368f879d-e363-47c7-b55e-7b927d896b20"},"id":"09db5e1e-a7e8-468c-ad70-0d2ab1c8b8b8"},"layerManager":{"type":"Layer Manager","name":"New Layer Manager","layers":[{"type":"Layer","name":"Live","code":{"product":"Live-Trading-History"},"id":"0ec05e1c-230b-44a1-9f2c-fba0ea2ff9e2"},{"type":"Layer","name":"Trades","code":{"product":"Simulation-Trades"},"id":"6326e03a-96c0-4d21-b877-f2c6a0419fc5"},{"type":"Layer","name":"Strategies","code":{"product":"Simulation-Strategies"},"id":"bda1e06c-e569-4bd5-95fb-90154aafb003"},{"type":"Layer","name":"Simulation","code":{"product":"Trading-Simulation"},"id":"d58d8cd2-f125-4f0c-b241-08e2af8ac4be"},{"type":"Layer","name":"Formulas & Conditions","code":{"product":"Simulation-Conditions"},"id":"15dfeca0-acd9-40d3-afe0-4805654bf0d0"}],"id":"884c4dd7-4beb-43fb-bda5-d6097d58ae09"},"socialBots":{"type":"Social Bots","name":"New Social Bots","bots":[],"id":"281eb022-cf9f-4ac2-978b-c8358bee8fec"},"id":"121202ea-6735-4bf3-8a95-aaa55f00f6e7"},"id":"3f483482-d8a1-4755-ba50-7cac6296fa5f"}],"id":"25fe9099-b53d-4757-8a63-3f8a15875f33"},"id":"5b21d2d2-0f52-4408-83eb-ddaa844ec58c"}' + let argument = '{"type":"Task","name":"Runs Backtests","bot":{"type":"Trading Bot Instance","code":{"team":"AAMasters","bot":"AAJason","repo":"AAJason-Trading-Engine-Bot"},"processes":[{"type":"Process","subType":"Trading Bot Instance Process","name":"Multi Period","code":{"process":"Multi-Period"},"session":{"type":"Backtesting Session","name":"Backtest 01","code":{"folderName":"Backtest-01"},"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Base Asset","code":{"name":"BTC","initialBalance":0.001,"minimumBalance":0.0001,"maximumBalance":1},"id":"666f6f10-2f17-4f5e-951c-b532bdb574d2"},"timeRange":{"type":"Time Range","name":"Time Range","code":{"initialDatetime":"2019-09-24T00:00:00.000Z","finalDatetime":"2019-09-27T00:00:00.000Z"},"id":"172958dd-b268-4444-8163-70979538ea6e"},"timePeriod":{"type":"Time Period","name":"Time Period","code":"10-min","id":"37ee839d-9a7c-410c-9251-c33b8f1b99f7"},"slippage":{"type":"Slippage","name":"Slippage","code":{"positionRate":0.1,"stopLoss":0.1,"takeProfit":0.1},"id":"86ebd973-6ad9-4070-aece-a87623f24c2e"},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":{"maker":0.15,"taker":0.25},"id":"a0ec3f6f-6047-4740-87fe-393b1c714adb"},"key":{"type":"Exchange Account Key","name":"New Key","code":"Paste your exchange API secret key here and the put the key name as this key object title. Secret keys are filtered out and NOT exported when using the SHARE menu option on any object at your workspace. Secret keys ARE downloaded when using the download button.","id":"368f879d-e363-47c7-b55e-7b927d896b20"},"id":"09db5e1e-a7e8-468c-ad70-0d2ab1c8b8b8"},"layerManager":{"type":"Layer Manager","name":"New Layer Manager","layers":[{"type":"Layer","name":"Live","code":{"product":"Live-Trading-History"},"id":"0ec05e1c-230b-44a1-9f2c-fba0ea2ff9e2"},{"type":"Layer","name":"Trades","code":{"product":"Simulation-Trades"},"id":"6326e03a-96c0-4d21-b877-f2c6a0419fc5"},{"type":"Layer","name":"Strategies","code":{"product":"Simulation-Strategies"},"id":"bda1e06c-e569-4bd5-95fb-90154aafb003"},{"type":"Layer","name":"Simulation","code":{"product":"Trading-Simulation"},"id":"d58d8cd2-f125-4f0c-b241-08e2af8ac4be"},{"type":"Layer","name":"Formulas & Conditions","code":{"product":"Simulation-Conditions"},"id":"15dfeca0-acd9-40d3-afe0-4805654bf0d0"}],"id":"884c4dd7-4beb-43fb-bda5-d6097d58ae09"},"socialBots":{"type":"Social Bots","name":"New Social Bots","bots":[],"id":"281eb022-cf9f-4ac2-978b-c8358bee8fec"},"id":"121202ea-6735-4bf3-8a95-aaa55f00f6e7"},"id":"3f483482-d8a1-4755-ba50-7cac6296fa5f"}],"id":"25fe9099-b53d-4757-8a63-3f8a15875f33"},"id":"5b21d2d2-0f52-4408-83eb-ddaa844ec58c"}' /* charly argument = '{"type":"Task","name":"Brings Trades Records from the Exchange","bot":{"type":"Sensor","name":"Charly","processes":[{"type":"Process","name":"Live Trades","code":{"team":"AAMasters","bot":"AACharly","process":"Live-Trades"},"id":"5846ebc7-1979-4a80-9cc7-94bb4b8659dc"},{"type":"Process","name":"Hole Fixing","code":{"team":"AAMasters","bot":"AACharly","process":"Hole-Fixing"},"id":"31fc4f05-75d4-419a-9fb0-73e25e856f15"}]},"id":"5bfef4dc-54c1-44db-ace6-1ab27a86746e"}' // olivia argument = '{"type":"Task","name":"Generates 1 min to 24 hs Candles & Volumes","bot":{"type":"Indicator","name":"Olivia","processes":[{"type":"Process","name":"Daily","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Daily"},"id":"68cc8e1b-e94a-477e-82d0-15ecc9f1b9e2"},{"type":"Process","name":"Market","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Market"},"id":"e1ac5e3d-d491-4e90-baf4-d4e5b71a8b1a"}]},"id":"efe36e05-75ea-41b5-8d92-c6b27635c834"}' // bruce argument = ' {"type":"Task","name":"Converts Trades into 1 min Candles & Volumes","bot":{"type":"Indicator","name":"Bruce","processes":[{"type":"Process","name":"New Process","code":{"team":"AAMasters","bot":"AABruce","process":"Single-Period-Daily"},"id":"e184744f-5de0-41c1-ad4c-36960f4ced90"}]},"id":"42758590-a7bd-4712-a5c9-80a3a820c5b3"}' @@ -177,7 +177,7 @@ function bootLoader() { /* Validate that the minimun amount of parameters required are defined. */ if (global.TASK_NODE.bot.code.bot === undefined) { - console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'bot' at the Indicator | Sensor | Trading Engine is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name); + console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'bot' at the Indicator | Sensor | Trading is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name); continue } From e61a74fd03bdf35bf907db154eab88d24a202d27 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 28 Oct 2019 09:14:07 +0100 Subject: [PATCH 05/38] debug stuff --- server.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/server.js b/server.js index 728c7eeed..5a6d11e39 100644 --- a/server.js +++ b/server.js @@ -72,12 +72,8 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. - let argument = '{"type":"Task","name":"Runs Backtests","bot":{"type":"Trading Bot Instance","code":{"team":"AAMasters","bot":"AAJason","repo":"AAJason-Trading-Engine-Bot"},"processes":[{"type":"Process","subType":"Trading Bot Instance Process","name":"Multi Period","code":{"process":"Multi-Period"},"session":{"type":"Backtesting Session","name":"Backtest 01","code":{"folderName":"Backtest-01"},"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Base Asset","code":{"name":"BTC","initialBalance":0.001,"minimumBalance":0.0001,"maximumBalance":1},"id":"666f6f10-2f17-4f5e-951c-b532bdb574d2"},"timeRange":{"type":"Time Range","name":"Time Range","code":{"initialDatetime":"2019-09-24T00:00:00.000Z","finalDatetime":"2019-09-27T00:00:00.000Z"},"id":"172958dd-b268-4444-8163-70979538ea6e"},"timePeriod":{"type":"Time Period","name":"Time Period","code":"10-min","id":"37ee839d-9a7c-410c-9251-c33b8f1b99f7"},"slippage":{"type":"Slippage","name":"Slippage","code":{"positionRate":0.1,"stopLoss":0.1,"takeProfit":0.1},"id":"86ebd973-6ad9-4070-aece-a87623f24c2e"},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":{"maker":0.15,"taker":0.25},"id":"a0ec3f6f-6047-4740-87fe-393b1c714adb"},"key":{"type":"Exchange Account Key","name":"New Key","code":"Paste your exchange API secret key here and the put the key name as this key object title. Secret keys are filtered out and NOT exported when using the SHARE menu option on any object at your workspace. Secret keys ARE downloaded when using the download button.","id":"368f879d-e363-47c7-b55e-7b927d896b20"},"id":"09db5e1e-a7e8-468c-ad70-0d2ab1c8b8b8"},"layerManager":{"type":"Layer Manager","name":"New Layer Manager","layers":[{"type":"Layer","name":"Live","code":{"product":"Live-Trading-History"},"id":"0ec05e1c-230b-44a1-9f2c-fba0ea2ff9e2"},{"type":"Layer","name":"Trades","code":{"product":"Simulation-Trades"},"id":"6326e03a-96c0-4d21-b877-f2c6a0419fc5"},{"type":"Layer","name":"Strategies","code":{"product":"Simulation-Strategies"},"id":"bda1e06c-e569-4bd5-95fb-90154aafb003"},{"type":"Layer","name":"Simulation","code":{"product":"Trading-Simulation"},"id":"d58d8cd2-f125-4f0c-b241-08e2af8ac4be"},{"type":"Layer","name":"Formulas & Conditions","code":{"product":"Simulation-Conditions"},"id":"15dfeca0-acd9-40d3-afe0-4805654bf0d0"}],"id":"884c4dd7-4beb-43fb-bda5-d6097d58ae09"},"socialBots":{"type":"Social Bots","name":"New Social Bots","bots":[],"id":"281eb022-cf9f-4ac2-978b-c8358bee8fec"},"id":"121202ea-6735-4bf3-8a95-aaa55f00f6e7"},"id":"3f483482-d8a1-4755-ba50-7cac6296fa5f"}],"id":"25fe9099-b53d-4757-8a63-3f8a15875f33"},"id":"5b21d2d2-0f52-4408-83eb-ddaa844ec58c"}' - /* charly argument = '{"type":"Task","name":"Brings Trades Records from the Exchange","bot":{"type":"Sensor","name":"Charly","processes":[{"type":"Process","name":"Live Trades","code":{"team":"AAMasters","bot":"AACharly","process":"Live-Trades"},"id":"5846ebc7-1979-4a80-9cc7-94bb4b8659dc"},{"type":"Process","name":"Hole Fixing","code":{"team":"AAMasters","bot":"AACharly","process":"Hole-Fixing"},"id":"31fc4f05-75d4-419a-9fb0-73e25e856f15"}]},"id":"5bfef4dc-54c1-44db-ace6-1ab27a86746e"}' - // olivia argument = '{"type":"Task","name":"Generates 1 min to 24 hs Candles & Volumes","bot":{"type":"Indicator","name":"Olivia","processes":[{"type":"Process","name":"Daily","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Daily"},"id":"68cc8e1b-e94a-477e-82d0-15ecc9f1b9e2"},{"type":"Process","name":"Market","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Market"},"id":"e1ac5e3d-d491-4e90-baf4-d4e5b71a8b1a"}]},"id":"efe36e05-75ea-41b5-8d92-c6b27635c834"}' - // bruce argument = ' {"type":"Task","name":"Converts Trades into 1 min Candles & Volumes","bot":{"type":"Indicator","name":"Bruce","processes":[{"type":"Process","name":"New Process","code":{"team":"AAMasters","bot":"AABruce","process":"Single-Period-Daily"},"id":"e184744f-5de0-41c1-ad4c-36960f4ced90"}]},"id":"42758590-a7bd-4712-a5c9-80a3a820c5b3"}' - */ - argument = '{"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula"},"processes":[{"type":"Process","subType":"Indicator Process","name":"Daily","code":{"process":"Multi-Period-Daily"},"id":"81f0108b-8af9-4b22-94dd-81ef4a5c8065"},{"type":"Process","subType":"Indicator Process","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c"}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' + let argument + argument = ' {"type":"Task","name":"Runs Backtests","bot":{"type":"Trading Bot Instance","code":{"team":"AAMasters","bot":"AAJason","repo":"AAJason-Trading-Bot"},"processes":[{"type":"Process Instance","subType":"Trading Process Instance","name":"Multi Period","code":{"process":"Multi-Period"},"session":{"type":"Backtesting Session","name":"Backtest 01","code":{"folderName":"Backtest-01"},"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Base Asset","code":{"name":"BTC","initialBalance":0.001,"minimumBalance":0.0001,"maximumBalance":1},"id":"666f6f10-2f17-4f5e-951c-b532bdb574d2"},"timeRange":{"type":"Time Range","name":"Time Range","code":{"initialDatetime":"2019-09-24T00:00:00.000Z","finalDatetime":"2019-09-26T00:00:00.000Z"},"id":"172958dd-b268-4444-8163-70979538ea6e"},"timePeriod":{"type":"Time Period","name":"Time Period","code":"10-min","id":"37ee839d-9a7c-410c-9251-c33b8f1b99f7"},"slippage":{"type":"Slippage","name":"Slippage","code":{"positionRate":0.1,"stopLoss":0.1,"takeProfit":0.1},"id":"86ebd973-6ad9-4070-aece-a87623f24c2e"},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":{"maker":0.15,"taker":0.25},"id":"a0ec3f6f-6047-4740-87fe-393b1c714adb"},"key":{"type":"Exchange Account Key","name":"New Key","code":"Paste your exchange API secret key here and the put the key name as this key object title. Secret keys are filtered out and NOT exported when using the SHARE menu option on any object at your workspace. Secret keys ARE downloaded when using the download button.","id":"368f879d-e363-47c7-b55e-7b927d896b20"},"id":"09db5e1e-a7e8-468c-ad70-0d2ab1c8b8b8"},"layerManager":{"type":"Layer Manager","name":"New Layer Manager","layers":[{"type":"Layer","name":"Live","code":{"product":"Live-Trading-History"},"id":"0ec05e1c-230b-44a1-9f2c-fba0ea2ff9e2"},{"type":"Layer","name":"Trades","code":{"product":"Simulation-Trades"},"id":"6326e03a-96c0-4d21-b877-f2c6a0419fc5"},{"type":"Layer","name":"Strategies","code":{"product":"Simulation-Strategies"},"id":"bda1e06c-e569-4bd5-95fb-90154aafb003"},{"type":"Layer","name":"Simulation","code":{"product":"Trading-Simulation"},"id":"d58d8cd2-f125-4f0c-b241-08e2af8ac4be"},{"type":"Layer","name":"Formulas & Conditions","code":{"product":"Simulation-Conditions"},"id":"15dfeca0-acd9-40d3-afe0-4805654bf0d0"}],"id":"884c4dd7-4beb-43fb-bda5-d6097d58ae09"},"socialBots":{"type":"Social Bots","name":"New Social Bots","bots":[],"id":"281eb022-cf9f-4ac2-978b-c8358bee8fec"},"id":"121202ea-6735-4bf3-8a95-aaa55f00f6e7"},"id":"3f483482-d8a1-4755-ba50-7cac6296fa5f"}],"id":"25fe9099-b53d-4757-8a63-3f8a15875f33"},"id":"5b21d2d2-0f52-4408-83eb-ddaa844ec58c"}' try { global.TASK_NODE = JSON.parse(argument) From dd73e85b1c710b0929d2004a1d2e4cdf1500efc1 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 9 Nov 2019 19:17:54 +0100 Subject: [PATCH 06/38] Process Execution Events Implemented --- DebugLog.js | 4 + IndicatorBotProcessMainLoop.js | 233 ++++++++++++++++++++++++++++++++- ProcessExecutionEvents.js | 154 ++++++++++++++++++++++ Project.njsproj | 3 + Root.js | 7 + SensorBotProcessMainLoop.js | 231 +++++++++++++++++++++++++++++++- StatusReport.js | 38 +----- server.js | 4 +- 8 files changed, 626 insertions(+), 48 deletions(-) create mode 100644 ProcessExecutionEvents.js diff --git a/DebugLog.js b/DebugLog.js index 06b40a94e..87137a604 100644 --- a/DebugLog.js +++ b/DebugLog.js @@ -126,6 +126,10 @@ exports.newDebugLog = function newDebugLog() { if (thisObject.bot === undefined) { return; } + if (pModule === "") { // For debugging purposes + console.log(pMessage) + } + let newDate = new Date(); newDate = newDate.toISOString(); diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index bd1bd766f..ef88f2f6e 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -129,6 +129,7 @@ const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); const DATA_SET = require(ROOT_DIR + 'DataSet'); + const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ @@ -141,6 +142,7 @@ /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + let processExecutionEvents let userBot; let processFramework; let statusDependencies; @@ -148,7 +150,155 @@ let nextWaitTime; - initializeStatusDependencies(); + initializeProcessExecutionEvents(); + + function initializeProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } + + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, parentLogger) + + processExecutionEvents.initialize(pMonth, pYear, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well."); + startProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.start(onStarted); + + function onStarted(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well."); + initializeStatusDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } function initializeStatusDependencies() { @@ -448,8 +598,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Execution finished well."); - nextWaitTime = 'Normal'; - loopControl(nextWaitTime); + finishProcessExecutionEvents() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -681,8 +830,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); - nextWaitTime = 'Normal'; - loopControl(nextWaitTime); + finishProcessExecutionEvents() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -795,6 +943,81 @@ } } + function finishProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.finish(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function loopControl(nextWaitTime) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } diff --git a/ProcessExecutionEvents.js b/ProcessExecutionEvents.js new file mode 100644 index 000000000..0975a490b --- /dev/null +++ b/ProcessExecutionEvents.js @@ -0,0 +1,154 @@ +exports.newProcessExecutionEvents = function newProcessExecutionEvents(BOT, logger) { + + /* + + Here we manage the process execution events. This allow us to emit events when the process starts and finishes. Also to wait for a dependent event + of another process. + + */ + + const MODULE_NAME = "Process Execution Events"; + + let bot = BOT; + + let thisObject = { + initialize, initialize, + start: start, + finish: finish + }; + + let processThisDependsOn; // This is the process this process needs to wait for it to finishes in order to start. + + let month; + let year; + let currentProcessKey + + return thisObject; + + function initialize(pMonth, pYear, callBackFunction) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + month = pMonth; + year = pYear; + + let name = 'Not Depends on any Process' + + if (bot.processNode.referenceParent !== undefined) { + + currentProcessKey = bot.processNode.referenceParent.name + "-" + bot.processNode.referenceParent.type + "-" + bot.processNode.referenceParent.id + + if (bot.processNode.referenceParent.executionStartedEvent !== undefined) { + if (bot.processNode.referenceParent.executionStartedEvent.referenceParent !== undefined) { + if (bot.processNode.referenceParent.executionStartedEvent.referenceParent.parentNode !== undefined) { + processThisDependsOn = bot.processNode.referenceParent.executionStartedEvent.referenceParent.parentNode + + name = processThisDependsOn.name + if (processThisDependsOn.code.monthlyInstances === true) { + logger.fileName = MODULE_NAME + "." + name + "." + year + "." + month; + } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> " + currentProcessKey + "Month: " + month + " Year: " + year + " depends on " + name); } + } + } + } + } + + logger.fileName = MODULE_NAME + "." + name; + 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 (processThisDependsOn !== undefined) { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> Entering Code Block."); } + + /* This forces this process to wait until the process that this one depends on, emits its event signaling that the process execution has finished. */ + + let extraCallerId = '' + if (month) { extraCallerId = extraCallerId + '-' + month } + if (year) { extraCallerId = extraCallerId + '-' + year } + + let key = processThisDependsOn.name + "-" + processThisDependsOn.type + "-" + processThisDependsOn.id + let callerId = bot.devTeam + "-" + bot.codeName + "-" + bot.process + extraCallerId + + let subscriptionId + + global.SYSTEM_EVENT_HANDLER.listenToEvent(key, 'Process Execution Finished', undefined, callerId, responseCallBack, eventsCallBack) + + function responseCallBack(message) { + if (message.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> responseCallBack -> message = " + JSON.stringify(message)) + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } else { + subscriptionId = message.eventSubscriptionId + } + } + + function eventsCallBack(message) { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> eventsCallBack -> Entering function."); } + + /* We continue the normal flow after we learn the dependent process has finished its execution. */ + global.SYSTEM_EVENT_HANDLER.stopListening(key, 'Process Execution Finished', subscriptionId) + + if (message.event.err.result === global.DEFAULT_OK_RESPONSE.result) { + /* We emit our own event that the process started. */ + let event = { + err: global.DEFAULT_OK_RESPONSE + } + global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> " + currentProcessKey + " Process Execution Started because " + key + " Month: " + month + " Year: " + year + " Finished."); } + } + + callBackFunction(message.event.err); + } + } else { + /* In this case, the Process does not depends on a process which needs to wait for. */ + /* We emit our own event that the process started. */ + let event = { + err: global.DEFAULT_OK_RESPONSE + } + global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> " + currentProcessKey + " Process Execution Started Month: " + month + " Year: " + year); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function finish(callBackFunction) { + + try { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> Entering function."); } + + let event = { + err: global.DEFAULT_OK_RESPONSE + } + + global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Finished', event) + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> " + currentProcessKey + " Process Execution Finished Month: " + month + " Year: " + year); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] finish -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + +}; diff --git a/Project.njsproj b/Project.njsproj index 69959a255..d6b366b77 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -45,6 +45,9 @@ Code + + Code + Code diff --git a/Root.js b/Root.js index 59e2e34e5..707c94093 100644 --- a/Root.js +++ b/Root.js @@ -92,6 +92,13 @@ logErrors: true, logContent: false, intensiveLogging: false + }, + "Process Execution Events": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false } }; diff --git a/SensorBotProcessMainLoop.js b/SensorBotProcessMainLoop.js index 3d20a878d..cd73954fd 100644 --- a/SensorBotProcessMainLoop.js +++ b/SensorBotProcessMainLoop.js @@ -130,6 +130,7 @@ const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); const EXCHANGE_API = require('@superalgos/exchange-gateway'); const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); + const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ @@ -140,15 +141,164 @@ console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + pad(pMonth, 2) + "/" + pad(pYear, 4) + " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8) + " bot.processDatetime = " + bot.processDatetime.toISOString()); - /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + /* We will prepare first the infraestructure needed for the bot to run. There are 4 modules we need to sucessfullly initialize first. */ + let processExecutionEvents let userBot; let statusDependencies; let exchangeAPI; let nextWaitTime; - initializeStatusDependencies(); + initializeProcessExecutionEvents(); + + function initializeProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } + + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, parentLogger) + + processExecutionEvents.initialize(pMonth, pYear, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well."); + startProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.start(onStarted); + + function onStarted(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well."); + initializeStatusDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } function initializeStatusDependencies() { @@ -427,8 +577,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished > Execution finished well."); - nextWaitTime = 'Normal'; - loopControl(nextWaitTime); + finishProcessExecutionEvents() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -541,6 +690,80 @@ } } + function finishProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.finish(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + function loopControl(nextWaitTime) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } diff --git a/StatusReport.js b/StatusReport.js index f239e826a..7289fb35a 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -59,43 +59,7 @@ timePath = "/" + year + "/" + month; } - if (owner.waitUntilNextUpdate === true) { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> waitUntilNextUpdate -> Entering Code Block."); } - - /* This forces this process to wait until the process that this one depends on, updates its status report. */ - - let extraCallerId = '' - if (pMonth) { extraCallerId = extraCallerId + '-' + pMonth } - if (pYear) { extraCallerId = extraCallerId + '-' + pYear } - - let key = owner.devTeam + "-" + owner.bot + "-" + owner.process - let callerId = bot.devTeam + "-" + bot.codeName + "-" + bot.process + extraCallerId - - let subscriptionIdStatusReport - - global.SYSTEM_EVENT_HANDLER.listenToEvent(key, 'Status Report Updated', undefined, callerId, responseCallBack, eventsCallBack) - - function responseCallBack(message) { - if (message.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] initialize -> responseCallBack -> message = " + JSON.stringify(message)) - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } else { - subscriptionIdStatusReport = message.eventSubscriptionId - } - } - - function eventsCallBack() { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> eventsCallBack -> Entering function."); } - - /* We continue the normal flow after we learn the dependent process has updated its status report. */ - global.SYSTEM_EVENT_HANDLER.stopListening(key, 'Status Report Updated', subscriptionIdStatusReport) - callBackFunction(global.DEFAULT_OK_RESPONSE); - } - } else { - /* In this case, the Status Report does not depends on a process which needs to wait for. */ - callBackFunction(global.DEFAULT_OK_RESPONSE); - } + callBackFunction(global.DEFAULT_OK_RESPONSE); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); diff --git a/server.js b/server.js index 5a6d11e39..4c0a1ee49 100644 --- a/server.js +++ b/server.js @@ -73,8 +73,8 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. let argument - argument = ' {"type":"Task","name":"Runs Backtests","bot":{"type":"Trading Bot Instance","code":{"team":"AAMasters","bot":"AAJason","repo":"AAJason-Trading-Bot"},"processes":[{"type":"Process Instance","subType":"Trading Process Instance","name":"Multi Period","code":{"process":"Multi-Period"},"session":{"type":"Backtesting Session","name":"Backtest 01","code":{"folderName":"Backtest-01"},"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Base Asset","code":{"name":"BTC","initialBalance":0.001,"minimumBalance":0.0001,"maximumBalance":1},"id":"666f6f10-2f17-4f5e-951c-b532bdb574d2"},"timeRange":{"type":"Time Range","name":"Time Range","code":{"initialDatetime":"2019-09-24T00:00:00.000Z","finalDatetime":"2019-09-26T00:00:00.000Z"},"id":"172958dd-b268-4444-8163-70979538ea6e"},"timePeriod":{"type":"Time Period","name":"Time Period","code":"10-min","id":"37ee839d-9a7c-410c-9251-c33b8f1b99f7"},"slippage":{"type":"Slippage","name":"Slippage","code":{"positionRate":0.1,"stopLoss":0.1,"takeProfit":0.1},"id":"86ebd973-6ad9-4070-aece-a87623f24c2e"},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":{"maker":0.15,"taker":0.25},"id":"a0ec3f6f-6047-4740-87fe-393b1c714adb"},"key":{"type":"Exchange Account Key","name":"New Key","code":"Paste your exchange API secret key here and the put the key name as this key object title. Secret keys are filtered out and NOT exported when using the SHARE menu option on any object at your workspace. Secret keys ARE downloaded when using the download button.","id":"368f879d-e363-47c7-b55e-7b927d896b20"},"id":"09db5e1e-a7e8-468c-ad70-0d2ab1c8b8b8"},"layerManager":{"type":"Layer Manager","name":"New Layer Manager","layers":[{"type":"Layer","name":"Live","code":{"product":"Live-Trading-History"},"id":"0ec05e1c-230b-44a1-9f2c-fba0ea2ff9e2"},{"type":"Layer","name":"Trades","code":{"product":"Simulation-Trades"},"id":"6326e03a-96c0-4d21-b877-f2c6a0419fc5"},{"type":"Layer","name":"Strategies","code":{"product":"Simulation-Strategies"},"id":"bda1e06c-e569-4bd5-95fb-90154aafb003"},{"type":"Layer","name":"Simulation","code":{"product":"Trading-Simulation"},"id":"d58d8cd2-f125-4f0c-b241-08e2af8ac4be"},{"type":"Layer","name":"Formulas & Conditions","code":{"product":"Simulation-Conditions"},"id":"15dfeca0-acd9-40d3-afe0-4805654bf0d0"}],"id":"884c4dd7-4beb-43fb-bda5-d6097d58ae09"},"socialBots":{"type":"Social Bots","name":"New Social Bots","bots":[],"id":"281eb022-cf9f-4ac2-978b-c8358bee8fec"},"id":"121202ea-6735-4bf3-8a95-aaa55f00f6e7"},"id":"3f483482-d8a1-4755-ba50-7cac6296fa5f"}],"id":"25fe9099-b53d-4757-8a63-3f8a15875f33"},"id":"5b21d2d2-0f52-4408-83eb-ddaa844ec58c"}' - + argument = ' {"type":"Task","name":"Brings Trades Records from the Exchange","bot":{"type":"Sensor Bot Instance","name":"Charly","code":{"team":"AAMasters","bot":"AACharly","repo":"AACharly-Sensor-Bot"},"processes":[{"type":"Process Instance","subType":"Sensor Process Instance","name":"Live Trades","code":{"process":"Live-Trades"},"id":"338266fa-14c2-42eb-b0e5-d0a8ed9309fc","referenceParent":{"type":"Process Definition","name":"Live-Trades","code":{},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"9ffdaadd-5204-4436-b89e-cabd8f0e5829"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[],"dataDependencies":[],"id":"a98b94ad-0cd4-4e54-ade4-68b0ab577414"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"942c8337-5d22-413d-9e44-e2da9a5479f1"},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"e3774584-43e5-4dc5-b097-7b16476d586a"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"0f8cc852-ad72-40fe-bf4a-866e68439492"},"id":"371a2507-655b-46c5-b959-52f527cc4d7e"}},{"type":"Process Instance","subType":"Sensor Process Instance","name":"Hole Fixing","code":{"process":"Hole-Fixing"},"id":"3d7e0bdd-64e3-4442-8742-cc9282a82631","referenceParent":{"type":"Process Definition","name":"Hole-Fixing","code":{},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"646ec2e6-98cb-489f-abb6-98b9bbdfa5f0","referenceParent":{"type":"Dataset Definition","name":"Trades","code":{},"id":"80c4ee03-aed4-4d8e-8145-a49f8b85df04"}}],"id":"4c577394-cc99-4d9d-ae73-38f28e08405d"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"b707a221-e8f4-4d6f-824c-8e26e92b8093","referenceParent":{"type":"Status Report","name":"New Status Report","id":"03aa3c2e-d33c-4054-9150-1b641bc02e96"}},{"type":"Status Dependency","name":"New Status Dependency","id":"e9aedbfd-8b76-44c4-bfda-733da4f59023"},{"type":"Status Dependency","name":"New Status Dependency","id":"7a3ebb1b-1d28-4abd-9875-6b0d9c2ecea8","referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"cab5ab89-a3e2-4833-a532-3884f0ff6fa9","referenceParent":{"type":"Dataset Definition","name":"Trades","code":{},"id":"80c4ee03-aed4-4d8e-8145-a49f8b85df04"}}],"id":"55891378-cdfa-4b6a-895e-235987c7b568"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"03aa3c2e-d33c-4054-9150-1b641bc02e96"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"39a7e02f-1b3b-49ea-9714-49f8fccc768d"},"executionFinishedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"39a7e02f-1b3b-49ea-9714-49f8fccc768d","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"8777f6f4-8e49-487c-8a77-2f1e4be738b1"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"0d2e6c5a-cb53-4b72-b688-db9478024182"},"id":"b9d87bfc-1df9-4be3-b8c8-41131f5a9dc4"}}],"id":"85c4f582-56ee-4b8e-95b2-aa61baedfa39"},"id":"2fcbea08-a941-4891-80fd-236eacd83c11"}' + try { global.TASK_NODE = JSON.parse(argument) } catch (err) { From 70808983e7eb99a92a8bce5f369a2bf01d662154 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 10 Nov 2019 12:03:10 +0100 Subject: [PATCH 07/38] Explicitly creating event handlers to avoid warnings --- ProcessExecutionEvents.js | 3 +++ StatusReport.js | 2 +- server.js | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ProcessExecutionEvents.js b/ProcessExecutionEvents.js index 0975a490b..47f131ab3 100644 --- a/ProcessExecutionEvents.js +++ b/ProcessExecutionEvents.js @@ -104,6 +104,7 @@ let event = { err: global.DEFAULT_OK_RESPONSE } + global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started') global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> " + currentProcessKey + " Process Execution Started because " + key + " Month: " + month + " Year: " + year + " Finished."); } @@ -117,6 +118,7 @@ let event = { err: global.DEFAULT_OK_RESPONSE } + global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started') global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> " + currentProcessKey + " Process Execution Started Month: " + month + " Year: " + year); } @@ -140,6 +142,7 @@ err: global.DEFAULT_OK_RESPONSE } + global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Finished') global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Finished', event) if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> " + currentProcessKey + " Process Execution Finished Month: " + month + " Year: " + year); } diff --git a/StatusReport.js b/StatusReport.js index 7289fb35a..089c778cc 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -217,8 +217,8 @@ let event = { lastFile: thisObject.file.lastFile } + global.SYSTEM_EVENT_HANDLER.createEventHandler(key, 'Dataset Updated') global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Dataset Updated', event) - } } } diff --git a/server.js b/server.js index 4c0a1ee49..707ffd2ed 100644 --- a/server.js +++ b/server.js @@ -73,8 +73,7 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. let argument - argument = ' {"type":"Task","name":"Brings Trades Records from the Exchange","bot":{"type":"Sensor Bot Instance","name":"Charly","code":{"team":"AAMasters","bot":"AACharly","repo":"AACharly-Sensor-Bot"},"processes":[{"type":"Process Instance","subType":"Sensor Process Instance","name":"Live Trades","code":{"process":"Live-Trades"},"id":"338266fa-14c2-42eb-b0e5-d0a8ed9309fc","referenceParent":{"type":"Process Definition","name":"Live-Trades","code":{},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"9ffdaadd-5204-4436-b89e-cabd8f0e5829"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[],"dataDependencies":[],"id":"a98b94ad-0cd4-4e54-ade4-68b0ab577414"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"942c8337-5d22-413d-9e44-e2da9a5479f1"},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"e3774584-43e5-4dc5-b097-7b16476d586a"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"0f8cc852-ad72-40fe-bf4a-866e68439492"},"id":"371a2507-655b-46c5-b959-52f527cc4d7e"}},{"type":"Process Instance","subType":"Sensor Process Instance","name":"Hole Fixing","code":{"process":"Hole-Fixing"},"id":"3d7e0bdd-64e3-4442-8742-cc9282a82631","referenceParent":{"type":"Process Definition","name":"Hole-Fixing","code":{},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"646ec2e6-98cb-489f-abb6-98b9bbdfa5f0","referenceParent":{"type":"Dataset Definition","name":"Trades","code":{},"id":"80c4ee03-aed4-4d8e-8145-a49f8b85df04"}}],"id":"4c577394-cc99-4d9d-ae73-38f28e08405d"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"b707a221-e8f4-4d6f-824c-8e26e92b8093","referenceParent":{"type":"Status Report","name":"New Status Report","id":"03aa3c2e-d33c-4054-9150-1b641bc02e96"}},{"type":"Status Dependency","name":"New Status Dependency","id":"e9aedbfd-8b76-44c4-bfda-733da4f59023"},{"type":"Status Dependency","name":"New Status Dependency","id":"7a3ebb1b-1d28-4abd-9875-6b0d9c2ecea8","referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"cab5ab89-a3e2-4833-a532-3884f0ff6fa9","referenceParent":{"type":"Dataset Definition","name":"Trades","code":{},"id":"80c4ee03-aed4-4d8e-8145-a49f8b85df04"}}],"id":"55891378-cdfa-4b6a-895e-235987c7b568"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"03aa3c2e-d33c-4054-9150-1b641bc02e96"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"39a7e02f-1b3b-49ea-9714-49f8fccc768d"},"executionFinishedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"39a7e02f-1b3b-49ea-9714-49f8fccc768d","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"8777f6f4-8e49-487c-8a77-2f1e4be738b1"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"0d2e6c5a-cb53-4b72-b688-db9478024182"},"id":"b9d87bfc-1df9-4be3-b8c8-41131f5a9dc4"}}],"id":"85c4f582-56ee-4b8e-95b2-aa61baedfa39"},"id":"2fcbea08-a941-4891-80fd-236eacd83c11"}' - + argument = '{"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula","repo":"AAChris-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Daily","code":{"process":"Multi-Period-Daily"},"id":"81f0108b-8af9-4b22-94dd-81ef4a5c8065","referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"name":"Multi-Period-Daily","description":"Produces for each market two files with bollinger channels respectively at each day of the market history.","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,"framework":{"name":"Multi-Period-Daily","startDate":{"takeItFromStatusDependency":0},"endDate":{"takeItFromStatusDependency":1}}},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"64430a93-38c2-4c71-8085-71661bbf804e","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"64910fea-c92a-4a55-850a-aa62d98b478e"}},{"type":"Output Dataset","name":"New Output Dataset","id":"b536a078-e32a-4489-93f6-75107a90b527","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"ca71e765-0f66-4b18-b500-847b9d16169b"}},{"type":"Output Dataset","name":"New Output Dataset","id":"d5bcc7c4-2798-4c34-9504-cc4617b64a04","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"029479ae-275d-4854-ad3e-66948656a053"}},{"type":"Output Dataset","name":"New Output Dataset","id":"57898dc1-83a2-4ee9-8a0b-6674f988f0fa","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"96b4e65b-e6db-44a0-80a5-ceb9d68a09ec"}}],"id":"b8fc5ac0-021d-4230-aa59-9876c5f0f3e1"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"3cb29ee1-02de-452a-a79f-2451700fe335","referenceParent":{"type":"Status Report","name":"New Status Report","id":"7323fef7-870a-48b6-b96f-586111253b40"}},{"type":"Status Dependency","name":"New Status Dependency","id":"d5f34469-0ae1-45e6-acd9-4155d5940c77","referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"New Status Dependency","id":"7f6266a5-bddb-4d49-962d-100b1993043d","referenceParent":{"type":"Status Report","name":"New Status Report","id":"b1dd07bf-a698-4bbb-995a-356c929a1e94"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"073e24ab-6b47-482a-b117-0d3554f63c1f","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","code":{},"id":"506104fa-e99a-4a46-930e-176bce311aff"}}],"id":"c0cb34fe-9268-4ebb-a8ce-7d53a13a6d4d"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"b1dd07bf-a698-4bbb-995a-356c929a1e94"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"4548cff9-f388-4f4f-b148-665d22ffaf9e","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"acebc3ed-4f3f-4a4b-b403-f16cbe987dde","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca","code":{}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"34e17fad-2c62-4c45-875c-774ac6b6cc99","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3","code":{"name":"Multi-Period-Daily","description":"Produces for each market two files with bollinger channels respectively at each day of the market history.","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,"framework":{"name":"Multi-Period-Daily","startDate":{"takeItFromStatusDependency":0},"endDate":{"takeItFromStatusDependency":1}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"0aaecc18-50b6-4634-9d3e-aafea3fa087f"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"a85d779e-1302-4c04-9c35-10049ffdcb28"},"id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}},{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"description":"Creates Market Files of Bollinger Channels and SubChannels","startMode":{"allMonths":{"run":"false","minYear":"","maxYear":""},"oneMonth":{"run":"false","year":"","month":""},"noTime":{"run":"true"}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Bollinger Channels","id":"d804e799-7c12-42f5-8de2-52a3a7d5daa4","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"610cd062-ab88-4892-b711-0fb430305c61"}},{"type":"Output Dataset","name":"Bollinger Standard Channels","id":"e524685d-8da0-41f6-839d-9a4100e6e3fe","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"22c1f2c6-3838-4657-add8-2e01d7056917"}},{"type":"Output Dataset","name":"Bollinger Sub-Channels","id":"eb0e7947-95e4-4f4a-87ea-1fb430b8b503","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"869f615e-0cc0-44fd-8648-4a951c23a410"}},{"type":"Output Dataset","name":"Bollinger Standard Sub-Channels","id":"dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}],"id":"dd39a54a-3714-446a-9a41-dfd83557664c"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","id":"ae627a69-4089-432d-b379-1b12ce4bd75e","referenceParent":{"type":"Status Report","name":"New Status Report","id":"af8aded9-002a-475a-88f5-d29573740969"}},{"type":"Status Dependency","name":"Chris","id":"4a603114-d464-46db-90a1-62304274117a","referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}],"dataDependencies":[{"type":"Data Dependency","name":"Chris Bollinger Bands","id":"ea27be54-93a3-48e3-8c52-72bc397b6c66","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{},"id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}],"id":"8b509294-6028-40e2-88a8-655a34245ed8"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"af8aded9-002a-475a-88f5-d29573740969"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"f4720e04-5def-4c0b-be42-33e50ac6a6b7","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100","code":{}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"671530dc-5ba5-4c8d-bc23-f228e22c56b3","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4","code":{"description":"Creates Market Files of Bollinger Channels and SubChannels","startMode":{"allMonths":{"run":"false","minYear":"","maxYear":""},"oneMonth":{"run":"false","year":"","month":""},"noTime":{"run":"true"}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ba2de283-a059-44d0-b229-1c7988f48f51"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","code":{"type":"Code","code":"// Write your code here","id":"26413b25-24b7-47d1-b73d-12cef3198657"},"id":"13b23649-2695-48cb-aa9a-efe8acb7ab2c"},"loop":{"type":"Procedure Loop","name":"New Procedure Loop","code":{"type":"Code","code":"// Write your code here","id":"f0a3227a-094b-4aae-babb-d78f9e9b38ed"},"id":"b7a118de-5ebe-4efb-9b10-3b69680063a3"},"id":"859a0c08-b0fd-4d42-851a-21e898d42ec8"},"id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' try { global.TASK_NODE = JSON.parse(argument) } catch (err) { From 058a5731d0287892303c10d9e0ac0812733025b2 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 10 Nov 2019 17:14:24 +0100 Subject: [PATCH 08/38] Status Depnendencies taken from UI tested on Charly only --- IndicatorBotProcessMainLoop.js | 2 +- MultiPeriod.js | 2 +- MultiPeriodDaily.js | 4 +- SensorBotProcessMainLoop.js | 2 +- StatusDependencies.js | 50 +++++++++++++--------- StatusReport.js | 77 +++++++++++++++++++++++++++++++--- TradingBotProcessMainLoop.js | 2 +- server.js | 3 +- 8 files changed, 110 insertions(+), 32 deletions(-) diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index ef88f2f6e..13ae6c05a 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -308,7 +308,7 @@ statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES); - statusDependencies.initialize(processConfig.statusDependencies, pMonth, pYear, onInizialized); + statusDependencies.initialize(pMonth, pYear, onInizialized); function onInizialized(err) { diff --git a/MultiPeriod.js b/MultiPeriod.js index df6dc1195..4c932ba8c 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -125,7 +125,7 @@ Here we get the status report from the bot who knows which is the end of the market. */ - let botWhoKnowsTheEndOfTheMarket = statusDependencies.config[processConfig.framework.endDate.takeItFromStatusDependency]; + let botWhoKnowsTheEndOfTheMarket = statusDependencies.nodeArray[processConfig.framework.endDate.takeItFromStatusDependency]; reportKey = botWhoKnowsTheEndOfTheMarket.devTeam + "-" + botWhoKnowsTheEndOfTheMarket.bot + "-" + botWhoKnowsTheEndOfTheMarket.process + "-" + "dataSet.V1"; if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 3c2bf0b47..b05a787ee 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -128,7 +128,7 @@ We look first for the bot who knows the begining of the marke in order to get when the market starts. */ - let botWhoKnowsTheBeginingOfTheMarket = statusDependencies.config[processConfig.framework.startDate.takeItFromStatusDependency]; + let botWhoKnowsTheBeginingOfTheMarket = statusDependencies.nodeArray[processConfig.framework.startDate.takeItFromStatusDependency]; reportKey = botWhoKnowsTheBeginingOfTheMarket.devTeam + "-" + botWhoKnowsTheBeginingOfTheMarket.bot + "-" + botWhoKnowsTheBeginingOfTheMarket.process + "-" + "dataSet.V1"; if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } @@ -178,7 +178,7 @@ Here we get the status report from the bot who knows which is the end of the market. */ - let botWhoKnowsTheEndOfTheMarket = statusDependencies.config[processConfig.framework.endDate.takeItFromStatusDependency]; + let botWhoKnowsTheEndOfTheMarket = statusDependencies.nodeArray[processConfig.framework.endDate.takeItFromStatusDependency]; reportKey = botWhoKnowsTheEndOfTheMarket.devTeam + "-" + botWhoKnowsTheEndOfTheMarket.bot + "-" + botWhoKnowsTheEndOfTheMarket.process + "-" + "dataSet.V1"; if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } diff --git a/SensorBotProcessMainLoop.js b/SensorBotProcessMainLoop.js index cd73954fd..58573ed3a 100644 --- a/SensorBotProcessMainLoop.js +++ b/SensorBotProcessMainLoop.js @@ -308,7 +308,7 @@ statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES); - statusDependencies.initialize(processConfig.statusDependencies, pMonth, pYear, onInizialized); + statusDependencies.initialize(pMonth, pYear, onInizialized); function onInizialized(err) { diff --git a/StatusDependencies.js b/StatusDependencies.js index a0eaa4374..1be68c6c0 100644 --- a/StatusDependencies.js +++ b/StatusDependencies.js @@ -2,11 +2,12 @@ const FULL_LOG = true; const LOG_FILE_CONTENT = false; - const MODULE_NAME = "Status Dependencies"; + let bot = BOT + let thisObject = { - config: undefined, + nodeArray: undefined, statusReports: new Map(), initialize: initialize, keys: [] @@ -14,28 +15,38 @@ return thisObject; - function initialize(pStatusDependenciesConfig, pMonth, pYear, callBackFunction) { + function initialize(pMonth, pYear, callBackFunction) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - thisObject.config = pStatusDependenciesConfig; + /* Basic Valdidations */ + if (bot.processNode.referenceParent.processDependencies !== undefined) { + if (bot.processNode.referenceParent.processDependencies.statusDependencies !== undefined) { + thisObject.nodeArray = bot.processNode.referenceParent.processDependencies.statusDependencies + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have status dependencies at all."); + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not status dependencies."); + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + } - /* + /*For each dependency we will initialize the status report, and load it as part of this initialization process.*/ - For each dependency declared at the bot config, we will initialize the status report, and load it as part of this initialization process. - - */ let alreadyCalledBack = false; let loadCount = 0; - for (let i = 0; i < pStatusDependenciesConfig.length; i++) { + for (let i = 0; i < thisObject.nodeArray.length; i++) { let statusReportModule = STATUS_REPORT.newStatusReport(BOT, logger, UTILITIES); logger.write(MODULE_NAME, "[INFO] initialize -> onInitilized -> Initializing Status Report # " + (i + 1)); - statusReportModule.initialize(pStatusDependenciesConfig[i], pMonth, pYear, onInitilized); + statusReportModule.initialize(thisObject.nodeArray[i], pMonth, pYear, onInitilized); function onInitilized(err) { @@ -66,8 +77,7 @@ switch (err.message) { case global.DEFAULT_OK_RESPONSE.message: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> bot = " + pStatusDependenciesConfig[i].bot); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> process = " + pStatusDependenciesConfig[i].process); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> Status Dependency = " + JSON.stringify(thisObject.nodeArray[i])) } addReport(); return; @@ -75,8 +85,8 @@ case "Status Report was never created.": { logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack); - logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> bot = " + pStatusDependenciesConfig[i].bot); - logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> process = " + pStatusDependenciesConfig[i].process); + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(thisObject.nodeArray[i])) + addReport(); return; } @@ -84,8 +94,8 @@ case "Status Report is corrupt.": { logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack); - logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> bot = " + pStatusDependenciesConfig[i].bot); - logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> process = " + pStatusDependenciesConfig[i].process); + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(thisObject.nodeArray[i])) + addReport(); return; } @@ -117,10 +127,10 @@ let key; - if (pStatusDependenciesConfig[i].dataSetSection === "Month") { - key = pStatusDependenciesConfig[i].devTeam + "-" + pStatusDependenciesConfig[i].bot + "-" + pStatusDependenciesConfig[i].process + "-" + pStatusDependenciesConfig[i].dataSetVersion + "-" + pYear + "-" + pMonth; + if (thisObject.nodeArray[i].processRunMonthly === true) { + key = thisObject.nodeArray[i].devTeam + "-" + thisObject.nodeArray[i].bot + "-" + thisObject.nodeArray[i].process + "-" + thisObject.nodeArray[i].dataSetVersion + "-" + pYear + "-" + pMonth; } else { - key = pStatusDependenciesConfig[i].devTeam + "-" + pStatusDependenciesConfig[i].bot + "-" + pStatusDependenciesConfig[i].process + "-" + pStatusDependenciesConfig[i].dataSetVersion; + key = thisObject.nodeArray[i].devTeam + "-" + thisObject.nodeArray[i].bot + "-" + thisObject.nodeArray[i].process + "-" + thisObject.nodeArray[i].dataSetVersion; } thisObject.keys.push(key); @@ -128,7 +138,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Report added to Map. -> key = " + key); } - if (loadCount === pStatusDependenciesConfig.length) { + if (loadCount === thisObject.nodeArray.length) { if (alreadyCalledBack === false) { alreadyCalledBack = true callBackFunction(global.DEFAULT_OK_RESPONSE); diff --git a/StatusReport.js b/StatusReport.js index 089c778cc..79c78a1cb 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -44,18 +44,85 @@ try { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - logger.fileName = MODULE_NAME + "." + pOwner.bot + "." + pOwner.process; - owner = pOwner; month = pMonth; year = pYear; - if (bot.SESSION !== undefined && pOwner.type === "Trading Bot Instance") { + /* Some very basic validations that we have all the information needed. */ + if (owner.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Status Dependency without Reference Parent. Status Dependency = " + JSON.stringify(owner)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (owner.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Status Report not attached to a Process Definition. Status Report = " + JSON.stringify(owner.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (owner.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(owner.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (owner.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition not attached to a Bot. Process Definition = " + JSON.stringify(owner.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (owner.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(owner.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (owner.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Team. Bot = " + JSON.stringify(owner.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (owner.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Team witn no codeName defined. Team = " + JSON.stringify(owner.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* Simplifying the access to basic info */ + owner.bot = owner.referenceParent.parentNode.parentNode.code.codeName + owner.process = owner.referenceParent.parentNode.code.codeName + owner.type = owner.referenceParent.parentNode.parentNode.type + owner.devTeam = owner.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* Let's see if the process is run monthly or not. */ + if (owner.referenceParent.parentNode.code.startMode !== undefined) { + if (owner.referenceParent.parentNode.code.startMode.allMonths !== undefined) { + if (owner.referenceParent.parentNode.code.startMode.oneMonth !== undefined) { + if (owner.referenceParent.parentNode.code.startMode.allMonths.run === "true" || owner.referenceParent.parentNode.code.startMode.oneMonth.run === "true") { + owner.processRunMonthly = true + } + } + } + } + + /* This stuff is still hardcoded and unresolved. */ + owner.botVersion = { + "major": 1, + "minor": 0 + } + owner.dataSetVersion = "dataSet.V1" + + logger.fileName = MODULE_NAME + "." + owner.bot + "." + owner.process; + + if (bot.SESSION !== undefined && owner.type === "Trading Bot Instance") { sessionPath = bot.SESSION.folderName + "/" } - if (owner.dataSetSection === "Month") { - logger.fileName = MODULE_NAME + "." + pOwner.bot + "." + pOwner.process + "." + year + "." + month; + if (owner.processRunMonthly === true) { + logger.fileName = MODULE_NAME + "." + owner.bot + "." + owner.process + "." + year + "." + month; timePath = "/" + year + "/" + month; } diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index 147e67c86..fd008890e 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -192,7 +192,7 @@ statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES); - statusDependencies.initialize(processConfig.statusDependencies, undefined, undefined, onInizialized); + statusDependencies.initialize(undefined, undefined, onInizialized); function onInizialized(err) { diff --git a/server.js b/server.js index 707ffd2ed..c63d95e5d 100644 --- a/server.js +++ b/server.js @@ -73,7 +73,8 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. let argument - argument = '{"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula","repo":"AAChris-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Daily","code":{"process":"Multi-Period-Daily"},"id":"81f0108b-8af9-4b22-94dd-81ef4a5c8065","referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"name":"Multi-Period-Daily","description":"Produces for each market two files with bollinger channels respectively at each day of the market history.","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,"framework":{"name":"Multi-Period-Daily","startDate":{"takeItFromStatusDependency":0},"endDate":{"takeItFromStatusDependency":1}}},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"64430a93-38c2-4c71-8085-71661bbf804e","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"64910fea-c92a-4a55-850a-aa62d98b478e"}},{"type":"Output Dataset","name":"New Output Dataset","id":"b536a078-e32a-4489-93f6-75107a90b527","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"ca71e765-0f66-4b18-b500-847b9d16169b"}},{"type":"Output Dataset","name":"New Output Dataset","id":"d5bcc7c4-2798-4c34-9504-cc4617b64a04","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"029479ae-275d-4854-ad3e-66948656a053"}},{"type":"Output Dataset","name":"New Output Dataset","id":"57898dc1-83a2-4ee9-8a0b-6674f988f0fa","referenceParent":{"type":"Dataset Definition","name":"Daily","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Daily/@Period/@Year/@Month/@Day","fileName":"@AssetA_@AssetB.json","dataRange":{"filePath":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Daily","fileName":"Data.Range.@AssetA_@AssetB.json"}},"id":"96b4e65b-e6db-44a0-80a5-ceb9d68a09ec"}}],"id":"b8fc5ac0-021d-4230-aa59-9876c5f0f3e1"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"3cb29ee1-02de-452a-a79f-2451700fe335","referenceParent":{"type":"Status Report","name":"New Status Report","id":"7323fef7-870a-48b6-b96f-586111253b40"}},{"type":"Status Dependency","name":"New Status Dependency","id":"d5f34469-0ae1-45e6-acd9-4155d5940c77","referenceParent":{"type":"Status Report","name":"New Status Report","id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"New Status Dependency","id":"7f6266a5-bddb-4d49-962d-100b1993043d","referenceParent":{"type":"Status Report","name":"New Status Report","id":"b1dd07bf-a698-4bbb-995a-356c929a1e94"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"073e24ab-6b47-482a-b117-0d3554f63c1f","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","code":{},"id":"506104fa-e99a-4a46-930e-176bce311aff"}}],"id":"c0cb34fe-9268-4ebb-a8ce-7d53a13a6d4d"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"b1dd07bf-a698-4bbb-995a-356c929a1e94"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"4548cff9-f388-4f4f-b148-665d22ffaf9e","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"acebc3ed-4f3f-4a4b-b403-f16cbe987dde","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","id":"e598ee86-1e18-4aa1-95c2-64aaf66776ca","code":{}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"34e17fad-2c62-4c45-875c-774ac6b6cc99","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","id":"bc15b49d-4547-4bac-a28b-193eb0158fe3","code":{"name":"Multi-Period-Daily","description":"Produces for each market two files with bollinger channels respectively at each day of the market history.","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,"framework":{"name":"Multi-Period-Daily","startDate":{"takeItFromStatusDependency":0},"endDate":{"takeItFromStatusDependency":1}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"0aaecc18-50b6-4634-9d3e-aafea3fa087f"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"a85d779e-1302-4c04-9c35-10049ffdcb28"},"id":"bc15b49d-4547-4bac-a28b-193eb0158fe3"}},{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"description":"Creates Market Files of Bollinger Channels and SubChannels","startMode":{"allMonths":{"run":"false","minYear":"","maxYear":""},"oneMonth":{"run":"false","year":"","month":""},"noTime":{"run":"true"}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Bollinger Channels","id":"d804e799-7c12-42f5-8de2-52a3a7d5daa4","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"610cd062-ab88-4892-b711-0fb430305c61"}},{"type":"Output Dataset","name":"Bollinger Standard Channels","id":"e524685d-8da0-41f6-839d-9a4100e6e3fe","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"22c1f2c6-3838-4657-add8-2e01d7056917"}},{"type":"Output Dataset","name":"Bollinger Sub-Channels","id":"eb0e7947-95e4-4f4a-87ea-1fb430b8b503","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"869f615e-0cc0-44fd-8648-4a951c23a410"}},{"type":"Output Dataset","name":"Bollinger Standard Sub-Channels","id":"dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}],"id":"dd39a54a-3714-446a-9a41-dfd83557664c"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self","id":"ae627a69-4089-432d-b379-1b12ce4bd75e","referenceParent":{"type":"Status Report","name":"New Status Report","id":"af8aded9-002a-475a-88f5-d29573740969"}},{"type":"Status Dependency","name":"Chris","id":"4a603114-d464-46db-90a1-62304274117a","referenceParent":{"type":"Status Report","name":"New Status Report","id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}],"dataDependencies":[{"type":"Data Dependency","name":"Chris Bollinger Bands","id":"ea27be54-93a3-48e3-8c52-72bc397b6c66","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{},"id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}],"id":"8b509294-6028-40e2-88a8-655a34245ed8"},"statusReport":{"type":"Status Report","name":"New Status Report","id":"af8aded9-002a-475a-88f5-d29573740969"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"f4720e04-5def-4c0b-be42-33e50ac6a6b7","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100","code":{}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"671530dc-5ba5-4c8d-bc23-f228e22c56b3","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4","code":{"description":"Creates Market Files of Bollinger Channels and SubChannels","startMode":{"allMonths":{"run":"false","minYear":"","maxYear":""},"oneMonth":{"run":"false","year":"","month":""},"noTime":{"run":"true"}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ba2de283-a059-44d0-b229-1c7988f48f51"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","code":{"type":"Code","code":"// Write your code here","id":"26413b25-24b7-47d1-b73d-12cef3198657"},"id":"13b23649-2695-48cb-aa9a-efe8acb7ab2c"},"loop":{"type":"Procedure Loop","name":"New Procedure Loop","code":{"type":"Code","code":"// Write your code here","id":"f0a3227a-094b-4aae-babb-d78f9e9b38ed"},"id":"b7a118de-5ebe-4efb-9b10-3b69680063a3"},"id":"859a0c08-b0fd-4d42-851a-21e898d42ec8"},"id":"28be432d-80ca-46b3-859a-55541e24b2b4"}}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' + argument = ' {"type":"Task","name":"Brings Trades Records from the Exchange","bot":{"type":"Sensor Bot Instance","name":"Charly","code":{"team":"AAMasters","bot":"AACharly","repo":"AACharly-Sensor-Bot"},"processes":[{"type":"Process Instance","subType":"Sensor Process Instance","name":"Live Trades","code":{"process":"Live-Trades"},"id":"338266fa-14c2-42eb-b0e5-d0a8ed9309fc","referenceParent":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"9ffdaadd-5204-4436-b89e-cabd8f0e5829"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"cfd2acec-5b56-40fb-b97b-10cdb91faa92","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"}}],"dataDependencies":[],"id":"a98b94ad-0cd4-4e54-ade4-68b0ab577414"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"942c8337-5d22-413d-9e44-e2da9a5479f1"},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"e3774584-43e5-4dc5-b097-7b16476d586a"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"0f8cc852-ad72-40fe-bf4a-866e68439492"}}},{"type":"Process Instance","subType":"Sensor Process Instance","name":"Hole Fixing","code":{"process":"Hole-Fixing"},"id":"3d7e0bdd-64e3-4442-8742-cc9282a82631","referenceParent":{"type":"Process Definition","name":"Hole-Fixing","code":{"codeName":"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},"id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"d8e3878e-6f12-4ef9-873b-e3f8a8f5a63e"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"2746eb59-ea5f-4f2d-b838-b13adeb4c2e6","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Historic-Trades","code":{"codeName":"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":"false","year":"2018","month":"01"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":60000,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"New Status Dependency","id":"3d84de0f-5939-4992-9395-dc1a1ee93232","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"}},{"type":"Status Dependency","name":"New Status Dependency","id":"612519b7-cc59-4363-9452-903ab2f4a958","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Hole-Fixing","code":{"codeName":"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},"id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"93f391ac-4651-47c6-b2ff-e9387fe0a444"}}],"dataDependencies":[],"id":"aa390ea7-0f89-47cc-a75f-fa4fb738219a"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Hole-Fixing","code":{"codeName":"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},"id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"93f391ac-4651-47c6-b2ff-e9387fe0a444"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"542f80b6-e848-4ab5-aa46-06674837ca3a","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{"codeName":"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}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ab5e01b1-8036-4761-8a93-d61a663b0088","parentNode":{"type":"Process Definition","name":"Hole-Fixing","id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ca19a529-5b73-4ef0-83e2-91aaa73ca4dd"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"5c586e2e-3f91-49d9-b0d3-fa1a84eedec2"}}}],"id":"85c4f582-56ee-4b8e-95b2-aa61baedfa39"},"id":"2fcbea08-a941-4891-80fd-236eacd83c11"}' + try { global.TASK_NODE = JSON.parse(argument) } catch (err) { From e8b15e46fea5dbb241ca13b7a726efae008b7003 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 11 Nov 2019 13:29:54 +0100 Subject: [PATCH 09/38] Process Output Implemented --- IndicatorBotProcessMainLoop.js | 84 ++++++++- ProcessOutput.js | 104 +++++++++++ Project.njsproj | 3 + Root.js | 7 + SensorBotProcessMainLoop.js | 83 ++++++++- StatusDependencies.js | 5 + StatusReport.js | 34 +--- TradingBotProcessMainLoop.js | 306 ++++++++++++++++++++++++++++++++- server.js | 2 +- 9 files changed, 592 insertions(+), 36 deletions(-) create mode 100644 ProcessOutput.js diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index 13ae6c05a..99bda771b 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -130,6 +130,7 @@ const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); const DATA_SET = require(ROOT_DIR + 'DataSet'); const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput'); /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ @@ -142,6 +143,7 @@ /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + let processOutput let processExecutionEvents let userBot; let processFramework; @@ -158,7 +160,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } - processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, parentLogger) + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) processExecutionEvents.initialize(pMonth, pYear, onInizialized); @@ -598,7 +600,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Execution finished well."); - finishProcessExecutionEvents() + raiseEventsProcessOutput() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -830,7 +832,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); - finishProcessExecutionEvents() + raiseEventsProcessOutput() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -943,6 +945,81 @@ } } + function raiseEventsProcessOutput() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); } + + processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger) + + processOutput.raiseEvents(statusDependencies, onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well."); + finishProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> raiseEventsProcessOutput -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + function finishProcessExecutionEvents() { try { @@ -1017,7 +1094,6 @@ } } - function loopControl(nextWaitTime) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } diff --git a/ProcessOutput.js b/ProcessOutput.js new file mode 100644 index 000000000..ef74e7305 --- /dev/null +++ b/ProcessOutput.js @@ -0,0 +1,104 @@ +exports.newProcessOutput = function newProcessOutput(BOT, logger) { + + /* + Here we emit the events that signals that a Dataset was updated. + */ + + const MODULE_NAME = "Process Output"; + + let bot = BOT; + + let thisObject = { + raiseEvents, raiseEvents + }; + + return thisObject; + + function raiseEvents(statusDependencies, callBackFunction) { + + try { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] raiseEvents -> Entering function."); } + + /* We will reaise the events for the datasets impacted by the process that just finished. */ + + if (bot.processNode.referenceParent.processOutput !== undefined) { + if (bot.processNode.referenceParent.processOutput.outputDatasets !== undefined) { + let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets + + for (let j = 0; j < outputDatasets.length; j++) { + let outputDataset = outputDatasets[j] + + /* Some validations to verify that everything is in place. */ + if (outputDataset.referenceParent !== undefined) { + if (outputDataset.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Dataset Definition not defined. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDataset.referenceParent.parentNode !== undefined) { + if (outputDataset.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Product not defined. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDataset.referenceParent.parentNode.parentNode !== undefined) { + if (outputDataset.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Bot not defined. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDataset.referenceParent.parentNode.parentNode.parentNode !== undefined) { + if (outputDataset.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Team not defined. Team = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Team. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Product not attached to a Bot. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Dataset Definition not attached to a Product. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Output Dataset with no reference to a Dataset Definition. Output Dataset = " + JSON.stringify(outputDataset)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* All good, lets emit the event. */ + + let devTeam = outputDataset.referenceParent.parentNode.parentNode.parentNode.code.codeName + let botName = outputDataset.referenceParent.parentNode.parentNode.code.codeName + let product = outputDataset.referenceParent.parentNode.code.codeName + let dataset = outputDataset.referenceParent.code.codeName + + let statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") + + key = devTeam + "-" + botName + "-" + product + "-" + dataset + let event = { + lastFile: statusReport.file.lastFile + } + global.SYSTEM_EVENT_HANDLER.createEventHandler(key, 'Dataset Updated') + global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Dataset Updated', event) + + } + } + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/Project.njsproj b/Project.njsproj index d6b366b77..5128f194e 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -48,6 +48,9 @@ Code + + Code + Code diff --git a/Root.js b/Root.js index 707c94093..a76e82d11 100644 --- a/Root.js +++ b/Root.js @@ -99,6 +99,13 @@ logErrors: true, logContent: false, intensiveLogging: false + }, + "Process Output": { + logInfo: true, + logWarnings: false, + logErrors: true, + logContent: false, + intensiveLogging: false } }; diff --git a/SensorBotProcessMainLoop.js b/SensorBotProcessMainLoop.js index 58573ed3a..3bd2fc6fd 100644 --- a/SensorBotProcessMainLoop.js +++ b/SensorBotProcessMainLoop.js @@ -130,7 +130,8 @@ const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); const EXCHANGE_API = require('@superalgos/exchange-gateway'); const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); - const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput'); /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ @@ -143,6 +144,7 @@ /* We will prepare first the infraestructure needed for the bot to run. There are 4 modules we need to sucessfullly initialize first. */ + let processOutput let processExecutionEvents let userBot; let statusDependencies; @@ -158,7 +160,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } - processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, parentLogger) + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) processExecutionEvents.initialize(pMonth, pYear, onInizialized); @@ -577,7 +579,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished > Execution finished well."); - finishProcessExecutionEvents() + raiseEventsProcessOutput() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -690,6 +692,81 @@ } } + function raiseEventsProcessOutput() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); } + + processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger) + + processOutput.raiseEvents(statusDependencies, onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well."); + finishProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> raiseEventsProcessOutput -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + function finishProcessExecutionEvents() { try { diff --git a/StatusDependencies.js b/StatusDependencies.js index 1be68c6c0..95092e152 100644 --- a/StatusDependencies.js +++ b/StatusDependencies.js @@ -9,6 +9,7 @@ let thisObject = { nodeArray: undefined, statusReports: new Map(), + reportsByMainUtility: new Map(), initialize: initialize, keys: [] }; @@ -136,6 +137,10 @@ thisObject.keys.push(key); thisObject.statusReports.set(key, statusReportModule); + if (statusReportModule.mainUtility !== undefined) { + thisObject.reportsByMainUtility.set(statusReportModule.mainUtility, statusReportModule) + } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Report added to Map. -> key = " + key); } if (loadCount === thisObject.nodeArray.length) { diff --git a/StatusReport.js b/StatusReport.js index 79c78a1cb..4e24d5201 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -13,6 +13,7 @@ let bot = BOT; let thisObject = { + mainUtility: undefined, file: undefined, // Here we have the JSON object representing the file content. initialize: initialize, load: load, @@ -107,7 +108,14 @@ } } } - + + /* We retrieve the report main utility */ + if (owner.code !== undefined) { + if (owner.code.mainUtility !== undefined) { + thisObject.mainUtility = owner.code.mainUtility + } + } + /* This stuff is still hardcoded and unresolved. */ owner.botVersion = { "major": 1, @@ -267,30 +275,6 @@ logger.write(MODULE_NAME, "[INFO] save -> onFileCreated -> Content written = " + fileContent); } - /* Here we raise the event stating that this status report was updated. */ - let key = bot.devTeam + "-" + bot.codeName + "-" + bot.process - global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Status Report Updated') - - /* We will also reaise the events for the datasets impacted by the process that just finished. */ - for (let j = 0; j < bot.processes.length; j++) { - let process = bot.processes[j] - if (process.name === bot.process) { - let updatesDatasets = process.updatesDatasets - if (updatesDatasets !== undefined) { - for (let i = 0; i < updatesDatasets.length; i++) { - let updatedDataSet = updatesDatasets[i] - - key = bot.devTeam + "-" + bot.codeName + "-" + updatedDataSet.product + "-" + updatedDataSet.dataSet - let event = { - lastFile: thisObject.file.lastFile - } - global.SYSTEM_EVENT_HANDLER.createEventHandler(key, 'Dataset Updated') - global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Dataset Updated', event) - } - } - } - } - callBackFunction(global.DEFAULT_OK_RESPONSE); return; } diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index fd008890e..8877a59f9 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -140,6 +140,8 @@ const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); const DATA_SET = require(ROOT_DIR + 'DataSet'); + const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput'); /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ @@ -177,12 +179,162 @@ /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + let processOutput + let processExecutionEvents let userBot; let processFramework; let statusDependencies; let dataDependencies; - initializeStatusDependencies(); + initializeProcessExecutionEvents(); + + function initializeProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); } + + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) + + processExecutionEvents.initialize(pMonth, pYear, onInizialized); + + function onInizialized(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well."); + startProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function startProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.start(onStarted); + + function onStarted(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well."); + initializeStatusDependencies(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } function initializeStatusDependencies() { @@ -848,8 +1000,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> onFinished -> Execution finished well."); } - nextWaitTime = 'Normal'; - loopControl(nextWaitTime); + raiseEventsProcessOutput() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -910,6 +1061,155 @@ } } + function raiseEventsProcessOutput() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); } + + processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger) + + processOutput.raiseEvents(statusDependencies, onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well."); + finishProcessExecutionEvents() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> raiseEventsProcessOutput -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + function finishProcessExecutionEvents() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); } + + processExecutionEvents.finish(onFinished); + + function onFinished(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write(MODULE_NAME, "[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + processStopped() + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process."); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message); + + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + } + } + function loopControl(nextWaitTime) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); } diff --git a/server.js b/server.js index c63d95e5d..7ad8aa56c 100644 --- a/server.js +++ b/server.js @@ -73,7 +73,7 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. let argument - argument = ' {"type":"Task","name":"Brings Trades Records from the Exchange","bot":{"type":"Sensor Bot Instance","name":"Charly","code":{"team":"AAMasters","bot":"AACharly","repo":"AACharly-Sensor-Bot"},"processes":[{"type":"Process Instance","subType":"Sensor Process Instance","name":"Live Trades","code":{"process":"Live-Trades"},"id":"338266fa-14c2-42eb-b0e5-d0a8ed9309fc","referenceParent":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"9ffdaadd-5204-4436-b89e-cabd8f0e5829"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"cfd2acec-5b56-40fb-b97b-10cdb91faa92","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"}}],"dataDependencies":[],"id":"a98b94ad-0cd4-4e54-ade4-68b0ab577414"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"942c8337-5d22-413d-9e44-e2da9a5479f1"},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"e3774584-43e5-4dc5-b097-7b16476d586a"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"0f8cc852-ad72-40fe-bf4a-866e68439492"}}},{"type":"Process Instance","subType":"Sensor Process Instance","name":"Hole Fixing","code":{"process":"Hole-Fixing"},"id":"3d7e0bdd-64e3-4442-8742-cc9282a82631","referenceParent":{"type":"Process Definition","name":"Hole-Fixing","code":{"codeName":"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},"id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[],"id":"d8e3878e-6f12-4ef9-873b-e3f8a8f5a63e"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"New Status Dependency","id":"2746eb59-ea5f-4f2d-b838-b13adeb4c2e6","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Historic-Trades","code":{"codeName":"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":"false","year":"2018","month":"01"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":60000,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"New Status Dependency","id":"3d84de0f-5939-4992-9395-dc1a1ee93232","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Live-Trades","code":{"codeName":"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},"id":"371a2507-655b-46c5-b959-52f527cc4d7e","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"9f3ea81f-2aa1-41ec-907f-c003dec0c827"}},{"type":"Status Dependency","name":"New Status Dependency","id":"612519b7-cc59-4363-9452-903ab2f4a958","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Hole-Fixing","code":{"codeName":"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},"id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"93f391ac-4651-47c6-b2ff-e9387fe0a444"}}],"dataDependencies":[],"id":"aa390ea7-0f89-47cc-a75f-fa4fb738219a"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Hole-Fixing","code":{"codeName":"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},"id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"93f391ac-4651-47c6-b2ff-e9387fe0a444"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"542f80b6-e848-4ab5-aa46-06674837ca3a","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"d5f1f2b3-ffa5-442b-b875-e91a786337ae","parentNode":{"type":"Process Definition","name":"Live-Trades","id":"371a2507-655b-46c5-b959-52f527cc4d7e","code":{"codeName":"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}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ab5e01b1-8036-4761-8a93-d61a663b0088","parentNode":{"type":"Process Definition","name":"Hole-Fixing","id":"60c8e8b0-0c0d-49bd-980e-6c7814a4cb18","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ca19a529-5b73-4ef0-83e2-91aaa73ca4dd"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"5c586e2e-3f91-49d9-b0d3-fa1a84eedec2"}}}],"id":"85c4f582-56ee-4b8e-95b2-aa61baedfa39"},"id":"2fcbea08-a941-4891-80fd-236eacd83c11"}' + argument = '{"type":"Task","name":"Generates 1 min to 24 hs Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":{"team":"AAMasters","bot":"AAOlivia","repo":"AAOlivia-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Daily","code":{"process":"Multi-Period-Daily"},"id":"050d715f-2851-48b4-86b6-d93ab4baa1b2","referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"codeName":"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},"id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"f349a029-5381-4f8e-92d4-1b7e7df76966","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","code":{"codeName":"Multi-Period-Daily"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"},"id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}},{"type":"Output Dataset","name":"New Output Dataset","id":"15fd40ad-ceca-46ea-8fdd-4a396f9bde14","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","code":{"codeName":"Multi-Period-Daily"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"},"id":"404b8d12-db14-462c-b4e4-6ee5093560c3"}}],"id":"e130a389-0b18-4433-809d-f44162a00a4a"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":{"mainUtility":"Market Starting Point"},"id":"93f6d813-594f-49cd-a32f-181a7b8352ee","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Historic-Trades","code":{"codeName":"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":"false","year":"2018","month":"01"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":60000,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"cb413af9-e1f6-4dbe-ab8c-9f7eb949bd93","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"codeName":"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},"id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"fbaa8e58-7e12-48d8-81e5-1050c034c736","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"}},"id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"c9f72b18-362c-4663-9dab-02b592aba3a1","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"3b42a93e-c933-420d-9504-25092a0d1279"},"id":"20d5740b-5711-4b8c-9675-a5391f5ba245"}},{"type":"Data Dependency","name":"New Data Dependency","id":"9aff9fc8-4935-4043-9a95-ee2fe02f2cc1","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"4e714023-0cfd-4945-a573-52922711cd09"},"id":"2788ed30-ed62-4638-9a1d-35dc7e826870"}}],"id":"dab7d491-7a05-47ac-9f86-a551eb24ef67"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"codeName":"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},"id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"b83c7f7c-d0c4-4daa-aa6f-41adaa2626b0","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f3b263bb-1051-40d8-a69a-d8640cf9afbe","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"522fca96-a4d5-4cba-9271-4e5f66209390"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"963bf983-f910-4c3e-bdee-b968d56dd155"}}},{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"111b43d5-d4ff-414c-8b9f-24f79e2a0254","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"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},"id":"383c3970-13d5-4313-bd8f-7e5d28948577","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"7c42483e-6b75-4b61-91c2-597b3fea1aa0","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"},"id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}},{"type":"Output Dataset","name":"New Output Dataset","id":"5aa52a69-6139-41c0-bce5-68f1419ab7f2","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"},"id":"8f6d7c41-f829-4157-8365-707a4789138e"}}],"id":"2912df2e-2d79-4a8b-af79-e79d26be51d0"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":{"mainUtility":"Market Starting Point"},"id":"92e1f19f-986b-43c8-a742-2b4832e061cc","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Historic-Trades","code":{"codeName":"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":"false","year":"2018","month":"01"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":60000,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"701ead56-8e85-4138-bfe8-263b1d59869d","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"}},"id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9"}},{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"757b878a-99bd-402c-bd1c-4ae7961e35bf","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"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},"id":"383c3970-13d5-4313-bd8f-7e5d28948577","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"4f71d561-5872-4539-8408-f481be275559"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"72f6e0db-ed45-4ee8-aac0-188a7db38c1b","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"3b42a93e-c933-420d-9504-25092a0d1279"},"id":"20d5740b-5711-4b8c-9675-a5391f5ba245"}},{"type":"Data Dependency","name":"New Data Dependency","id":"0bcc9ab5-7534-4aae-ab82-5dde001673db","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"4e714023-0cfd-4945-a573-52922711cd09"},"id":"2788ed30-ed62-4638-9a1d-35dc7e826870"}}],"id":"af72941b-880c-47b4-8a23-fc7279809d4d"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"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},"id":"383c3970-13d5-4313-bd8f-7e5d28948577","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"4f71d561-5872-4539-8408-f481be275559"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"d54f53d2-7a7e-4ff7-afdf-33d25405865c","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f3b263bb-1051-40d8-a69a-d8640cf9afbe","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"9cd6a59f-a537-4c58-ac92-915db8416ad5"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"406ff220-1437-4030-816b-3d1de0988a60"}}}],"id":"1e587326-ad4d-4ea8-9273-367b3009ea3d"},"id":"e4164330-478a-4de8-b3ae-db67225bfa7a"}' try { global.TASK_NODE = JSON.parse(argument) From 7ae581b1a57778948c07196916d0805672980f93 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 11 Nov 2019 15:18:06 +0100 Subject: [PATCH 10/38] Just refactoring --- MultiPeriod.js | 20 ++++++++++---------- MultiPeriodDaily.js | 16 ++++++++-------- MultiPeriodMarket.js | 14 +++++++------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/MultiPeriod.js b/MultiPeriod.js index 4c932ba8c..024f7f111 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -16,7 +16,7 @@ let statusDependencies; let dataDependencies; - let storages = []; + let datasets = []; let dataFiles = []; let multiPeriodDataFiles = new Map(); @@ -45,7 +45,7 @@ for (let i = 0; i < dataDependencies.config.length; i++) { let key; - let storage; + let dataset; let dependency = dataDependencies.config[i]; key = dependency.devTeam + "-" + @@ -54,9 +54,9 @@ dependency.dataSet + "-" + dependency.dataSetVersion - storage = dataDependencies.dataSets.get(key); + dataset = dataDependencies.dataSets.get(key); - storages.push(storage); + datasets.push(dataset); } @@ -70,7 +70,7 @@ } function finalize() { - storages = undefined + datasets = undefined dataFiles = undefined multiPeriodDataFiles = undefined statusDependencies = undefined @@ -308,7 +308,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsMarketFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } let dependency = dataDependencies.config[dependencyIndex]; - let storage = storages[dependencyIndex]; + let dataset = datasets[dependencyIndex]; getFile(); @@ -328,7 +328,7 @@ let filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel; - storage.getTextFile(filePath, fileName, onFileReceived); + dataset.getTextFile(filePath, fileName, onFileReceived); function onFileReceived(err, text) { @@ -612,7 +612,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } let dependency = dataDependencies.config[dependencyIndex]; - let storage = storages[dependencyIndex]; + let dataset = datasets[dependencyIndex]; let previousFile; let currentFile; @@ -646,7 +646,7 @@ } let fileName = market.assetA + '_' + market.assetB + ".json"; - storage.getTextFile(filePath, fileName, onFileReceived); + dataset.getTextFile(filePath, fileName, onFileReceived); function onFileReceived(err, text) { @@ -722,7 +722,7 @@ } let fileName = market.assetA + '_' + market.assetB + ".json"; - storage.getTextFile(filePath, fileName, onFileReceived); + dataset.getTextFile(filePath, fileName, onFileReceived); function onFileReceived(err, text) { diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index b05a787ee..3802235d0 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -16,7 +16,7 @@ let statusDependencies; let dataDependencies; - let storages = []; + let datasets = []; let dataFiles = []; let usertBot; @@ -45,7 +45,7 @@ for (let i = 0; i < dataDependencies.config.length; i++) { let key; - let storage; + let dataset; let dependency = dataDependencies.config[i]; key = dependency.devTeam + "-" + @@ -54,9 +54,9 @@ dependency.dataSet + "-" + dependency.dataSetVersion - storage = dataDependencies.dataSets.get(key); + dataset = dataDependencies.dataSets.get(key); - storages.push(storage); + datasets.push(dataset); } @@ -73,7 +73,7 @@ } function finalize() { - storages = undefined + datasets = undefined dataFiles = undefined statusDependencies = undefined dataDependencies = undefined @@ -411,7 +411,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } let dependency = dataDependencies.config[dependencyIndex]; - let storage = storages[dependencyIndex]; + let dataset = datasets[dependencyIndex]; let previousFile; let currentFile; @@ -438,7 +438,7 @@ } let fileName = market.assetA + '_' + market.assetB + ".json"; - storage.getTextFile(filePath, fileName, onFileReceived); + dataset.getTextFile(filePath, fileName, onFileReceived); function onFileReceived(err, text) { @@ -508,7 +508,7 @@ } let fileName = market.assetA + '_' + market.assetB + ".json"; - storage.getTextFile(filePath, fileName, onFileReceived); + dataset.getTextFile(filePath, fileName, onFileReceived); function onFileReceived(err, text) { diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 999b6673f..70c77d75d 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -14,7 +14,7 @@ let statusDependencies; let dataDependencies; - let storages = []; + let datasets = []; let dataFiles = []; let usertBot; @@ -39,7 +39,7 @@ for (let i = 0; i < dataDependencies.config.length; i++) { let key; - let storage; + let dataset; let dependency = dataDependencies.config[i]; key = dependency.devTeam + "-" + @@ -48,9 +48,9 @@ dependency.dataSet + "-" + dependency.dataSetVersion - storage = dataDependencies.dataSets.get(key); + dataset = dataDependencies.dataSets.get(key); - storages.push(storage); + datasets.push(dataset); } @@ -67,7 +67,7 @@ } function finalize() { - storages = undefined + datasets = undefined dataFiles = undefined statusDependencies = undefined dataDependencies = undefined @@ -163,7 +163,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } let dependency = dataDependencies.config[dependencyIndex]; - let storage = storages[dependencyIndex]; + let dataset = datasets[dependencyIndex]; getFile(); @@ -182,7 +182,7 @@ } else { filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; } - storage.getTextFile(filePath, fileName, onFileReceived); + dataset.getTextFile(filePath, fileName, onFileReceived); function onFileReceived(err, text) { From dfaa8a9c513183f267bd84847678007fc84cf9c1 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 11 Nov 2019 15:24:27 +0100 Subject: [PATCH 11/38] Refactoring for nodeArray --- DataDependencies.js | 16 ++++++++-------- MultiPeriod.js | 12 ++++++------ MultiPeriodDaily.js | 6 +++--- MultiPeriodMarket.js | 8 ++++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/DataDependencies.js b/DataDependencies.js index 0be8aa16e..20bca751d 100644 --- a/DataDependencies.js +++ b/DataDependencies.js @@ -6,7 +6,7 @@ const MODULE_NAME = "Data Dependencies"; let thisObject = { - config: undefined, + nodeArray: undefined, dataSets: new Map(), initialize: initialize, keys: [] @@ -21,9 +21,9 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - thisObject.config = pDataDependenciesConfig; + thisObject.nodeArray = pDataDependenciesConfig; - if (thisObject.config === undefined) { + if (thisObject.nodeArray === undefined) { // We allow old indicators not to declare their data dependencies. @@ -32,17 +32,17 @@ } /* - For each dependency declared at the bot config, we will initialize a DataSet as part of this initialization process. + For each dependency declared at the bot nodeArray, we will initialize a DataSet as part of this initialization process. */ let alreadyCalledBack = false; let addCount = 0; - for (let i = 0; i < thisObject.config.length; i++) { + for (let i = 0; i < thisObject.nodeArray.length; i++) { let dataSetModule = DATA_SET.newDataSet(BOT, logger); - dataSetModule.initialize(thisObject.config[i], onInitilized); + dataSetModule.initialize(thisObject.nodeArray[i], onInitilized); function onInitilized(err) { @@ -65,14 +65,14 @@ let key; - key = thisObject.config[i].devTeam + "-" + thisObject.config[i].bot + "-" + thisObject.config[i].product + "-" + thisObject.config[i].dataSet + "-" + thisObject.config[i].dataSetVersion; + key = thisObject.nodeArray[i].devTeam + "-" + thisObject.nodeArray[i].bot + "-" + thisObject.nodeArray[i].product + "-" + thisObject.nodeArray[i].dataSet + "-" + thisObject.nodeArray[i].dataSetVersion; thisObject.keys.push(key); thisObject.dataSets.set(key, dataSetModule); if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addDataSet -> DataSet added to Map. -> key = " + key); } - if (addCount === thisObject.config.length) { + if (addCount === thisObject.nodeArray.length) { if (alreadyCalledBack === false) { alreadyCalledBack = true callBackFunction(global.DEFAULT_OK_RESPONSE); diff --git a/MultiPeriod.js b/MultiPeriod.js index 024f7f111..a2707c4e3 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -42,11 +42,11 @@ dataDependencies = pDataDependencies; processConfig = pProcessConfig; - for (let i = 0; i < dataDependencies.config.length; i++) { + for (let i = 0; i < dataDependencies.nodeArray.length; i++) { let key; let dataset; - let dependency = dataDependencies.config[i]; + let dependency = dataDependencies.nodeArray[i]; key = dependency.devTeam + "-" + dependency.bot + "-" + @@ -307,7 +307,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsMarketFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } - let dependency = dataDependencies.config[dependencyIndex]; + let dependency = dataDependencies.nodeArray[dependencyIndex]; let dataset = datasets[dependencyIndex]; getFile(); @@ -377,7 +377,7 @@ dependencyIndex++; - if (dependencyIndex < dataDependencies.config.length) { + if (dependencyIndex < dataDependencies.nodeArray.length) { dependencyLoopBody(); @@ -611,7 +611,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } - let dependency = dataDependencies.config[dependencyIndex]; + let dependency = dataDependencies.nodeArray[dependencyIndex]; let dataset = datasets[dependencyIndex]; let previousFile; @@ -785,7 +785,7 @@ dependencyIndex++; - if (dependencyIndex < dataDependencies.config.length) { + if (dependencyIndex < dataDependencies.nodeArray.length) { dependencyLoopBody(); diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 3802235d0..33588ac3b 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -42,7 +42,7 @@ dataDependencies = pDataDependencies; processConfig = pProcessConfig; - for (let i = 0; i < dataDependencies.config.length; i++) { + for (let i = 0; i < dataDependencies.nodeArray.length; i++) { let key; let dataset; @@ -410,7 +410,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } - let dependency = dataDependencies.config[dependencyIndex]; + let dependency = dataDependencies.nodeArray[dependencyIndex]; let dataset = datasets[dependencyIndex]; let previousFile; @@ -577,7 +577,7 @@ dependencyIndex++; - if (dependencyIndex < dataDependencies.config.length) { + if (dependencyIndex < dataDependencies.nodeArray.length) { dependencyLoopBody(); diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 70c77d75d..a5f286233 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -36,11 +36,11 @@ dataDependencies = pDataDependencies; processConfig = pProcessConfig; - for (let i = 0; i < dataDependencies.config.length; i++) { + for (let i = 0; i < dataDependencies.nodeArray.length; i++) { let key; let dataset; - let dependency = dataDependencies.config[i]; + let dependency = dataDependencies.nodeArray[i]; key = dependency.devTeam + "-" + dependency.bot + "-" + @@ -162,7 +162,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> Entering function."); } - let dependency = dataDependencies.config[dependencyIndex]; + let dependency = dataDependencies.nodeArray[dependencyIndex]; let dataset = datasets[dependencyIndex]; getFile(); @@ -231,7 +231,7 @@ dependencyIndex++; - if (dependencyIndex < dataDependencies.config.length) { + if (dependencyIndex < dataDependencies.nodeArray.length) { dependencyLoopBody(); From 18323d526d504eced83b63e46f9c1bbbe9e10eb4 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 11 Nov 2019 16:22:56 +0100 Subject: [PATCH 12/38] Data Dependencies upgraded to be UI based --- DataDependencies.js | 24 +++++++-- DataSet.js | 82 ++++++++++++++++++++++++++---- IndicatorBotProcessMainLoop.js | 2 +- Root.js | 2 +- StatusReport.js | 92 +++++++++++++++++----------------- TradingBotProcessMainLoop.js | 2 +- server.js | 2 +- 7 files changed, 141 insertions(+), 65 deletions(-) diff --git a/DataDependencies.js b/DataDependencies.js index 20bca751d..ec7bbbb95 100644 --- a/DataDependencies.js +++ b/DataDependencies.js @@ -2,9 +2,10 @@ const FULL_LOG = true; const LOG_FILE_CONTENT = false; - const MODULE_NAME = "Data Dependencies"; + let bot = BOT + let thisObject = { nodeArray: undefined, dataSets: new Map(), @@ -15,15 +16,28 @@ return thisObject; - function initialize(pDataDependenciesConfig, callBackFunction) { + function initialize(callBackFunction) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - thisObject.nodeArray = pDataDependenciesConfig; + /* Basic Valdidations */ + if (bot.processNode.referenceParent.processDependencies !== undefined) { + if (bot.processNode.referenceParent.processDependencies.dataDependencies !== undefined) { + thisObject.nodeArray = bot.processNode.referenceParent.processDependencies.dataDependencies + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have data dependencies at all."); + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + } + } else { + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not data dependencies."); + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + } - if (thisObject.nodeArray === undefined) { + if (thisObject.nodeArray.length === 0) { // We allow old indicators not to declare their data dependencies. @@ -32,7 +46,7 @@ } /* - For each dependency declared at the bot nodeArray, we will initialize a DataSet as part of this initialization process. + For each dependency declared at the nodeArray, we will initialize a DataSet as part of this initialization process. */ let alreadyCalledBack = false; diff --git a/DataSet.js b/DataSet.js index 17b5d8ee4..4d6a72bc4 100644 --- a/DataSet.js +++ b/DataSet.js @@ -1,6 +1,6 @@ exports.newDataSet = function newDataSet(BOT, logger) { - const MODULE_NAME = "Data Set"; + const MODULE_NAME = "Dataset"; let bot = BOT; @@ -10,7 +10,7 @@ exports.newDataSet = function newDataSet(BOT, logger) { createTextFile: createTextFile }; - let dependencyConfig; + let dataDependencyNode; /* Storage account to be used here. */ @@ -19,16 +19,76 @@ exports.newDataSet = function newDataSet(BOT, logger) { return thisObject; - function initialize(pDependencyConfig, callBackFunction) { + function initialize(pDataDependencyNode, callBackFunction) { try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - logger.fileName = MODULE_NAME + "." + pDependencyConfig.bot + "." + pDependencyConfig.product + "." + pDependencyConfig.dataSet; + dataDependencyNode = pDataDependencyNode; + logger.fileName = MODULE_NAME + "." + dataDependencyNode.type + "." + dataDependencyNode.name + "." + dataDependencyNode.id; - dependencyConfig = pDependencyConfig; + /* Some very basic validations that we have all the information needed. */ + if (dataDependencyNode.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Data Dependency without Reference Parent. Data Dependency = " + JSON.stringify(dataDependencyNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(dataDependencyNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(dataDependencyNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Team. Bot = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (dataDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Team witn no codeName defined. Team = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* Simplifying the access to basic info */ + dataDependencyNode.dataSet = dataDependencyNode.referenceParent.code.codeName + dataDependencyNode.product = dataDependencyNode.referenceParent.parentNode.code.codeName + dataDependencyNode.bot = dataDependencyNode.referenceParent.parentNode.parentNode.code.codeName + dataDependencyNode.devTeam = dataDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + dataDependencyNode.botVersion = { + "major": 1, + "minor": 0 + } + dataDependencyNode.dataSetVersion = "dataSet.V1" callBackFunction(global.DEFAULT_OK_RESPONSE); @@ -44,11 +104,11 @@ exports.newDataSet = function newDataSet(BOT, logger) { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTextFile -> Entering function."); } - let filePathRoot = dependencyConfig.devTeam + "/" + dependencyConfig.bot + "." + dependencyConfig.botVersion.major + "." + dependencyConfig.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dependencyConfig.dataSetVersion; + let filePathRoot = dataDependencyNode.devTeam + "/" + dataDependencyNode.bot + "." + dataDependencyNode.botVersion.major + "." + dataDependencyNode.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dataDependencyNode.dataSetVersion; let filePath = filePathRoot + "/Output/" + pFolderPath; filePath += '/' + pFileName - fileStorage.getTextFile(dependencyConfig.devTeam, filePath, onFileReceived); + fileStorage.getTextFile(dataDependencyNode.devTeam, filePath, onFileReceived); function onFileReceived(err, text) { @@ -72,7 +132,7 @@ exports.newDataSet = function newDataSet(BOT, logger) { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> pFolderPath = " + pFolderPath); } if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> pFileName = " + pFileName); } - let ownerId = dependencyConfig.devTeam + "-" + dependencyConfig.bot + "-" + dependencyConfig.botVersion.major + "-" + dependencyConfig.botVersion.minor + "-" + dependencyConfig.dataSetVersion; + let ownerId = dataDependencyNode.devTeam + "-" + dataDependencyNode.bot + "-" + dataDependencyNode.botVersion.major + "-" + dataDependencyNode.botVersion.minor + "-" + dataDependencyNode.dataSetVersion; let botId = bot.devTeam + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.dataSetVersion; if (ownerId !== botId) { @@ -86,10 +146,10 @@ exports.newDataSet = function newDataSet(BOT, logger) { return; } - let filePathRoot = dependencyConfig.devTeam + "/" + dependencyConfig.bot + "." + dependencyConfig.botVersion.major + "." + dependencyConfig.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dependencyConfig.dataSetVersion; + let filePathRoot = dataDependencyNode.devTeam + "/" + dataDependencyNode.bot + "." + dataDependencyNode.botVersion.major + "." + dataDependencyNode.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dataDependencyNode.dataSetVersion; let filePath = filePathRoot + "/Output/" + pFolderPath + '/' + pFileName; - fileStorage.createTextFile(dependencyConfig.devTeam, filePath, pFileContent, onFileCreated); + fileStorage.createTextFile(dataDependencyNode.devTeam, filePath, pFileContent, onFileCreated); function onFileCreated(err) { diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index 99bda771b..76f8675ab 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -385,7 +385,7 @@ dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET); - dataDependencies.initialize(processConfig.dataDependencies, onInizialized); + dataDependencies.initialize(onInizialized); function onInizialized(err) { diff --git a/Root.js b/Root.js index a76e82d11..19d741e01 100644 --- a/Root.js +++ b/Root.js @@ -79,7 +79,7 @@ logContent: false, intensiveLogging: false }, - "Data Set": { + "Dataset": { logInfo: true, logWarnings: false, logErrors: true, diff --git a/StatusReport.js b/StatusReport.js index 4e24d5201..b2e931a99 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -22,7 +22,7 @@ verifyMarketComplete: verifyMarketComplete }; - let owner; // This is the bot owner of the Status Report. Only owners can save the report and override the existing content. + let statusDependencyNode; /* Utilities needed. */ @@ -40,97 +40,99 @@ return thisObject; - function initialize(pOwner, pMonth, pYear, callBackFunction) { + function initialize(pStatusDependencyNode, pMonth, pYear, callBackFunction) { try { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - owner = pOwner; + statusDependencyNode = pStatusDependencyNode; + logger.fileName = MODULE_NAME + "." + statusDependencyNode.type + "." + statusDependencyNode.name + "." + statusDependencyNode.id; + month = pMonth; year = pYear; /* Some very basic validations that we have all the information needed. */ - if (owner.referenceParent === undefined) { - logger.write(MODULE_NAME, "[ERROR] initialize -> Status Dependency without Reference Parent. Status Dependency = " + JSON.stringify(owner)); + if (statusDependencyNode.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Status Dependency without Reference Parent. Status Dependency = " + JSON.stringify(statusDependencyNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (owner.referenceParent.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] initialize -> Status Report not attached to a Process Definition. Status Report = " + JSON.stringify(owner.referenceParent)); + if (statusDependencyNode.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Status Report not attached to a Process Definition. Status Report = " + JSON.stringify(statusDependencyNode.referenceParent)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (owner.referenceParent.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(owner.referenceParent.parentNode)); + if (statusDependencyNode.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (owner.referenceParent.parentNode.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition not attached to a Bot. Process Definition = " + JSON.stringify(owner.referenceParent.parentNode)); + if (statusDependencyNode.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition not attached to a Bot. Process Definition = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (owner.referenceParent.parentNode.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(owner.referenceParent.parentNode.parentNode)); + if (statusDependencyNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (owner.referenceParent.parentNode.parentNode.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Team. Bot = " + JSON.stringify(owner.referenceParent.parentNode.parentNode)); + if (statusDependencyNode.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Team. Bot = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (owner.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] initialize -> Team witn no codeName defined. Team = " + JSON.stringify(owner.referenceParent.parentNode.parentNode.parentNode)); + if (statusDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] initialize -> Team witn no codeName defined. Team = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } /* Simplifying the access to basic info */ - owner.bot = owner.referenceParent.parentNode.parentNode.code.codeName - owner.process = owner.referenceParent.parentNode.code.codeName - owner.type = owner.referenceParent.parentNode.parentNode.type - owner.devTeam = owner.referenceParent.parentNode.parentNode.parentNode.code.codeName + statusDependencyNode.bot = statusDependencyNode.referenceParent.parentNode.parentNode.code.codeName + statusDependencyNode.process = statusDependencyNode.referenceParent.parentNode.code.codeName + statusDependencyNode.type = statusDependencyNode.referenceParent.parentNode.parentNode.type + statusDependencyNode.devTeam = statusDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName /* Let's see if the process is run monthly or not. */ - if (owner.referenceParent.parentNode.code.startMode !== undefined) { - if (owner.referenceParent.parentNode.code.startMode.allMonths !== undefined) { - if (owner.referenceParent.parentNode.code.startMode.oneMonth !== undefined) { - if (owner.referenceParent.parentNode.code.startMode.allMonths.run === "true" || owner.referenceParent.parentNode.code.startMode.oneMonth.run === "true") { - owner.processRunMonthly = true + if (statusDependencyNode.referenceParent.parentNode.code.startMode !== undefined) { + if (statusDependencyNode.referenceParent.parentNode.code.startMode.allMonths !== undefined) { + if (statusDependencyNode.referenceParent.parentNode.code.startMode.oneMonth !== undefined) { + if (statusDependencyNode.referenceParent.parentNode.code.startMode.allMonths.run === "true" || statusDependencyNode.referenceParent.parentNode.code.startMode.oneMonth.run === "true") { + statusDependencyNode.processRunMonthly = true } } } } /* We retrieve the report main utility */ - if (owner.code !== undefined) { - if (owner.code.mainUtility !== undefined) { - thisObject.mainUtility = owner.code.mainUtility + if (statusDependencyNode.code !== undefined) { + if (statusDependencyNode.code.mainUtility !== undefined) { + thisObject.mainUtility = statusDependencyNode.code.mainUtility } } /* This stuff is still hardcoded and unresolved. */ - owner.botVersion = { + statusDependencyNode.botVersion = { "major": 1, "minor": 0 } - owner.dataSetVersion = "dataSet.V1" + statusDependencyNode.dataSetVersion = "dataSet.V1" - logger.fileName = MODULE_NAME + "." + owner.bot + "." + owner.process; - if (bot.SESSION !== undefined && owner.type === "Trading Bot Instance") { + + if (bot.SESSION !== undefined && statusDependencyNode.type === "Trading Bot Instance") { sessionPath = bot.SESSION.folderName + "/" } - if (owner.processRunMonthly === true) { - logger.fileName = MODULE_NAME + "." + owner.bot + "." + owner.process + "." + year + "." + month; + if (statusDependencyNode.processRunMonthly === true) { + logger.fileName = MODULE_NAME + "." + statusDependencyNode.bot + "." + statusDependencyNode.process + "." + year + "." + month; timePath = "/" + year + "/" + month; } @@ -151,23 +153,23 @@ let fileName = "Status.Report." + global.MARKET.assetA + '_' + global.MARKET.assetB + ".json"; let filePath; - let ownerId = owner.devTeam + "-" + owner.bot + "-" + owner.botVersion.major + "-" + owner.botVersion.minor + "-" + owner.process + "-" + owner.dataSetVersion; + let ownerId = statusDependencyNode.devTeam + "-" + statusDependencyNode.bot + "-" + statusDependencyNode.botVersion.major + "-" + statusDependencyNode.botVersion.minor + "-" + statusDependencyNode.process + "-" + statusDependencyNode.dataSetVersion; let botId = bot.devTeam + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.process + "-" + bot.dataSetVersion; if (ownerId !== botId) { - let rootPath = owner.devTeam + "/" + owner.bot + "." + owner.botVersion.major + "." + owner.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + owner.dataSetVersion; - filePath = rootPath + "/Reports/" + sessionPath + owner.process + timePath; + let rootPath = statusDependencyNode.devTeam + "/" + statusDependencyNode.bot + "." + statusDependencyNode.botVersion.major + "." + statusDependencyNode.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + statusDependencyNode.dataSetVersion; + filePath = rootPath + "/Reports/" + sessionPath + statusDependencyNode.process + timePath; } else { - filePath = bot.filePathRoot + "/Reports/" + sessionPath + owner.process + timePath; + filePath = bot.filePathRoot + "/Reports/" + sessionPath + statusDependencyNode.process + timePath; } - if (owner.type === 'Trading') { + if (statusDependencyNode.type === 'Trading') { fileName = bot.startMode + "." + fileName; } filePath += '/' + fileName - fileStorage.getTextFile(owner.devTeam, filePath, onFileReceived); + fileStorage.getTextFile(statusDependencyNode.devTeam, filePath, onFileReceived); function onFileReceived(err, text) { @@ -235,10 +237,10 @@ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] save -> Entering function."); } - let ownerId = owner.devTeam + "-" + owner.bot + "-" + owner.botVersion.major + "-" + owner.botVersion.minor + "-" + owner.process + "-" + owner.dataSetVersion; + let ownerId = statusDependencyNode.devTeam + "-" + statusDependencyNode.bot + "-" + statusDependencyNode.botVersion.major + "-" + statusDependencyNode.botVersion.minor + "-" + statusDependencyNode.process + "-" + statusDependencyNode.dataSetVersion; let botId = bot.devTeam + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.process + "-" + bot.dataSetVersion; - if (ownerId !== botId && owner.process !== "Context") { // Context is a special case where the report is created by the Context.js module itself. + if (ownerId !== botId && statusDependencyNode.process !== "Context") { // Context is a special case where the report is created by the Context.js module itself. let customErr = { result: global.CUSTOM_FAIL_RESPONSE.result, @@ -250,7 +252,7 @@ } let fileName = "Status.Report." + global.MARKET.assetA + '_' + global.MARKET.assetB + ".json"; - let filePath = bot.filePathRoot + "/Reports/" + sessionPath + owner.process + timePath; + let filePath = bot.filePathRoot + "/Reports/" + sessionPath + statusDependencyNode.process + timePath; if (bot.type === 'Trading' ) { fileName = bot.startMode + "." + fileName; @@ -259,7 +261,7 @@ filePath += '/' + fileName let fileContent = JSON.stringify(thisObject.file); - fileStorage.createTextFile(owner.devTeam, filePath, fileContent + '\n', onFileCreated); + fileStorage.createTextFile(statusDependencyNode.devTeam, filePath, fileContent + '\n', onFileCreated); function onFileCreated(err) { diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index 8877a59f9..af2787b80 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -420,7 +420,7 @@ dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET); - dataDependencies.initialize(processConfig.dataDependencies, onInizialized); + dataDependencies.initialize(onInizialized); function onInizialized(err) { diff --git a/server.js b/server.js index 7ad8aa56c..4b4f4a776 100644 --- a/server.js +++ b/server.js @@ -73,7 +73,7 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. let argument - argument = '{"type":"Task","name":"Generates 1 min to 24 hs Candles & Volumes","bot":{"type":"Indicator Bot Instance","name":"Olivia","code":{"team":"AAMasters","bot":"AAOlivia","repo":"AAOlivia-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Daily","code":{"process":"Multi-Period-Daily"},"id":"050d715f-2851-48b4-86b6-d93ab4baa1b2","referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"codeName":"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},"id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"f349a029-5381-4f8e-92d4-1b7e7df76966","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","code":{"codeName":"Multi-Period-Daily"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"},"id":"742ab33f-d382-4a40-ab03-42e4f1916cc2"}},{"type":"Output Dataset","name":"New Output Dataset","id":"15fd40ad-ceca-46ea-8fdd-4a396f9bde14","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","code":{"codeName":"Multi-Period-Daily"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"},"id":"404b8d12-db14-462c-b4e4-6ee5093560c3"}}],"id":"e130a389-0b18-4433-809d-f44162a00a4a"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":{"mainUtility":"Market Starting Point"},"id":"93f6d813-594f-49cd-a32f-181a7b8352ee","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Historic-Trades","code":{"codeName":"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":"false","year":"2018","month":"01"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":60000,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"cb413af9-e1f6-4dbe-ab8c-9f7eb949bd93","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"codeName":"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},"id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"fbaa8e58-7e12-48d8-81e5-1050c034c736","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"}},"id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"c9f72b18-362c-4663-9dab-02b592aba3a1","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"3b42a93e-c933-420d-9504-25092a0d1279"},"id":"20d5740b-5711-4b8c-9675-a5391f5ba245"}},{"type":"Data Dependency","name":"New Data Dependency","id":"9aff9fc8-4935-4043-9a95-ee2fe02f2cc1","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"4e714023-0cfd-4945-a573-52922711cd09"},"id":"2788ed30-ed62-4638-9a1d-35dc7e826870"}}],"id":"dab7d491-7a05-47ac-9f86-a551eb24ef67"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","code":{"codeName":"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},"id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"973fe943-4d30-49eb-84b3-52f6cd9cdbd1"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"b83c7f7c-d0c4-4daa-aa6f-41adaa2626b0","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f3b263bb-1051-40d8-a69a-d8640cf9afbe","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"ac785a4c-9848-4e7e-b2ac-5c3c98defa2f","parentNode":{"type":"Process Definition","name":"Multi-Period-Daily","id":"5c56f87a-e3b2-46fc-8e4e-f40c0dacabb5","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"522fca96-a4d5-4cba-9271-4e5f66209390"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"963bf983-f910-4c3e-bdee-b968d56dd155"}}},{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"111b43d5-d4ff-414c-8b9f-24f79e2a0254","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"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},"id":"383c3970-13d5-4313-bd8f-7e5d28948577","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"New Output Dataset","id":"7c42483e-6b75-4b61-91c2-597b3fea1aa0","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"36ae2d5d-6734-4526-b2f8-31c89a0c4fc1"},"id":"7160ac78-d806-4bac-955b-a201c21c2e7e"}},{"type":"Output Dataset","name":"New Output Dataset","id":"5aa52a69-6139-41c0-bce5-68f1419ab7f2","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"},"id":"b2d5ba69-67ac-490f-9e74-28239b35a92e"},"id":"8f6d7c41-f829-4157-8365-707a4789138e"}}],"id":"2912df2e-2d79-4a8b-af79-e79d26be51d0"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Market Starting Point","code":{"mainUtility":"Market Starting Point"},"id":"92e1f19f-986b-43c8-a742-2b4832e061cc","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Historic-Trades","code":{"codeName":"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":"false","year":"2018","month":"01"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":60000,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"e2c3f7c3-b74c-4145-8a60-f73cc3364629","parentNode":{"type":"Sensor Bot","name":"Charly","code":{"codeName":"AACharly"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"957bef47-cd90-4dfa-bbb1-3696c527b010"}},"id":"c542a73e-a350-431f-ad3e-10f3ebef8ec7"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"701ead56-8e85-4138-bfe8-263b1d59869d","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000},"id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"}},"id":"deaa7a7d-b186-42d8-ad64-aab8a7638ca9"}},{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"757b878a-99bd-402c-bd1c-4ae7961e35bf","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"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},"id":"383c3970-13d5-4313-bd8f-7e5d28948577","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"4f71d561-5872-4539-8408-f481be275559"}}],"dataDependencies":[{"type":"Data Dependency","name":"New Data Dependency","id":"72f6e0db-ed45-4ee8-aac0-188a7db38c1b","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Candles","code":{"codeName":"Candles"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"3b42a93e-c933-420d-9504-25092a0d1279"},"id":"20d5740b-5711-4b8c-9675-a5391f5ba245"}},{"type":"Data Dependency","name":"New Data Dependency","id":"0bcc9ab5-7534-4aae-ab82-5dde001673db","referenceParent":{"type":"Dataset Definition","name":"One-Min","code":{"codeName":"One-Min"},"parentNode":{"type":"Product Definition","name":"Volumes","code":{"codeName":"Volumes"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Bruce","code":{"codeName":"AABruce"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"952e8327-4c9a-422a-bf64-cd773ccc36dd"},"id":"4e714023-0cfd-4945-a573-52922711cd09"},"id":"2788ed30-ed62-4638-9a1d-35dc7e826870"}}],"id":"af72941b-880c-47b4-8a23-fc7279809d4d"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"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},"id":"383c3970-13d5-4313-bd8f-7e5d28948577","parentNode":{"type":"Indicator Bot","name":"Olivia","code":{"codeName":"AAOlivia"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"30d6289c-4f5c-471e-9d6d-93bfd5453781"}},"id":"4f71d561-5872-4539-8408-f481be275559"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"d54f53d2-7a7e-4ff7-afdf-33d25405865c","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"f3b263bb-1051-40d8-a69a-d8640cf9afbe","parentNode":{"type":"Process Definition","name":"Single-Period-Daily","id":"661e048c-e9e5-435e-bdf9-53dce5ebfa0f","code":{"codeName":"Single-Period-Daily","description":"Produces two data-sets with candles and volumes.","startMode":{"allMonths":{"run":"true","minYear":"2019","maxYear":"2019"},"oneMonth":{"run":"false","year":"2018","month":"6"},"noTime":{"run":"false"},"fixedInterval":{"run":"false","interval":0}},"deadWaitTime":0,"normalWaitTime":0,"retryWaitTime":10000,"sleepWaitTime":3600000,"comaWaitTime":86400000}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"63929f31-bc0f-4d9c-9ddc-3d7e76305e51","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"383c3970-13d5-4313-bd8f-7e5d28948577","code":{"codeName":"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}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"9cd6a59f-a537-4c58-ac92-915db8416ad5"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","id":"406ff220-1437-4030-816b-3d1de0988a60"}}}],"id":"1e587326-ad4d-4ea8-9273-367b3009ea3d"},"id":"e4164330-478a-4de8-b3ae-db67225bfa7a"}' + argument = '{"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula","repo":"AAChris-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Bollinger Channels","id":"d804e799-7c12-42f5-8de2-52a3a7d5daa4","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Channels","code":{"codeName":"Bollinger-Channels","displayName":"Bollinger-Channels","description":"Two sets of files per period that contains Bollinger-Bands-Channels-Patterns.","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"1f111f72-624f-4166-9f99-ab8d785d5267"},"id":"610cd062-ab88-4892-b711-0fb430305c61"}},{"type":"Output Dataset","name":"Bollinger Standard Channels","id":"e524685d-8da0-41f6-839d-9a4100e6e3fe","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Channels","code":{"codeName":"Bollinger-Standard-Channels","displayName":"Bollinger-Standard-Channels","description":"","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"94bc3958-cc85-43a1-a178-103e84f42910"},"id":"22c1f2c6-3838-4657-add8-2e01d7056917"}},{"type":"Output Dataset","name":"Bollinger Sub-Channels","id":"eb0e7947-95e4-4f4a-87ea-1fb430b8b503","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Sub-Channels","code":{"codeName":"Bollinger-Sub-Channels","displayName":"Bollinger-Sub-Channels","description":"Bollinger Sub Channels with slope.","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"080c87fc-4546-4482-ab11-180a33f6d9ab"},"id":"869f615e-0cc0-44fd-8648-4a951c23a410"}},{"type":"Output Dataset","name":"Bollinger Standard Sub-Channels","id":"dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Sub-Channels","code":{"codeName":"Bollinger-Standard-Sub-Channels","displayName":"Bollinger-Standard-Sub-Channels","description":"Bollinger Standard Sub Channels with slope.","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"79ce9c05-8a4c-4b58-850f-288037acc702"},"id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}],"id":"dd39a54a-3714-446a-9a41-dfd83557664c"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"ae627a69-4089-432d-b379-1b12ce4bd75e","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"4a603114-d464-46db-90a1-62304274117a","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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},"id":"941a0543-0cc0-4a5d-a782-728d2835c100","parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"}},"id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}],"dataDependencies":[{"type":"Data Dependency","name":"Chris Bollinger Bands","id":"ea27be54-93a3-48e3-8c52-72bc397b6c66","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Bollinger Bands","code":{"codeName":"Bollinger-Bands"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"},"id":"29088877-8d63-4217-8d34-8742afc262f4"},"id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}],"id":"8b509294-6028-40e2-88a8-655a34245ed8"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"f4720e04-5def-4c0b-be42-33e50ac6a6b7","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"671530dc-5ba5-4c8d-bc23-f228e22c56b3","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ba2de283-a059-44d0-b229-1c7988f48f51"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","code":{"type":"Code","code":"// Write your code here","id":"26413b25-24b7-47d1-b73d-12cef3198657"},"id":"13b23649-2695-48cb-aa9a-efe8acb7ab2c"},"loop":{"type":"Procedure Loop","name":"New Procedure Loop","code":{"type":"Code","code":"// Write your code here","id":"f0a3227a-094b-4aae-babb-d78f9e9b38ed"},"id":"b7a118de-5ebe-4efb-9b10-3b69680063a3"},"id":"859a0c08-b0fd-4d42-851a-21e898d42ec8"}}}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' try { global.TASK_NODE = JSON.parse(argument) From fcbb89312344eb974cbbc3f6dec83340d813adf3 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 12 Nov 2019 11:04:30 +0100 Subject: [PATCH 13/38] jsonifyDataFile implemented --- IndicatorBotCommons.js | 67 +++++++++++++++++++++++++++++++++++++----- IndicatorBotMarket.js | 25 ++++++++++++++++ server.js | 3 +- 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/IndicatorBotCommons.js b/IndicatorBotCommons.js index 2fef25f43..ab9e5af03 100644 --- a/IndicatorBotCommons.js +++ b/IndicatorBotCommons.js @@ -8,8 +8,8 @@ const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; let thisObject = { - buildBandsArray: buildBandsArray, - buildChannels: buildChannels, + jsonifyDataFile: jsonifyDataFile, + calculationsProcedure: calculationsProcedure, buildStandardChannels: buildStandardChannels, buildSubChannels: buildSubChannels, buildStandardSubChannels: buildStandardSubChannels, @@ -18,24 +18,75 @@ return thisObject; - function buildDependecyArray(dataFile, recordDefinition) { + function jsonifyDataFile(dataFile, recordDefinition) { - let dependecyArray = [] + /* + This function has as an input the raw data on files and creates with it an array of JSON objects + with not calculated properties for later being consumed by Formulas + */ + + let jsonifiedArray = [] for (let i = 0; i < dataFile.length; i++) { let record = {} - for (let j = 0; j < recordDefinition.length; j++) { - let property = recordDefinition[j] - record[property.name] = dataFile[i][j] + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + record[property.code.codeName] = dataFile[i][j] } - dependecyArray.push(record); + jsonifiedArray.push(record); } + return jsonifiedArray + } + + function calculationsProcedure(dependencyArray, variableName, timePeriod) { + + /* + This function has as an input an array of JSON objects, and it adds calculated properties to + complete the set of properties that will be available for Formulas. + */ + + let dependecyArray = [] + + /* This is Initialization Code */ + let lastMovingAverage = 0; + const SIDE_TOLERANCE = 0.5 * timePeriod / ONE_DAY_IN_MILISECONDS; + const SMALL_SLOPE = 1.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + const MEDIUM_SLOPE = 2.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + const HIGH_SLOPE = 4.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + /* This is Initialization Code */ + + for (let i = 0; i < dataFile.length; i++) { + + let indicator = {} + indicator[variableName] = dataFile[i] + + /* This is Add Properties Code */ + if (lastMovingAverage > indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Down'; } + if (lastMovingAverage < indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Up'; } + if (lastMovingAverage === indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Side'; } + + let delta = Math.abs(indicator.bollingerBand.movingAverage - lastMovingAverage); + + indicator.bollingerBand.slope = 'Extreme'; + if (delta < indicator.bollingerBand.movingAverage * HIGH_SLOPE / 100) { indicator.bollingerBand.slope = 'Steep'; } + if (delta < indicator.bollingerBand.movingAverage * MEDIUM_SLOPE / 100) { indicator.bollingerBand.slope = 'Medium'; } + if (delta < indicator.bollingerBand.movingAverage * SMALL_SLOPE / 100) { indicator.bollingerBand.slope = 'Gentle'; } + if (delta < indicator.bollingerBand.movingAverage * SIDE_TOLERANCE / 100) { indicator.bollingerBand.slope = 'Side'; } + + lastMovingAverage = indicator.bollingerBand.movingAverage; + /* This is Add Properties Code */ + + dependecyArray.push(indicator.bollingerBand); + } + return dependecyArray } + + function addCalculatedProperties(dependencyArray, variableName, timePeriod) { let dependecyArray = [] diff --git a/IndicatorBotMarket.js b/IndicatorBotMarket.js index 1fa706c52..97e70f03e 100644 --- a/IndicatorBotMarket.js +++ b/IndicatorBotMarket.js @@ -48,6 +48,31 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } let market = global.MARKET; + + let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies + for (let i = 0; i < dataDependencies.length; i++) { + let dataset = dataDependencies[i].referenceParent + /* + For each dataset in our data dependencies, we should have da dataFile containing the records needed as an imput for this process. + What we need to do first is transform those records into JSON objects that can be used by user-defined formulas. + */ + if (dataset.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataset.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + dataset.inputData = commons.jsonifyDataFile(dataFiles[i], dataset.parentNode.record) + } + + + + + + + + + + let dataFile; let bands = []; diff --git a/server.js b/server.js index 4b4f4a776..a7ca3d339 100644 --- a/server.js +++ b/server.js @@ -73,7 +73,8 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. let argument - argument = '{"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula","repo":"AAChris-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Bollinger Channels","id":"d804e799-7c12-42f5-8de2-52a3a7d5daa4","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Channels","code":{"codeName":"Bollinger-Channels","displayName":"Bollinger-Channels","description":"Two sets of files per period that contains Bollinger-Bands-Channels-Patterns.","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"1f111f72-624f-4166-9f99-ab8d785d5267"},"id":"610cd062-ab88-4892-b711-0fb430305c61"}},{"type":"Output Dataset","name":"Bollinger Standard Channels","id":"e524685d-8da0-41f6-839d-9a4100e6e3fe","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Channels","code":{"codeName":"Bollinger-Standard-Channels","displayName":"Bollinger-Standard-Channels","description":"","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"94bc3958-cc85-43a1-a178-103e84f42910"},"id":"22c1f2c6-3838-4657-add8-2e01d7056917"}},{"type":"Output Dataset","name":"Bollinger Sub-Channels","id":"eb0e7947-95e4-4f4a-87ea-1fb430b8b503","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Sub-Channels","code":{"codeName":"Bollinger-Sub-Channels","displayName":"Bollinger-Sub-Channels","description":"Bollinger Sub Channels with slope.","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"080c87fc-4546-4482-ab11-180a33f6d9ab"},"id":"869f615e-0cc0-44fd-8648-4a951c23a410"}},{"type":"Output Dataset","name":"Bollinger Standard Sub-Channels","id":"dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Sub-Channels","code":{"codeName":"Bollinger-Standard-Sub-Channels","displayName":"Bollinger-Standard-Sub-Channels","description":"Bollinger Standard Sub Channels with slope.","shareWith":"Public"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"79ce9c05-8a4c-4b58-850f-288037acc702"},"id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}],"id":"dd39a54a-3714-446a-9a41-dfd83557664c"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"ae627a69-4089-432d-b379-1b12ce4bd75e","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"4a603114-d464-46db-90a1-62304274117a","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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},"id":"941a0543-0cc0-4a5d-a782-728d2835c100","parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"}},"id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}],"dataDependencies":[{"type":"Data Dependency","name":"Chris Bollinger Bands","id":"ea27be54-93a3-48e3-8c52-72bc397b6c66","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Bollinger Bands","code":{"codeName":"Bollinger-Bands"},"datasets":[],"parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"},"id":"29088877-8d63-4217-8d34-8742afc262f4"},"id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}],"id":"8b509294-6028-40e2-88a8-655a34245ed8"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"f4720e04-5def-4c0b-be42-33e50ac6a6b7","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"671530dc-5ba5-4c8d-bc23-f228e22c56b3","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ba2de283-a059-44d0-b229-1c7988f48f51"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","code":{"type":"Code","code":"// Write your code here","id":"26413b25-24b7-47d1-b73d-12cef3198657"},"id":"13b23649-2695-48cb-aa9a-efe8acb7ab2c"},"loop":{"type":"Procedure Loop","name":"New Procedure Loop","code":{"type":"Code","code":"// Write your code here","id":"f0a3227a-094b-4aae-babb-d78f9e9b38ed"},"id":"b7a118de-5ebe-4efb-9b10-3b69680063a3"},"id":"859a0c08-b0fd-4d42-851a-21e898d42ec8"}}}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' + argument = ' {"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula","repo":"AAPaula-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Bollinger Channels","id":"d804e799-7c12-42f5-8de2-52a3a7d5daa4","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Channels","code":{"codeName":"Bollinger-Channels","displayName":"Bollinger-Channels","description":"Two sets of files per period that contains Bollinger-Bands-Channels-Patterns.","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":{"codeName":"begin","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"caf8122c-3a07-4263-a56f-ffb05df281d6"},"id":"06edb099-ff64-4528-b8c6-599154bc2ddf"},{"type":"Record Property","name":"End","code":{"codeName":"end","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"c8ec0868-8413-468c-88d5-3e43169395e0"},"id":"94656d1f-508a-4e94-a8d2-013690aac596"},{"type":"Record Property","name":"Direction","code":{"codeName":"direction","isNumeric":false},"formula":{"type":"Formula","code":"// Write your formula here","id":"e913afe9-b610-4777-bc9e-6349d436a90a"},"id":"edc0f697-f5e2-446f-bae8-8db7adf91d23"},{"type":"Record Property","name":"Period","code":{"codeName":"period","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"83fce657-a0bb-41ee-80d1-9e2ab980c472"},"id":"eb733f85-6cf5-485e-9bd3-2bf78733c492"},{"type":"Record Property","name":"First Moving Average","code":{"codeName":"firstMovingAverage","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"52490460-35e3-469a-824f-5b139571d4a0"},"id":"a67a15ee-6f7f-48bd-9d55-82c57efc993b"},{"type":"Record Property","name":"Last Moving Average","code":{"codeName":"lastMovingAverage","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"41edc1ef-293a-4ceb-bf67-8042e8fdc4eb"},"id":"0f05bffe-d668-4d25-ab67-d55a4a7c8f5d"},{"type":"Record Property","name":"First Deviation","code":{"codeName":"firstDeviation","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"5af02103-9bc9-4e6c-9684-5a612adfa495"},"id":"eae84509-b689-4a76-9277-eed72af491dd"},{"type":"Record Property","name":"Last Deviation","code":{"codeName":"lastDeviation","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"bc82d292-e71a-4367-9b07-a0e3982ad60d"},"id":"d5c32d1f-9e3c-48e8-8025-a17d57c0812e"}],"id":"6c3e68b0-b781-4c53-9ff7-67c3f747ac8a"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"1f111f72-624f-4166-9f99-ab8d785d5267"},"id":"610cd062-ab88-4892-b711-0fb430305c61"}},{"type":"Output Dataset","name":"Bollinger Standard Channels","id":"e524685d-8da0-41f6-839d-9a4100e6e3fe","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Channels","code":{"codeName":"Bollinger-Standard-Channels","displayName":"Bollinger-Standard-Channels","description":"","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[],"id":"9106250d-b042-4e96-9121-c760b4c1eab2"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"94bc3958-cc85-43a1-a178-103e84f42910"},"id":"22c1f2c6-3838-4657-add8-2e01d7056917"}},{"type":"Output Dataset","name":"Bollinger Sub-Channels","id":"eb0e7947-95e4-4f4a-87ea-1fb430b8b503","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Sub-Channels","code":{"codeName":"Bollinger-Sub-Channels","displayName":"Bollinger-Sub-Channels","description":"Bollinger Sub Channels with slope.","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[],"id":"5a780d7f-70df-4e86-8192-eacccafb5ca8"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"080c87fc-4546-4482-ab11-180a33f6d9ab"},"id":"869f615e-0cc0-44fd-8648-4a951c23a410"}},{"type":"Output Dataset","name":"Bollinger Standard Sub-Channels","id":"dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Sub-Channels","code":{"codeName":"Bollinger-Standard-Sub-Channels","displayName":"Bollinger-Standard-Sub-Channels","description":"Bollinger Standard Sub Channels with slope.","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[],"id":"cdbe5e0f-9292-4d3d-b98a-d441e6565b76"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"79ce9c05-8a4c-4b58-850f-288037acc702"},"id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}],"id":"dd39a54a-3714-446a-9a41-dfd83557664c"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"ae627a69-4089-432d-b379-1b12ce4bd75e","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"4a603114-d464-46db-90a1-62304274117a","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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},"id":"941a0543-0cc0-4a5d-a782-728d2835c100","parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"}},"id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}],"dataDependencies":[{"type":"Data Dependency","name":"Chris Bollinger Bands","id":"ea27be54-93a3-48e3-8c52-72bc397b6c66","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Bollinger Bands","code":{"codeName":"Bollinger-Bands"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":{"codeName":"begin","isNumeric":true},"id":"1e7186ee-c481-401d-827f-a30ee7434d92"},{"type":"Record Property","name":"End","code":{"codeName":"end","isNumeric":true},"id":"15f90f2e-80f0-4575-94d8-6d8957eceee1"},{"type":"Record Property","name":"Moving Average","code":{"codeName":"movingAverage","isNumeric":true},"id":"9786a9cd-1eff-4b5c-ae7c-1dde51659f82"},{"type":"Record Property","name":"Standard Deviation","code":{"codeName":"standardDeviation","isNumeric":true},"id":"e92d7855-bb18-494e-ab37-a895bbcb8287"},{"type":"Record Property","name":"Deviation","code":{"codeName":"deviation","isNumeric":true},"id":"03043530-caa1-416e-8303-840b732e516e"}],"id":"b5a5ec97-7246-42bd-97e4-28fb4a148b2c"},"parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"},"id":"29088877-8d63-4217-8d34-8742afc262f4"},"id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}],"id":"8b509294-6028-40e2-88a8-655a34245ed8"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"f4720e04-5def-4c0b-be42-33e50ac6a6b7","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"671530dc-5ba5-4c8d-bc23-f228e22c56b3","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ba2de283-a059-44d0-b229-1c7988f48f51"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","code":{"type":"Code","code":"// Write your code here","id":"26413b25-24b7-47d1-b73d-12cef3198657"},"id":"13b23649-2695-48cb-aa9a-efe8acb7ab2c"},"loop":{"type":"Procedure Loop","name":"New Procedure Loop","code":{"type":"Code","code":"// Write your code here","id":"f0a3227a-094b-4aae-babb-d78f9e9b38ed"},"id":"b7a118de-5ebe-4efb-9b10-3b69680063a3"},"id":"859a0c08-b0fd-4d42-851a-21e898d42ec8"}}}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' + try { global.TASK_NODE = JSON.parse(argument) From 8eaff70835eb1d6181ea46ac3bce841a682b8fbd Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 12 Nov 2019 17:07:57 +0100 Subject: [PATCH 14/38] Dependencies working --- IndicatorBotCommons.js | 80 ++++++++++++++++++++++++------------------ IndicatorBotMarket.js | 37 ++++++++++++++++++- server.js | 10 +----- 3 files changed, 82 insertions(+), 45 deletions(-) diff --git a/IndicatorBotCommons.js b/IndicatorBotCommons.js index ab9e5af03..d1ae58093 100644 --- a/IndicatorBotCommons.js +++ b/IndicatorBotCommons.js @@ -32,7 +32,9 @@ let record = {} for (let j = 0; j < recordDefinition.properties.length; j++) { let property = recordDefinition.properties[j] - record[property.code.codeName] = dataFile[i][j] + if (property.code.isCalculated !== true) { + record[property.code.codeName] = dataFile[i][j] + } } jsonifiedArray.push(record); @@ -41,52 +43,60 @@ return jsonifiedArray } - function calculationsProcedure(dependencyArray, variableName, timePeriod) { + function calculationsProcedure(jsonArray, recordDefinition, calculationsProcedure, variableName, timePeriod) { /* This function has as an input an array of JSON objects, and it adds calculated properties to complete the set of properties that will be available for Formulas. */ - let dependecyArray = [] + let system = { // These are the available system variables to be used in User Code and Formulas + timePeriod: timePeriod, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared amond different code blocks and formulas. + let resultingArray = [] /* This is Initialization Code */ - let lastMovingAverage = 0; - const SIDE_TOLERANCE = 0.5 * timePeriod / ONE_DAY_IN_MILISECONDS; - const SMALL_SLOPE = 1.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - const MEDIUM_SLOPE = 2.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - const HIGH_SLOPE = 4.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - /* This is Initialization Code */ - - for (let i = 0; i < dataFile.length; i++) { - - let indicator = {} - indicator[variableName] = dataFile[i] - - /* This is Add Properties Code */ - if (lastMovingAverage > indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Down'; } - if (lastMovingAverage < indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Up'; } - if (lastMovingAverage === indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Side'; } - - let delta = Math.abs(indicator.bollingerBand.movingAverage - lastMovingAverage); - - indicator.bollingerBand.slope = 'Extreme'; - if (delta < indicator.bollingerBand.movingAverage * HIGH_SLOPE / 100) { indicator.bollingerBand.slope = 'Steep'; } - if (delta < indicator.bollingerBand.movingAverage * MEDIUM_SLOPE / 100) { indicator.bollingerBand.slope = 'Medium'; } - if (delta < indicator.bollingerBand.movingAverage * SMALL_SLOPE / 100) { indicator.bollingerBand.slope = 'Gentle'; } - if (delta < indicator.bollingerBand.movingAverage * SIDE_TOLERANCE / 100) { indicator.bollingerBand.slope = 'Side'; } - - lastMovingAverage = indicator.bollingerBand.movingAverage; - /* This is Add Properties Code */ - - dependecyArray.push(indicator.bollingerBand); + if (calculationsProcedure.initialization !== undefined) { + if (calculationsProcedure.initialization.code !== undefined) { + eval(calculationsProcedure.initialization.code.code) + } } - return dependecyArray + /* This is Initialization Code */ + if (calculationsProcedure.loop !== undefined) { + if (calculationsProcedure.loop.code !== undefined) { + for (let i = 0; i < jsonArray.length; i++) { + + let indicator = {} + indicator[variableName] = jsonArray[i] + + /* This is Loop Code */ + eval(calculationsProcedure.loop.code.code) + + /* For each calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated === true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + let newValue = eval(property.formula.code) + let currentRecord = indicator[variableName] + currentRecord[property.code.codeName] = newValue + } + } + } + } + + /* Adding the new element to the resulting array */ + resultingArray.push(indicator.bollingerBand); + } + } + } + return resultingArray } - - function addCalculatedProperties(dependencyArray, variableName, timePeriod) { let dependecyArray = [] diff --git a/IndicatorBotMarket.js b/IndicatorBotMarket.js index 97e70f03e..57fe4073e 100644 --- a/IndicatorBotMarket.js +++ b/IndicatorBotMarket.js @@ -51,19 +51,54 @@ let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies for (let i = 0; i < dataDependencies.length; i++) { + let dataset = dataDependencies[i].referenceParent + let dataFile = dataFiles[i] + let jsonData // Datafile converted into Json objects + let inputData // Includes calculated properties + let variableName // name of the variable for this product + let recordDefinition + /* For each dataset in our data dependencies, we should have da dataFile containing the records needed as an imput for this process. What we need to do first is transform those records into JSON objects that can be used by user-defined formulas. + The first step does that but with the not calculated properties, the second step adds the calculated properties. */ + + /* Basic validations to see if we have everything we need. */ + if (dataset.parentNode.code.variableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } if (dataset.parentNode.record === undefined) { logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataset.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - dataset.inputData = commons.jsonifyDataFile(dataFiles[i], dataset.parentNode.record) + if (dataset.parentNode.calculations === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Calculations Procedure. Product Definition = " + JSON.stringify(dataset.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataset.parentNode.dataBuilding === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(dataset.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + recordDefinition = dataset.parentNode.record + variableName = dataset.parentNode.code.variableName + /* Transform the raw data into JSON objects */ + jsonData = commons.jsonifyDataFile(dataFile, recordDefinition) + /* Add the calculated properties */ + if (dataset.parentNode.calculations !== undefined) { + inputData = commons.calculationsProcedure(jsonData, recordDefinition, dataset.parentNode.calculations, variableName, timePeriod) + } } + /* During the next phase, we need to generate the data of the different products this process produces an output */ + diff --git a/server.js b/server.js index a7ca3d339..7713e3b90 100644 --- a/server.js +++ b/server.js @@ -72,15 +72,7 @@ if (global.TASK_NODE !== undefined) { } else { // I use this section to debug in standalone mode. - let argument - argument = ' {"type":"Task","name":"Generates Bollinger Channels & Sub-Channels","bot":{"type":"Indicator Bot Instance","name":"Paula","code":{"team":"AAMasters","bot":"AAPaula","repo":"AAPaula-Indicator-Bot"},"processes":[{"type":"Process Instance","subType":"Indicator Process Instance","name":"Market","code":{"process":"Multi-Period-Market"},"id":"6ddd81e2-d6db-4814-9c6c-96538a74824c","referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","processOutput":{"type":"Process Output","name":"New Process Output","outputDatasets":[{"type":"Output Dataset","name":"Bollinger Channels","id":"d804e799-7c12-42f5-8de2-52a3a7d5daa4","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Channels","code":{"codeName":"Bollinger-Channels","displayName":"Bollinger-Channels","description":"Two sets of files per period that contains Bollinger-Bands-Channels-Patterns.","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":{"codeName":"begin","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"caf8122c-3a07-4263-a56f-ffb05df281d6"},"id":"06edb099-ff64-4528-b8c6-599154bc2ddf"},{"type":"Record Property","name":"End","code":{"codeName":"end","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"c8ec0868-8413-468c-88d5-3e43169395e0"},"id":"94656d1f-508a-4e94-a8d2-013690aac596"},{"type":"Record Property","name":"Direction","code":{"codeName":"direction","isNumeric":false},"formula":{"type":"Formula","code":"// Write your formula here","id":"e913afe9-b610-4777-bc9e-6349d436a90a"},"id":"edc0f697-f5e2-446f-bae8-8db7adf91d23"},{"type":"Record Property","name":"Period","code":{"codeName":"period","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"83fce657-a0bb-41ee-80d1-9e2ab980c472"},"id":"eb733f85-6cf5-485e-9bd3-2bf78733c492"},{"type":"Record Property","name":"First Moving Average","code":{"codeName":"firstMovingAverage","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"52490460-35e3-469a-824f-5b139571d4a0"},"id":"a67a15ee-6f7f-48bd-9d55-82c57efc993b"},{"type":"Record Property","name":"Last Moving Average","code":{"codeName":"lastMovingAverage","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"41edc1ef-293a-4ceb-bf67-8042e8fdc4eb"},"id":"0f05bffe-d668-4d25-ab67-d55a4a7c8f5d"},{"type":"Record Property","name":"First Deviation","code":{"codeName":"firstDeviation","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"5af02103-9bc9-4e6c-9684-5a612adfa495"},"id":"eae84509-b689-4a76-9277-eed72af491dd"},{"type":"Record Property","name":"Last Deviation","code":{"codeName":"lastDeviation","isNumeric":true},"formula":{"type":"Formula","code":"// Write your formula here","id":"bc82d292-e71a-4367-9b07-a0e3982ad60d"},"id":"d5c32d1f-9e3c-48e8-8025-a17d57c0812e"}],"id":"6c3e68b0-b781-4c53-9ff7-67c3f747ac8a"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"1f111f72-624f-4166-9f99-ab8d785d5267"},"id":"610cd062-ab88-4892-b711-0fb430305c61"}},{"type":"Output Dataset","name":"Bollinger Standard Channels","id":"e524685d-8da0-41f6-839d-9a4100e6e3fe","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Channels","code":{"codeName":"Bollinger-Standard-Channels","displayName":"Bollinger-Standard-Channels","description":"","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[],"id":"9106250d-b042-4e96-9121-c760b4c1eab2"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"94bc3958-cc85-43a1-a178-103e84f42910"},"id":"22c1f2c6-3838-4657-add8-2e01d7056917"}},{"type":"Output Dataset","name":"Bollinger Sub-Channels","id":"eb0e7947-95e4-4f4a-87ea-1fb430b8b503","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Sub-Channels","code":{"codeName":"Bollinger-Sub-Channels","displayName":"Bollinger-Sub-Channels","description":"Bollinger Sub Channels with slope.","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[],"id":"5a780d7f-70df-4e86-8192-eacccafb5ca8"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"080c87fc-4546-4482-ab11-180a33f6d9ab"},"id":"869f615e-0cc0-44fd-8648-4a951c23a410"}},{"type":"Output Dataset","name":"Bollinger Standard Sub-Channels","id":"dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1","referenceParent":{"type":"Dataset Definition","name":"Market","code":{"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":"AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period","fileName":"@AssetA_@AssetB.json"},"parentNode":{"type":"Product Definition","name":"Bollinger Standard Sub-Channels","code":{"codeName":"Bollinger-Standard-Sub-Channels","displayName":"Bollinger-Standard-Sub-Channels","description":"Bollinger Standard Sub Channels with slope.","shareWith":"Public"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[],"id":"cdbe5e0f-9292-4d3d-b98a-d441e6565b76"},"parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"},"id":"79ce9c05-8a4c-4b58-850f-288037acc702"},"id":"f8d44a4d-3771-4a71-97ca-dc604ff565d3"}}],"id":"dd39a54a-3714-446a-9a41-dfd83557664c"},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","statusDependencies":[{"type":"Status Dependency","name":"Self Reference","code":{"mainUtility":"Self Reference"},"id":"ae627a69-4089-432d-b379-1b12ce4bd75e","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"}},{"type":"Status Dependency","name":"Market Ending Point","code":{"mainUtility":"Market Ending Point"},"id":"4a603114-d464-46db-90a1-62304274117a","referenceParent":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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},"id":"941a0543-0cc0-4a5d-a782-728d2835c100","parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"}},"id":"4f8197fa-2627-46df-bd36-7d30fdb12d8c"}}],"dataDependencies":[{"type":"Data Dependency","name":"Chris Bollinger Bands","id":"ea27be54-93a3-48e3-8c52-72bc397b6c66","referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market"},"parentNode":{"type":"Product Definition","name":"Bollinger Bands","code":{"codeName":"Bollinger-Bands"},"datasets":[],"record":{"type":"Record Definition","name":"New Record Definition","properties":[{"type":"Record Property","name":"Begin","code":{"codeName":"begin","isNumeric":true},"id":"1e7186ee-c481-401d-827f-a30ee7434d92"},{"type":"Record Property","name":"End","code":{"codeName":"end","isNumeric":true},"id":"15f90f2e-80f0-4575-94d8-6d8957eceee1"},{"type":"Record Property","name":"Moving Average","code":{"codeName":"movingAverage","isNumeric":true},"id":"9786a9cd-1eff-4b5c-ae7c-1dde51659f82"},{"type":"Record Property","name":"Standard Deviation","code":{"codeName":"standardDeviation","isNumeric":true},"id":"e92d7855-bb18-494e-ab37-a895bbcb8287"},{"type":"Record Property","name":"Deviation","code":{"codeName":"deviation","isNumeric":true},"id":"03043530-caa1-416e-8303-840b732e516e"}],"id":"b5a5ec97-7246-42bd-97e4-28fb4a148b2c"},"parentNode":{"type":"Indicator Bot","name":"Chris","code":{"codeName":"AAChris"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23"},"id":"29088877-8d63-4217-8d34-8742afc262f4"},"id":"f5b2a7d8-fc32-44a3-a002-9b0fc30a9719"}}],"id":"8b509294-6028-40e2-88a8-655a34245ed8"},"statusReport":{"type":"Status Report","name":"New Status Report","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}},"id":"28be432d-80ca-46b3-859a-55541e24b2b4","parentNode":{"type":"Indicator Bot","name":"Paula","code":{"codeName":"AAPaula"},"processes":[],"products":[],"parentNode":{"type":"Team","name":"Masters","code":{"codeName":"AAMasters"},"sensorBots":[],"indicatorBots":[],"tradingBots":[],"plotters":[],"id":"35be7b1c-a0f7-4b5f-9ee1-8fb51595e959"},"id":"bd0f325d-6f98-468b-8b7b-99a42a175d80"}},"id":"af8aded9-002a-475a-88f5-d29573740969"},"executionStartedEvent":{"type":"Execution Started Event","name":"New Execution Started Event","id":"f4720e04-5def-4c0b-be42-33e50ac6a6b7","referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100","code":{"codeName":"Multi-Period-Market","description":"Produces a single file for each time period 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}}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"671530dc-5ba5-4c8d-bc23-f228e22c56b3","parentNode":{"type":"Process Definition","name":"Multi-Period-Market","id":"28be432d-80ca-46b3-859a-55541e24b2b4","code":{"codeName":"Multi-Period-Market","description":"Produces for each market two files with bollinger channels respectively.","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,"framework":{"name":"Multi-Period-Market","startDate":{},"endDate":{}}}}},"calculations":{"type":"Calculations Procedure","name":"New Calculations Procedure","id":"ba2de283-a059-44d0-b229-1c7988f48f51"},"dataBuilding":{"type":"Data Building Procedure","name":"New Data Building Procedure","initialization":{"type":"Procedure Initialization","name":"New Procedure Initialization","code":{"type":"Code","code":"// Write your code here","id":"26413b25-24b7-47d1-b73d-12cef3198657"},"id":"13b23649-2695-48cb-aa9a-efe8acb7ab2c"},"loop":{"type":"Procedure Loop","name":"New Procedure Loop","code":{"type":"Code","code":"// Write your code here","id":"f0a3227a-094b-4aae-babb-d78f9e9b38ed"},"id":"b7a118de-5ebe-4efb-9b10-3b69680063a3"},"id":"859a0c08-b0fd-4d42-851a-21e898d42ec8"}}}],"id":"27564838-24b1-4814-af8c-ec65accb05d1"},"id":"aa0f2533-460c-464f-ad77-6ae4cadd1178"}' - - - try { - global.TASK_NODE = JSON.parse(argument) - } catch (err) { - console.log(err.stack) - } + global.TASK_NODE = { "type": "Task", "name": "Generates Bollinger Channels & Sub-Channels", "bot": { "type": "Indicator Bot Instance", "name": "Paula", "code": { "team": "AAMasters", "bot": "AAPaula", "repo": "AAPaula-Indicator-Bot" }, "processes": [{ "type": "Process Instance", "subType": "Indicator Process Instance", "name": "Market", "code": { "process": "Multi-Period-Market" }, "id": "6ddd81e2-d6db-4814-9c6c-96538a74824c", "referenceParent": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } }, "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "processOutput": { "type": "Process Output", "name": "New Process Output", "outputDatasets": [{ "type": "Output Dataset", "name": "Bollinger Channels", "id": "d804e799-7c12-42f5-8de2-52a3a7d5daa4", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Channels", "code": { "codeName": "Bollinger-Channels", "displayName": "Bollinger-Channels", "description": "Two sets of files per period that contains Bollinger-Bands-Channels-Patterns.", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [{ "type": "Record Property", "name": "Begin", "code": { "codeName": "begin", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "caf8122c-3a07-4263-a56f-ffb05df281d6" }, "id": "06edb099-ff64-4528-b8c6-599154bc2ddf" }, { "type": "Record Property", "name": "End", "code": { "codeName": "end", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "c8ec0868-8413-468c-88d5-3e43169395e0" }, "id": "94656d1f-508a-4e94-a8d2-013690aac596" }, { "type": "Record Property", "name": "Direction", "code": { "codeName": "direction", "isNumeric": false }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "e913afe9-b610-4777-bc9e-6349d436a90a" }, "id": "edc0f697-f5e2-446f-bae8-8db7adf91d23" }, { "type": "Record Property", "name": "Period", "code": { "codeName": "period", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "83fce657-a0bb-41ee-80d1-9e2ab980c472" }, "id": "eb733f85-6cf5-485e-9bd3-2bf78733c492" }, { "type": "Record Property", "name": "First Moving Average", "code": { "codeName": "firstMovingAverage", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "52490460-35e3-469a-824f-5b139571d4a0" }, "id": "a67a15ee-6f7f-48bd-9d55-82c57efc993b" }, { "type": "Record Property", "name": "Last Moving Average", "code": { "codeName": "lastMovingAverage", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "41edc1ef-293a-4ceb-bf67-8042e8fdc4eb" }, "id": "0f05bffe-d668-4d25-ab67-d55a4a7c8f5d" }, { "type": "Record Property", "name": "First Deviation", "code": { "codeName": "firstDeviation", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "5af02103-9bc9-4e6c-9684-5a612adfa495" }, "id": "eae84509-b689-4a76-9277-eed72af491dd" }, { "type": "Record Property", "name": "Last Deviation", "code": { "codeName": "lastDeviation", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "bc82d292-e71a-4367-9b07-a0e3982ad60d" }, "id": "d5c32d1f-9e3c-48e8-8025-a17d57c0812e" }], "id": "6c3e68b0-b781-4c53-9ff7-67c3f747ac8a" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "1f111f72-624f-4166-9f99-ab8d785d5267" }, "id": "610cd062-ab88-4892-b711-0fb430305c61" } }, { "type": "Output Dataset", "name": "Bollinger Standard Channels", "id": "e524685d-8da0-41f6-839d-9a4100e6e3fe", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Standard Channels", "code": { "codeName": "Bollinger-Standard-Channels", "displayName": "Bollinger-Standard-Channels", "description": "", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [], "id": "9106250d-b042-4e96-9121-c760b4c1eab2" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "94bc3958-cc85-43a1-a178-103e84f42910" }, "id": "22c1f2c6-3838-4657-add8-2e01d7056917" } }, { "type": "Output Dataset", "name": "Bollinger Sub-Channels", "id": "eb0e7947-95e4-4f4a-87ea-1fb430b8b503", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Sub-Channels", "code": { "codeName": "Bollinger-Sub-Channels", "displayName": "Bollinger-Sub-Channels", "description": "Bollinger Sub Channels with slope.", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [], "id": "5a780d7f-70df-4e86-8192-eacccafb5ca8" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "080c87fc-4546-4482-ab11-180a33f6d9ab" }, "id": "869f615e-0cc0-44fd-8648-4a951c23a410" } }, { "type": "Output Dataset", "name": "Bollinger Standard Sub-Channels", "id": "dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Standard Sub-Channels", "code": { "codeName": "Bollinger-Standard-Sub-Channels", "displayName": "Bollinger-Standard-Sub-Channels", "description": "Bollinger Standard Sub Channels with slope.", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [], "id": "cdbe5e0f-9292-4d3d-b98a-d441e6565b76" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "79ce9c05-8a4c-4b58-850f-288037acc702" }, "id": "f8d44a4d-3771-4a71-97ca-dc604ff565d3" } }], "id": "dd39a54a-3714-446a-9a41-dfd83557664c" }, "processDependencies": { "type": "Process Dependencies", "name": "New Process Dependencies", "statusDependencies": [{ "type": "Status Dependency", "name": "Self Reference", "code": { "mainUtility": "Self Reference" }, "id": "ae627a69-4089-432d-b379-1b12ce4bd75e", "referenceParent": { "type": "Status Report", "name": "New Status Report", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } }, "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" } }, "id": "af8aded9-002a-475a-88f5-d29573740969" } }, { "type": "Status Dependency", "name": "Market Ending Point", "code": { "mainUtility": "Market Ending Point" }, "id": "4a603114-d464-46db-90a1-62304274117a", "referenceParent": { "type": "Status Report", "name": "New Status Report", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces a single file for each time period 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 }, "id": "941a0543-0cc0-4a5d-a782-728d2835c100", "parentNode": { "type": "Indicator Bot", "name": "Chris", "code": { "codeName": "AAChris" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23" } }, "id": "4f8197fa-2627-46df-bd36-7d30fdb12d8c" } }], "dataDependencies": [{ "type": "Data Dependency", "name": "Chris Bollinger Bands", "id": "ea27be54-93a3-48e3-8c52-72bc397b6c66", "referenceParent": { "type": "Dataset Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Bands", "code": { "codeName": "Bollinger-Bands", "variableName": "bollingerBand" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [{ "type": "Record Property", "name": "Begin", "code": { "codeName": "begin", "isNumeric": true }, "id": "1e7186ee-c481-401d-827f-a30ee7434d92" }, { "type": "Record Property", "name": "End", "code": { "codeName": "end", "isNumeric": true }, "id": "15f90f2e-80f0-4575-94d8-6d8957eceee1" }, { "type": "Record Property", "name": "Moving Average", "code": { "codeName": "movingAverage", "isNumeric": true }, "id": "9786a9cd-1eff-4b5c-ae7c-1dde51659f82" }, { "type": "Record Property", "name": "Standard Deviation", "code": { "codeName": "standardDeviation", "isNumeric": true }, "id": "e92d7855-bb18-494e-ab37-a895bbcb8287" }, { "type": "Record Property", "name": "Deviation", "code": { "codeName": "deviation", "isNumeric": true }, "id": "03043530-caa1-416e-8303-840b732e516e" }, { "type": "Record Property", "name": "Direction", "code": { "codeName": "direction", "isNumeric": false, "isCalculated": true }, "formula": { "type": "Formula", "code": "variable.direction", "id": "f72926d9-c869-4b7c-ba1e-b36d134e2bc6" }, "id": "856f24c9-f5f1-491b-98e7-6e1d3c5c5874" }, { "type": "Record Property", "name": "Slope", "code": { "codeName": "slope", "isNumeric": false, "isCalculated": true }, "formula": { "type": "Formula", "code": "variable.slope", "id": "fac935fe-b448-4b34-aed7-b35f70808911" }, "id": "4c027b0c-da8a-4146-a51a-d0a7a126150b" }], "id": "b5a5ec97-7246-42bd-97e4-28fb4a148b2c" }, "calculations": { "type": "Calculations Procedure", "name": "New Calculations Procedure", "initialization": { "type": "Procedure Initialization", "name": "New Procedure Initialization", "code": { "type": "Code", "code": "variable.lastMovingAverage = 0\nvariable.SIDE_TOLERANCE = 0.5 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\nvariable.SMALL_SLOPE = 1.0 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\nvariable.MEDIUM_SLOPE = 2.0 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\nvariable.HIGH_SLOPE = 4.0 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\n", "id": "f00ac417-cd99-493c-b959-35eddd6fe4f3" }, "id": "d144ab2f-5b4e-4153-806a-d471e9f281dd" }, "loop": { "type": "Procedure Loop", "name": "New Procedure Loop", "code": { "type": "Code", "code": "\nif (variable.lastMovingAverage > indicator.bollingerBand.movingAverage) { variable.direction = \"Down\" }\nif (variable.lastMovingAverage < indicator.bollingerBand.movingAverage) { variable.direction = \"Up\" }\nif (variable.lastMovingAverage === indicator.bollingerBand.movingAverage) { variable.direction = \"Side\" }\n\nvariable.delta = Math.abs(indicator.bollingerBand.movingAverage - variable.lastMovingAverage)\n\nvariable.slope = \"Extreme\"\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.HIGH_SLOPE / 100) { variable.slope = \"Steep\" }\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.MEDIUM_SLOPE / 100) { variable.slope = \"Medium\" }\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.SMALL_SLOPE / 100) { variable.slope = \"Gentle\" }\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.SIDE_TOLERANCE / 100) { variable.slope = \"Side\" }\n\nvariable.lastMovingAverage = indicator.bollingerBand.movingAverage", "id": "14fc0c9c-cd10-4931-95b5-4bc7d549942a" }, "id": "8a669548-845b-4d80-ae96-29f68e9c9ac7" }, "id": "d72c7cb2-8886-4101-a83a-f9788b8400a0" }, "dataBuilding": { "type": "Data Building Procedure", "name": "New Data Building Procedure", "id": "139484b3-3a3f-4268-801e-a8c441746c7a" }, "parentNode": { "type": "Indicator Bot", "name": "Chris", "code": { "codeName": "AAChris" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23" }, "id": "29088877-8d63-4217-8d34-8742afc262f4" }, "id": "f5b2a7d8-fc32-44a3-a002-9b0fc30a9719" } }], "id": "8b509294-6028-40e2-88a8-655a34245ed8" }, "statusReport": { "type": "Status Report", "name": "New Status Report", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } }, "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" } }, "id": "af8aded9-002a-475a-88f5-d29573740969" }, "executionStartedEvent": { "type": "Execution Started Event", "name": "New Execution Started Event", "id": "f4720e04-5def-4c0b-be42-33e50ac6a6b7", "referenceParent": { "type": "Execution Finished Event", "name": "New Execution Finished Event", "id": "4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "id": "941a0543-0cc0-4a5d-a782-728d2835c100", "code": { "codeName": "Multi-Period-Market", "description": "Produces a single file for each time period 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 } } } }, "executionFinishedEvent": { "type": "Execution Finished Event", "name": "New Execution Finished Event", "id": "671530dc-5ba5-4c8d-bc23-f228e22c56b3", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } } } } } }], "id": "27564838-24b1-4814-af8c-ec65accb05d1" }, "id": "aa0f2533-460c-464f-ad77-6ae4cadd1178" } } require('dotenv').config(); From 5e91af82d49878e5f7f2ec7f5508b6eacfc9f34d Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 13 Nov 2019 18:23:22 +0100 Subject: [PATCH 15/38] Build Data Function Implemented --- IndicatorBotCommons.js | 530 ++++++++--------------------------------- IndicatorBotMarket.js | 77 +++++- server.js | 89 +++---- 3 files changed, 217 insertions(+), 479 deletions(-) diff --git a/IndicatorBotCommons.js b/IndicatorBotCommons.js index d1ae58093..959c6deda 100644 --- a/IndicatorBotCommons.js +++ b/IndicatorBotCommons.js @@ -10,10 +10,7 @@ let thisObject = { jsonifyDataFile: jsonifyDataFile, calculationsProcedure: calculationsProcedure, - buildStandardChannels: buildStandardChannels, - buildSubChannels: buildSubChannels, - buildStandardSubChannels: buildStandardSubChannels, - buildRecords: buildRecords + dataBuildingProcedure: dataBuildingProcedure }; return thisObject; @@ -54,26 +51,39 @@ timePeriod: timePeriod, ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS } - let variable = {} // This is the structure where the user will define its own variables that will be shared amond different code blocks and formulas. - let resultingArray = [] + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] /* This is Initialization Code */ if (calculationsProcedure.initialization !== undefined) { if (calculationsProcedure.initialization.code !== undefined) { - eval(calculationsProcedure.initialization.code.code) + try { + eval(calculationsProcedure.initialization.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = " + calculationsProcedure.initialization.code.code); + throw("Error Executing User Code.") + } } } /* This is Initialization Code */ if (calculationsProcedure.loop !== undefined) { if (calculationsProcedure.loop.code !== undefined) { - for (let i = 0; i < jsonArray.length; i++) { + for (let index = 0; index < jsonArray.length; index++) { - let indicator = {} - indicator[variableName] = jsonArray[i] + let product = {} + product[variableName] = jsonArray[index] /* This is Loop Code */ - eval(calculationsProcedure.loop.code.code) + try { + eval(calculationsProcedure.loop.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = " + calculationsProcedure.loop.code.code); + throw ("Error Executing User Code.") + } /* For each calculated property we apply its formula*/ for (let j = 0; j < recordDefinition.properties.length; j++) { @@ -81,8 +91,16 @@ if (property.code.isCalculated === true) { if (property.formula !== undefined) { if (property.formula.code !== undefined) { - let newValue = eval(property.formula.code) - let currentRecord = indicator[variableName] + let newValue + try { + newvalue = eval(property.formula.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + let currentRecord = product[variableName] currentRecord[property.code.codeName] = newValue } } @@ -90,440 +108,96 @@ } /* Adding the new element to the resulting array */ - resultingArray.push(indicator.bollingerBand); + results.push(product[variableName]); } } } - return resultingArray + return results } - function addCalculatedProperties(dependencyArray, variableName, timePeriod) { + function dataBuildingProcedure(products, mainDependency, recordDefinition, dataBuildingProcedure, variableName, timePeriod) { - let dependecyArray = [] + /* + This function has as an input the products object, with all the information + of all products calculated so far by the process. Based on that information + the function will evaluate user supplied code and formulas in order to build + a new set of information. + */ + + let system = { // These are the available system variables to be used in User Code and Formulas + timePeriod: timePeriod, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] /* This is Initialization Code */ - let lastMovingAverage = 0; - const SIDE_TOLERANCE = 0.5 * timePeriod / ONE_DAY_IN_MILISECONDS; - const SMALL_SLOPE = 1.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - const MEDIUM_SLOPE = 2.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - const HIGH_SLOPE = 4.0 * timePeriod / ONE_DAY_IN_MILISECONDS; + if (dataBuildingProcedure.initialization !== undefined) { + if (dataBuildingProcedure.initialization.code !== undefined) { + try { + eval(dataBuildingProcedure.initialization.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Code = " + dataBuildingProcedure.initialization.code.code); + throw ("Error Executing User Code.") + } + } + } + /* This is Initialization Code */ + if (dataBuildingProcedure.loop !== undefined) { + if (dataBuildingProcedure.loop.code !== undefined) { + for (let index = 0; index < mainDependency.length; index++) { - for (let i = 0; i < dataFile.length; i++) { + let record = { + previous: mainDependency[index-1], + current: mainDependency[index], + next: mainDependency[index+1] + } - let indicator = {} - indicator[variableName] = dataFile[i] + let product = {} + product[variableName] = {} - /* This is Add Properties Code */ - if (lastMovingAverage > indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Down'; } - if (lastMovingAverage < indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Up'; } - if (lastMovingAverage === indicator.bollingerBand.movingAverage) { indicator.bollingerBand.direction = 'Side'; } + /* This is Loop Code */ + try { + eval(dataBuildingProcedure.loop.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Code = " + dataBuildingProcedure.loop.code.code); + throw ("Error Executing User Code.") + } - let delta = Math.abs(indicator.bollingerBand.movingAverage - lastMovingAverage); - - indicator.bollingerBand.slope = 'Extreme'; - if (delta < indicator.bollingerBand.movingAverage * HIGH_SLOPE / 100) { indicator.bollingerBand.slope = 'Steep'; } - if (delta < indicator.bollingerBand.movingAverage * MEDIUM_SLOPE / 100) { indicator.bollingerBand.slope = 'Medium'; } - if (delta < indicator.bollingerBand.movingAverage * SMALL_SLOPE / 100) { indicator.bollingerBand.slope = 'Gentle'; } - if (delta < indicator.bollingerBand.movingAverage * SIDE_TOLERANCE / 100) { indicator.bollingerBand.slope = 'Side'; } - - lastMovingAverage = indicator.bollingerBand.movingAverage; - /* This is Add Properties Code */ - - dependecyArray.push(indicator.bollingerBand); - } - - return dependecyArray - } - - function buildRecords(dataDependency, recordDefinition) { - let records = [] - - /* Initialization Code */ - let period = 1 - /* Initialization Code */ - - for (let i = 0; i < dataDependency.bollinerBands.length; i++) { - - /* Calculations Code*/ - if (i === 0) {continue} - let currentBand = dataDependency.bollinerBands[i]; - let previousBand = dataDependency.bollinerBands[i - 1]; - - if (currentBand.direction === previousBand.direction) { - period++ - } else { - period = 1 - } - /* Calculations Code*/ - - let record = { - begin: currentBand.begin, /* Property Formula Code */ - end: currentBand.end, /* Property Formula Code */ - direction: currentBand.direction, /* Property Formula Code */ - period: period /* Property Formula Code */ - }; - records.push(record) - } - return records - } - - - function buildBandsArray(dataFile, bands, timePeriod, callBackFunction) { - - try { - - let lastMovingAverage = 0; - const SIDE_TOLERANCE = 0.5 * timePeriod / ONE_DAY_IN_MILISECONDS; - const SMALL_SLOPE = 1.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - const MEDIUM_SLOPE = 2.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - const HIGH_SLOPE = 4.0 * timePeriod / ONE_DAY_IN_MILISECONDS; - - for (let i = 0; i < dataFile.length; i++) { - - let band = { - begin: undefined, - end: undefined, - movingAverage: 10000000000000, - standardDeviation: 0, - deviation: undefined - }; - - band.begin = dataFile[i][0]; - band.end = dataFile[i][1]; - - band.movingAverage = dataFile[i][2]; - band.standardDeviation = dataFile[i][3]; - band.deviation = dataFile[i][4]; - - if (lastMovingAverage > band.movingAverage) { band.direction = 'Down'; } - if (lastMovingAverage < band.movingAverage) { band.direction = 'Up'; } - if (lastMovingAverage === band.movingAverage) { band.direction = 'Side'; } - - let delta = Math.abs(band.movingAverage - lastMovingAverage); - - band.slope = 'Extreme'; - if (delta < band.movingAverage * HIGH_SLOPE / 100) { band.slope = 'Steep'; } - if (delta < band.movingAverage * MEDIUM_SLOPE / 100) { band.slope = 'Medium'; } - if (delta < band.movingAverage * SMALL_SLOPE / 100) { band.slope = 'Gentle'; } - if (delta < band.movingAverage * SIDE_TOLERANCE / 100) { band.slope = 'Side'; } - - bands.push(band); - - lastMovingAverage = band.movingAverage; - } - } - catch (err) { - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function buildChannels(bands, channels, callBackFunction) { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildChannels -> Entering function."); } - - try { - - let channel; - - for (let i = 0; i < bands.length; i++) { - - let currentBand = bands[i]; - let nextBand; - - if (i < bands.length - 1) { - nextBand = bands[i + 1]; - - if ( - currentBand.direction === nextBand.direction) { - - if (channel === undefined) { - - channel = { - begin: undefined, - end: undefined, - direction: undefined, - period: 0, - firstMovingAverage: 0, - lastMovingAverage: 0, - firstDeviation: 0, - lastDeviation: 0 - }; - - channel.direction = currentBand.direction; - channel.period = 2; - - channel.begin = currentBand.begin; - channel.end = nextBand.end; - - channel.firstMovingAverage = currentBand.movingAverage; - channel.lastMovingAverage = nextBand.movingAverage; - - channel.firstDeviation = currentBand.deviation; - channel.lastDeviation = nextBand.deviation; - - } else { - - channel.period++; - channel.end = nextBand.end; - channel.lastMovingAverage = nextBand.movingAverage; - channel.lastDeviation = nextBand.deviation; - - } - } else { - - if (channel !== undefined) { - channels.push(channel); - channel = undefined; - } else { - /* The channel has only one period */ - - channel = {}; - - channel.direction = currentBand.direction; - channel.period = 1; - - channel.begin = currentBand.begin; - channel.end = currentBand.end; - - channel.firstMovingAverage = currentBand.movingAverage; - channel.lastMovingAverage = currentBand.movingAverage; - - channel.firstDeviation = currentBand.deviation; - channel.lastDeviation = currentBand.deviation; - - channels.push(channel); - channel = undefined; + /* For each non-calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + let newValue + try { + newvalue = eval(property.formula.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue + } + } } } - } else { - if (channel !== undefined) { - channel.period++; - channel.end = currentBand.end; - channel.lastMovingAverage = currentBand.movingAverage; - channel.lastDeviation = currentBand.deviation; - channels.push(channel); - channel = undefined; - } else { - /* The channel has only one period */ - channel = {}; - - channel.direction = currentBand.direction; - channel.period = 1; - - channel.begin = currentBand.begin; - channel.end = currentBand.end; - - channel.firstMovingAverage = currentBand.movingAverage; - channel.lastMovingAverage = currentBand.movingAverage; - - channel.firstDeviation = currentBand.deviation; - channel.lastDeviation = currentBand.deviation; - - channels.push(channel); - channel = undefined; + /* If within the code the new data structure was not yet pushed to the results array, we do it */ + if (dataBuildingProcedure.loop.code.code.indexOf('results.push') < 0) { + /* Adding the new element to the resulting array */ + results.push(product[variableName]); } } } } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] buildChannels -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function buildStandardChannels(bands, standardChannels, callBackFunction) { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildStandardChannels -> Entering function."); } - - try { - - let period = 1 - - for (let i = 1; i < bands.length; i++) { - - let channel; - let currentBand = bands[i]; - let previousBand = bands[i - 1]; - - if (currentBand.direction === previousBand.direction) { - period++ - } else { - period = 1 - } - - channel = { - begin: currentBand.begin, - end: currentBand.end, - direction: currentBand.direction, - period: period - }; - - standardChannels.push(channel) - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] buildStandardChannels -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function buildSubChannels(bands, subChannels, callBackFunction) { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildSubChannels -> Entering function."); } - - try { - - let channel; - - for (let i = 0; i < bands.length; i++) { - - let currentBand = bands[i]; - let nextBand; - - if (i < bands.length - 1) { - nextBand = bands[i + 1]; - - if ( - currentBand.direction === nextBand.direction && - currentBand.slope === nextBand.slope) { - - if (channel === undefined) { - - channel = { - begin: undefined, - end: undefined, - direction: undefined, - slope: undefined, - period: 0, - firstMovingAverage: 0, - lastMovingAverage: 0, - firstDeviation: 0, - lastDeviation: 0 - }; - - channel.direction = currentBand.direction; - channel.slope = currentBand.slope; - channel.period = 2; - - channel.begin = currentBand.begin; - channel.end = nextBand.end; - - channel.firstMovingAverage = currentBand.movingAverage; - channel.lastMovingAverage = nextBand.movingAverage; - - channel.firstDeviation = currentBand.deviation; - channel.lastDeviation = nextBand.deviation; - - } else { - - channel.period++; - channel.end = nextBand.end; - channel.lastMovingAverage = nextBand.movingAverage; - channel.lastDeviation = nextBand.deviation; - - } - } else { - - if (channel !== undefined) { - subChannels.push(channel); - channel = undefined; - } else { - /* The channel has only one period */ - - channel = {}; - - channel.direction = currentBand.direction; - channel.slope = currentBand.slope; - channel.period = 1; - - channel.begin = currentBand.begin; - channel.end = currentBand.end; - - channel.firstMovingAverage = currentBand.movingAverage; - channel.lastMovingAverage = currentBand.movingAverage; - - channel.firstDeviation = currentBand.deviation; - channel.lastDeviation = currentBand.deviation; - - subChannels.push(channel); - channel = undefined; - } - } - - } else { - if (channel !== undefined) { - channel.period++; - channel.end = currentBand.end; - channel.lastMovingAverage = currentBand.movingAverage; - channel.lastDeviation = currentBand.deviation; - subChannels.push(channel); - channel = undefined; - } else { - /* The channel has only one period */ - - channel = {}; - - channel.direction = currentBand.direction; - channel.slope = currentBand.slope; - channel.period = 1; - - channel.begin = currentBand.begin; - channel.end = currentBand.end; - - channel.firstMovingAverage = currentBand.movingAverage; - channel.lastMovingAverage = currentBand.movingAverage; - - channel.firstDeviation = currentBand.deviation; - channel.lastDeviation = currentBand.deviation; - - subChannels.push(channel); - channel = undefined; - } - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] buildSubChannels -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function buildStandardSubChannels(bands, standardSubChannels, callBackFunction) { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] buildStandardSubChannels -> Entering function."); } - - try { - - let period = 1 - - for (let i = 1; i < bands.length; i++) { - - let channel; - let currentBand = bands[i]; - let previousBand = bands[i - 1]; - - if (currentBand.direction === previousBand.direction && currentBand.slope === previousBand.slope) { - period++ - } else { - period = 1 - } - - channel = { - begin: currentBand.begin, - end: currentBand.end, - direction: currentBand.direction, - slope: currentBand.slope, - period: period - }; - - standardSubChannels.push(channel) - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] buildStandardSubChannels -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } + return results } }; diff --git a/IndicatorBotMarket.js b/IndicatorBotMarket.js index 57fe4073e..1f6c44456 100644 --- a/IndicatorBotMarket.js +++ b/IndicatorBotMarket.js @@ -48,6 +48,8 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } let market = global.MARKET; + let products = {} + let mainDependency let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies for (let i = 0; i < dataDependencies.length; i++) { @@ -56,7 +58,7 @@ let dataFile = dataFiles[i] let jsonData // Datafile converted into Json objects let inputData // Includes calculated properties - let variableName // name of the variable for this product + let singularVariableName // name of the variable for this product let recordDefinition /* @@ -66,8 +68,13 @@ */ /* Basic validations to see if we have everything we need. */ - if (dataset.parentNode.code.variableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (dataset.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataset.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } @@ -76,8 +83,52 @@ callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataset.parentNode.calculations === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Calculations Procedure. Product Definition = " + JSON.stringify(dataset.parentNode)); + + recordDefinition = dataset.parentNode.record + singularVariableName = dataset.parentNode.code.singularVariableName + /* Transform the raw data into JSON objects */ + jsonData = commons.jsonifyDataFile(dataFile, recordDefinition) + /* Add the calculated properties */ + if (dataset.parentNode.calculations !== undefined) { + inputData = commons.calculationsProcedure(jsonData, recordDefinition, dataset.parentNode.calculations, singularVariableName, timePeriod) + } else { + inputData = jsonData + } + products[dataset.parentNode.code.pluralVariableName] = inputData + + /* The main dependency is defined as the first dependency processed. */ + if (mainDependency === undefined) { + mainDependency = inputData + } + } + + /* During the next phase, we need to generate the data of the different products this process produces an output */ + let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets + for (let i = 0; i < outputDatasets.length; i++) { + + let dataset = outputDatasets[i].referenceParent + let jsonData // Just build data as Json objects + let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure + let singularVariableName // name of the variable for this product + let recordDefinition + + /* + For each dataset in our process output, we will build the information based on our input products. + */ + + /* Basic validations to see if we have everything we need. */ + if (dataset.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataset.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataset.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataset.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } @@ -88,16 +139,22 @@ } recordDefinition = dataset.parentNode.record - variableName = dataset.parentNode.code.variableName - /* Transform the raw data into JSON objects */ - jsonData = commons.jsonifyDataFile(dataFile, recordDefinition) + singularVariableName = dataset.parentNode.code.singularVariableName + + /* Build the data */ + jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, dataset.parentNode.dataBuilding, singularVariableName, timePeriod) + /* Add the calculated properties */ if (dataset.parentNode.calculations !== undefined) { - inputData = commons.calculationsProcedure(jsonData, recordDefinition, dataset.parentNode.calculations, variableName, timePeriod) + outputData = commons.calculationsProcedure(jsonData, recordDefinition, dataset.parentNode.calculations, singularVariableName, timePeriod) + } else { + outputData = jsonData } + products[dataset.parentNode.code.pluralVariableName] = outputData } - /* During the next phase, we need to generate the data of the different products this process produces an output */ + + diff --git a/server.js b/server.js index 7713e3b90..9697386e5 100644 --- a/server.js +++ b/server.js @@ -53,46 +53,6 @@ process.on('exit', function (code) { console.log('[INFO] Task Server -> server -> process.on.exit -> About to exit -> code = ' + code) }) -/* - -We read the first string sent as an argument when the process was created by the Task Manager. Ther we will find the information of the identity -of this Task and know exactly what to run within this server instance. - -*/ -global.TASK_NODE = process.argv[2] - -if (global.TASK_NODE !== undefined) { - - try { - global.TASK_NODE = JSON.parse(global.TASK_NODE) - console.log('[INFO] Task Server -> server -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE)) - } catch (err) { - console.log('[ERROR] Task Server -> server -> global.TASK_NODE -> ' + err.stack) - } - -} -else { // I use this section to debug in standalone mode. - global.TASK_NODE = { "type": "Task", "name": "Generates Bollinger Channels & Sub-Channels", "bot": { "type": "Indicator Bot Instance", "name": "Paula", "code": { "team": "AAMasters", "bot": "AAPaula", "repo": "AAPaula-Indicator-Bot" }, "processes": [{ "type": "Process Instance", "subType": "Indicator Process Instance", "name": "Market", "code": { "process": "Multi-Period-Market" }, "id": "6ddd81e2-d6db-4814-9c6c-96538a74824c", "referenceParent": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } }, "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "processOutput": { "type": "Process Output", "name": "New Process Output", "outputDatasets": [{ "type": "Output Dataset", "name": "Bollinger Channels", "id": "d804e799-7c12-42f5-8de2-52a3a7d5daa4", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Channels", "code": { "codeName": "Bollinger-Channels", "displayName": "Bollinger-Channels", "description": "Two sets of files per period that contains Bollinger-Bands-Channels-Patterns.", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [{ "type": "Record Property", "name": "Begin", "code": { "codeName": "begin", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "caf8122c-3a07-4263-a56f-ffb05df281d6" }, "id": "06edb099-ff64-4528-b8c6-599154bc2ddf" }, { "type": "Record Property", "name": "End", "code": { "codeName": "end", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "c8ec0868-8413-468c-88d5-3e43169395e0" }, "id": "94656d1f-508a-4e94-a8d2-013690aac596" }, { "type": "Record Property", "name": "Direction", "code": { "codeName": "direction", "isNumeric": false }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "e913afe9-b610-4777-bc9e-6349d436a90a" }, "id": "edc0f697-f5e2-446f-bae8-8db7adf91d23" }, { "type": "Record Property", "name": "Period", "code": { "codeName": "period", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "83fce657-a0bb-41ee-80d1-9e2ab980c472" }, "id": "eb733f85-6cf5-485e-9bd3-2bf78733c492" }, { "type": "Record Property", "name": "First Moving Average", "code": { "codeName": "firstMovingAverage", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "52490460-35e3-469a-824f-5b139571d4a0" }, "id": "a67a15ee-6f7f-48bd-9d55-82c57efc993b" }, { "type": "Record Property", "name": "Last Moving Average", "code": { "codeName": "lastMovingAverage", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "41edc1ef-293a-4ceb-bf67-8042e8fdc4eb" }, "id": "0f05bffe-d668-4d25-ab67-d55a4a7c8f5d" }, { "type": "Record Property", "name": "First Deviation", "code": { "codeName": "firstDeviation", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "5af02103-9bc9-4e6c-9684-5a612adfa495" }, "id": "eae84509-b689-4a76-9277-eed72af491dd" }, { "type": "Record Property", "name": "Last Deviation", "code": { "codeName": "lastDeviation", "isNumeric": true }, "formula": { "type": "Formula", "code": "// Write your formula here", "id": "bc82d292-e71a-4367-9b07-a0e3982ad60d" }, "id": "d5c32d1f-9e3c-48e8-8025-a17d57c0812e" }], "id": "6c3e68b0-b781-4c53-9ff7-67c3f747ac8a" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "1f111f72-624f-4166-9f99-ab8d785d5267" }, "id": "610cd062-ab88-4892-b711-0fb430305c61" } }, { "type": "Output Dataset", "name": "Bollinger Standard Channels", "id": "e524685d-8da0-41f6-839d-9a4100e6e3fe", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Standard Channels", "code": { "codeName": "Bollinger-Standard-Channels", "displayName": "Bollinger-Standard-Channels", "description": "", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [], "id": "9106250d-b042-4e96-9121-c760b4c1eab2" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "94bc3958-cc85-43a1-a178-103e84f42910" }, "id": "22c1f2c6-3838-4657-add8-2e01d7056917" } }, { "type": "Output Dataset", "name": "Bollinger Sub-Channels", "id": "eb0e7947-95e4-4f4a-87ea-1fb430b8b503", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Sub-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Sub-Channels", "code": { "codeName": "Bollinger-Sub-Channels", "displayName": "Bollinger-Sub-Channels", "description": "Bollinger Sub Channels with slope.", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [], "id": "5a780d7f-70df-4e86-8192-eacccafb5ca8" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "080c87fc-4546-4482-ab11-180a33f6d9ab" }, "id": "869f615e-0cc0-44fd-8648-4a951c23a410" } }, { "type": "Output Dataset", "name": "Bollinger Standard Sub-Channels", "id": "dc96b9ac-f8d5-4bf8-a135-45cd5d6369f1", "referenceParent": { "type": "Dataset Definition", "name": "Market", "code": { "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": "AAMasters/AAPaula.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Standard-Sub-Channels/Multi-Period-Market/@Period", "fileName": "@AssetA_@AssetB.json" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Standard Sub-Channels", "code": { "codeName": "Bollinger-Standard-Sub-Channels", "displayName": "Bollinger-Standard-Sub-Channels", "description": "Bollinger Standard Sub Channels with slope.", "shareWith": "Public" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [], "id": "cdbe5e0f-9292-4d3d-b98a-d441e6565b76" }, "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" }, "id": "79ce9c05-8a4c-4b58-850f-288037acc702" }, "id": "f8d44a4d-3771-4a71-97ca-dc604ff565d3" } }], "id": "dd39a54a-3714-446a-9a41-dfd83557664c" }, "processDependencies": { "type": "Process Dependencies", "name": "New Process Dependencies", "statusDependencies": [{ "type": "Status Dependency", "name": "Self Reference", "code": { "mainUtility": "Self Reference" }, "id": "ae627a69-4089-432d-b379-1b12ce4bd75e", "referenceParent": { "type": "Status Report", "name": "New Status Report", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } }, "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" } }, "id": "af8aded9-002a-475a-88f5-d29573740969" } }, { "type": "Status Dependency", "name": "Market Ending Point", "code": { "mainUtility": "Market Ending Point" }, "id": "4a603114-d464-46db-90a1-62304274117a", "referenceParent": { "type": "Status Report", "name": "New Status Report", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces a single file for each time period 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 }, "id": "941a0543-0cc0-4a5d-a782-728d2835c100", "parentNode": { "type": "Indicator Bot", "name": "Chris", "code": { "codeName": "AAChris" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23" } }, "id": "4f8197fa-2627-46df-bd36-7d30fdb12d8c" } }], "dataDependencies": [{ "type": "Data Dependency", "name": "Chris Bollinger Bands", "id": "ea27be54-93a3-48e3-8c52-72bc397b6c66", "referenceParent": { "type": "Dataset Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market" }, "parentNode": { "type": "Product Definition", "name": "Bollinger Bands", "code": { "codeName": "Bollinger-Bands", "variableName": "bollingerBand" }, "datasets": [], "record": { "type": "Record Definition", "name": "New Record Definition", "properties": [{ "type": "Record Property", "name": "Begin", "code": { "codeName": "begin", "isNumeric": true }, "id": "1e7186ee-c481-401d-827f-a30ee7434d92" }, { "type": "Record Property", "name": "End", "code": { "codeName": "end", "isNumeric": true }, "id": "15f90f2e-80f0-4575-94d8-6d8957eceee1" }, { "type": "Record Property", "name": "Moving Average", "code": { "codeName": "movingAverage", "isNumeric": true }, "id": "9786a9cd-1eff-4b5c-ae7c-1dde51659f82" }, { "type": "Record Property", "name": "Standard Deviation", "code": { "codeName": "standardDeviation", "isNumeric": true }, "id": "e92d7855-bb18-494e-ab37-a895bbcb8287" }, { "type": "Record Property", "name": "Deviation", "code": { "codeName": "deviation", "isNumeric": true }, "id": "03043530-caa1-416e-8303-840b732e516e" }, { "type": "Record Property", "name": "Direction", "code": { "codeName": "direction", "isNumeric": false, "isCalculated": true }, "formula": { "type": "Formula", "code": "variable.direction", "id": "f72926d9-c869-4b7c-ba1e-b36d134e2bc6" }, "id": "856f24c9-f5f1-491b-98e7-6e1d3c5c5874" }, { "type": "Record Property", "name": "Slope", "code": { "codeName": "slope", "isNumeric": false, "isCalculated": true }, "formula": { "type": "Formula", "code": "variable.slope", "id": "fac935fe-b448-4b34-aed7-b35f70808911" }, "id": "4c027b0c-da8a-4146-a51a-d0a7a126150b" }], "id": "b5a5ec97-7246-42bd-97e4-28fb4a148b2c" }, "calculations": { "type": "Calculations Procedure", "name": "New Calculations Procedure", "initialization": { "type": "Procedure Initialization", "name": "New Procedure Initialization", "code": { "type": "Code", "code": "variable.lastMovingAverage = 0\nvariable.SIDE_TOLERANCE = 0.5 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\nvariable.SMALL_SLOPE = 1.0 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\nvariable.MEDIUM_SLOPE = 2.0 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\nvariable.HIGH_SLOPE = 4.0 * system.timePeriod / system.ONE_DAY_IN_MILISECONDS\n", "id": "f00ac417-cd99-493c-b959-35eddd6fe4f3" }, "id": "d144ab2f-5b4e-4153-806a-d471e9f281dd" }, "loop": { "type": "Procedure Loop", "name": "New Procedure Loop", "code": { "type": "Code", "code": "\nif (variable.lastMovingAverage > indicator.bollingerBand.movingAverage) { variable.direction = \"Down\" }\nif (variable.lastMovingAverage < indicator.bollingerBand.movingAverage) { variable.direction = \"Up\" }\nif (variable.lastMovingAverage === indicator.bollingerBand.movingAverage) { variable.direction = \"Side\" }\n\nvariable.delta = Math.abs(indicator.bollingerBand.movingAverage - variable.lastMovingAverage)\n\nvariable.slope = \"Extreme\"\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.HIGH_SLOPE / 100) { variable.slope = \"Steep\" }\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.MEDIUM_SLOPE / 100) { variable.slope = \"Medium\" }\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.SMALL_SLOPE / 100) { variable.slope = \"Gentle\" }\nif (variable.delta < indicator.bollingerBand.movingAverage * variable.SIDE_TOLERANCE / 100) { variable.slope = \"Side\" }\n\nvariable.lastMovingAverage = indicator.bollingerBand.movingAverage", "id": "14fc0c9c-cd10-4931-95b5-4bc7d549942a" }, "id": "8a669548-845b-4d80-ae96-29f68e9c9ac7" }, "id": "d72c7cb2-8886-4101-a83a-f9788b8400a0" }, "dataBuilding": { "type": "Data Building Procedure", "name": "New Data Building Procedure", "id": "139484b3-3a3f-4268-801e-a8c441746c7a" }, "parentNode": { "type": "Indicator Bot", "name": "Chris", "code": { "codeName": "AAChris" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "1b7ecdbd-82e9-4a27-8fdd-467c0ca6bf23" }, "id": "29088877-8d63-4217-8d34-8742afc262f4" }, "id": "f5b2a7d8-fc32-44a3-a002-9b0fc30a9719" } }], "id": "8b509294-6028-40e2-88a8-655a34245ed8" }, "statusReport": { "type": "Status Report", "name": "New Status Report", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } }, "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "parentNode": { "type": "Indicator Bot", "name": "Paula", "code": { "codeName": "AAPaula" }, "processes": [], "products": [], "parentNode": { "type": "Team", "name": "Masters", "code": { "codeName": "AAMasters" }, "sensorBots": [], "indicatorBots": [], "tradingBots": [], "plotters": [], "id": "35be7b1c-a0f7-4b5f-9ee1-8fb51595e959" }, "id": "bd0f325d-6f98-468b-8b7b-99a42a175d80" } }, "id": "af8aded9-002a-475a-88f5-d29573740969" }, "executionStartedEvent": { "type": "Execution Started Event", "name": "New Execution Started Event", "id": "f4720e04-5def-4c0b-be42-33e50ac6a6b7", "referenceParent": { "type": "Execution Finished Event", "name": "New Execution Finished Event", "id": "4a9c8cc7-0ebf-43eb-b7f9-0b280630ff67", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "id": "941a0543-0cc0-4a5d-a782-728d2835c100", "code": { "codeName": "Multi-Period-Market", "description": "Produces a single file for each time period 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 } } } }, "executionFinishedEvent": { "type": "Execution Finished Event", "name": "New Execution Finished Event", "id": "671530dc-5ba5-4c8d-bc23-f228e22c56b3", "parentNode": { "type": "Process Definition", "name": "Multi-Period-Market", "id": "28be432d-80ca-46b3-859a-55541e24b2b4", "code": { "codeName": "Multi-Period-Market", "description": "Produces for each market two files with bollinger channels respectively.", "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, "framework": { "name": "Multi-Period-Market", "startDate": {}, "endDate": {} } } } } } }], "id": "27564838-24b1-4814-af8c-ec65accb05d1" }, "id": "aa0f2533-460c-464f-ad77-6ae4cadd1178" } -} - -require('dotenv').config(); - -global.WRITE_LOGS_TO_FILES = process.env.WRITE_LOGS_TO_FILES - -/* Default parameters can be changed by the execution configuration */ -global.EXCHANGE_NAME = 'Poloniex' -global.MARKET = { assetA: 'USDT', assetB: 'BTC' } -global.CLONE_EXECUTOR = { codeName: 'AACloud', version: '1.1' } // NOTE: To refactor the name of this variable you would need to go through the bots code that are using it. - -/* -We need to count how many process instances we deployd and how many of them have already finished their job, either -because they just finished or because there was a request to stop the proceses. In this way, once we reach the -amount of instances started, we can safelly destroy the rest of the objects running and let this nodejs process die. -*/ - -global.ENDED_PROCESSES_COUNTER = 0 -global.TOTAL_PROCESS_INSTANCES_CREATED = 0 - /* Here we listen for the message to stop this Task / Process comming from the Task Manager, which is the paret of this node js process. */ process.on('message', message => { if (message === 'Stop this Task') { @@ -133,14 +93,61 @@ global.EXIT_NODE_PROCESS = function exitProcess() { setTimeout(process.exit, 10000) // We will give 10 seconds to logs be written on file } +require('dotenv').config(); + +global.WRITE_LOGS_TO_FILES = process.env.WRITE_LOGS_TO_FILES + +/* Default parameters can be changed by the execution configuration */ +global.EXCHANGE_NAME = 'Poloniex' +global.MARKET = { assetA: 'USDT', assetB: 'BTC' } +global.CLONE_EXECUTOR = { codeName: 'AACloud', version: '1.1' } // NOTE: To refactor the name of this variable you would need to go through the bots code that are using it. + +/* +We need to count how many process instances we deployd and how many of them have already finished their job, either +because they just finished or because there was a request to stop the proceses. In this way, once we reach the +amount of instances started, we can safelly destroy the rest of the objects running and let this nodejs process die. +*/ + +global.ENDED_PROCESSES_COUNTER = 0 +global.TOTAL_PROCESS_INSTANCES_CREATED = 0 + +/* + +We read the first string sent as an argument when the process was created by the Task Manager. There we will find the information of the identity +of this Task and know exactly what to run within this server instance. + +*/ +global.TASK_NODE = process.argv[2] + /* Setting up the global Event Handler */ const EVENT_HANDLER_MODULE = require('./SystemEventHandler.js'); const IPC = require('node-ipc'); global.SYSTEM_EVENT_HANDLER = EVENT_HANDLER_MODULE.newSystemEventHandler(IPC) -global.SYSTEM_EVENT_HANDLER.initialize('Task Server', bootLoader) +global.SYSTEM_EVENT_HANDLER.initialize('Task Server', preLoader) global.STOP_TASK_GRACEFULLY = false; +function preLoader() { + if (global.TASK_NODE !== undefined) { + /* The Task Manager sent the info via a process argument. */ + try { + global.TASK_NODE = JSON.parse(global.TASK_NODE) + console.log('[INFO] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE)) + } catch (err) { + console.log('[ERROR] Task Server -> server -> preLoader -> vglobal.TASK_NODE -> ' + err.stack) + } + bootLoader() + } + else { /* This process was started not by the Task Manager, but independently (most likely for debugging purposes). In this case we listen to an event with the Task Info */ + console.log('[INFO] Task Server -> server -> preLoader -> Waiting for event to start debugging...') + global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server', 'Debug Task Started', undefined, undefined, undefined, startDebugging) + function startDebugging(message) { + global.TASK_NODE = JSON.parse(message.event.definition) + bootLoader() + } + } +} + function bootLoader() { /* Heartbeat sent to the UI */ From 44ac721bbd2a1b2e73b9ecb4dedcf1b8ffb590d0 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 14 Nov 2019 10:04:45 +0100 Subject: [PATCH 16/38] Generated File Being Recorded --- IndicatorBotCommons.js | 14 ++- IndicatorBotMarket.js | 223 ++++++++++++++++++++++------------------- 2 files changed, 128 insertions(+), 109 deletions(-) diff --git a/IndicatorBotCommons.js b/IndicatorBotCommons.js index 959c6deda..384cf0a2f 100644 --- a/IndicatorBotCommons.js +++ b/IndicatorBotCommons.js @@ -91,17 +91,16 @@ if (property.code.isCalculated === true) { if (property.formula !== undefined) { if (property.formula.code !== undefined) { - let newValue try { - newvalue = eval(property.formula.code) + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue } catch (err) { logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); throw ("Error Executing User Code.") } - let currentRecord = product[variableName] - currentRecord[property.code.codeName] = newValue } } } @@ -174,17 +173,16 @@ if (property.code.isCalculated !== true) { if (property.formula !== undefined) { if (property.formula.code !== undefined) { - let newValue try { - newvalue = eval(property.formula.code) + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue } catch (err) { logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); throw ("Error Executing User Code.") } - let currentRecord = product[variableName] - currentRecord[property.code.codeName] = newValue } } } diff --git a/IndicatorBotMarket.js b/IndicatorBotMarket.js index 1f6c44456..ec7b1e019 100644 --- a/IndicatorBotMarket.js +++ b/IndicatorBotMarket.js @@ -51,10 +51,11 @@ let products = {} let mainDependency + /* The first phase is about transforming the inputs into a format that can be used to apply the user defined code. */ let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies for (let i = 0; i < dataDependencies.length; i++) { - let dataset = dataDependencies[i].referenceParent + let dataDependencyNode = dataDependencies[i].referenceParent let dataFile = dataFiles[i] let jsonData // Datafile converted into Json objects let inputData // Includes calculated properties @@ -62,39 +63,39 @@ let recordDefinition /* - For each dataset in our data dependencies, we should have da dataFile containing the records needed as an imput for this process. + For each dataDependencyNode in our data dependencies, we should have da dataFile containing the records needed as an imput for this process. What we need to do first is transform those records into JSON objects that can be used by user-defined formulas. The first step does that but with the not calculated properties, the second step adds the calculated properties. */ /* Basic validations to see if we have everything we need. */ - if (dataset.parentNode.code.singularVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (dataDependencyNode.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataset.parentNode.code.pluralVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (dataDependencyNode.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataset.parentNode.record === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (dataDependencyNode.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - recordDefinition = dataset.parentNode.record - singularVariableName = dataset.parentNode.code.singularVariableName + recordDefinition = dataDependencyNode.parentNode.record + singularVariableName = dataDependencyNode.parentNode.code.singularVariableName /* Transform the raw data into JSON objects */ jsonData = commons.jsonifyDataFile(dataFile, recordDefinition) /* Add the calculated properties */ - if (dataset.parentNode.calculations !== undefined) { - inputData = commons.calculationsProcedure(jsonData, recordDefinition, dataset.parentNode.calculations, singularVariableName, timePeriod) + if (dataDependencyNode.parentNode.calculations !== undefined) { + inputData = commons.calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.parentNode.calculations, singularVariableName, timePeriod) } else { inputData = jsonData } - products[dataset.parentNode.code.pluralVariableName] = inputData + products[dataDependencyNode.parentNode.code.pluralVariableName] = inputData /* The main dependency is defined as the first dependency processed. */ if (mainDependency === undefined) { @@ -102,117 +103,135 @@ } } - /* During the next phase, we need to generate the data of the different products this process produces an output */ + /* During the second phase, we need to generate the data of the different products this process produces an output */ let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets for (let i = 0; i < outputDatasets.length; i++) { - let dataset = outputDatasets[i].referenceParent + let outputDatasetNode = outputDatasets[i] let jsonData // Just build data as Json objects let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure - let singularVariableName // name of the variable for this product - let recordDefinition + let singularVariableName // Name of the variable for this product + let recordDefinition // Record as defined by the user at the UI + let fileContent // Here we store the contents of the new data built just before writing it to a file. /* - For each dataset in our process output, we will build the information based on our input products. + For each outputDatasetNode in our process output, we will build the information based on our input products. */ /* Basic validations to see if we have everything we need. */ - if (dataset.parentNode.code.singularVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (outputDatasetNode.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Output Dataset without Reference Parent. Output Dataset = " + JSON.stringify(outputDatasetNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataset.parentNode.code.pluralVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (outputDatasetNode.referenceParent.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataset.parentNode.record === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (outputDatasetNode.referenceParent.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataset.parentNode.dataBuilding === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(dataset.parentNode)); + if (outputDatasetNode.referenceParent.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.dataBuilding === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - recordDefinition = dataset.parentNode.record - singularVariableName = dataset.parentNode.code.singularVariableName + recordDefinition = outputDatasetNode.referenceParent.parentNode.record + singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName /* Build the data */ - jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, dataset.parentNode.dataBuilding, singularVariableName, timePeriod) + jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod) /* Add the calculated properties */ - if (dataset.parentNode.calculations !== undefined) { - outputData = commons.calculationsProcedure(jsonData, recordDefinition, dataset.parentNode.calculations, singularVariableName, timePeriod) + if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { + outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) } else { outputData = jsonData } - products[dataset.parentNode.code.pluralVariableName] = outputData - } - - - - - - - - - - - - - - let dataFile; - - let bands = []; - let channels = []; - let standardChannels = []; - let subChannels = []; - let standardSubChannels = []; - - dataFile = dataFiles[0]; // We only need the bollinger bands. - - commons.buildBandsArray(dataFile, bands, timePeriod, callBackFunction); - commons.buildChannels(bands, channels, callBackFunction); - commons.buildStandardChannels(bands, standardChannels, callBackFunction); - commons.buildSubChannels(bands, subChannels, callBackFunction); - commons.buildStandardSubChannels(bands, standardSubChannels, callBackFunction); - - let dataDependency = {} - - for (let i = 0; i < processConfig.dataDependencies.length; i++) { - let dependency = processConfig.dataDependencies[i] - let recordDefinition = processConfig.recordDefinition - let dependencyArray = commons.buildDependencyArray(dataFiles[i], recordDefinition); - dependencyArray = commons.addCalculatedProperties(dependencyArray, dependency.singularVariableName, timePeriod); - dataDependency[dependency.pluralVariableName] = dependencyArray - } - - for (let i = 0; i < processConfig.updatesDatasets.length; i++) { - let product = processConfig.updatesDatasets[i].product - let dataset = processConfig.updatesDatasets[i].dataset - let recordDefinition = getRecordDefinition(product) - let records = commons.buildRecords(dataDependency, recordDefinition) - let fileContent = generateFileContent(indicatorData, recordDefinition) - writeFile(product, dataset, fileContent) + products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData } + /*At the third and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ let totalFilesWritten = 0 + for (let i = 0; i < outputDatasets.length; i++) { + let outputDatasetNode = outputDatasets[i] + let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] + let contextSummary = {} + + /* Some very basic validations that we have all the information needed. */ + if (outputDatasetNode.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot witn no codeName defined. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Team. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Team witn no codeName defined. Team = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* Simplifying the access to basic info */ + + contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName + contextSummary.product = outputDatasetNode.referenceParent.parentNode.code.codeName + contextSummary.bot = outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName + contextSummary.devTeam = outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + contextSummary.botVersion = { + "major": 1, + "minor": 0 + } + contextSummary.dataSetVersion = "dataSet.V1" + + let fileContent = generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record) + writeFile(contextSummary, fileContent) + } + function anotherFileWritten() { totalFilesWritten++ if (totalFilesWritten === processConfig.updatesDatasets.length) { callBackFunction(global.DEFAULT_OK_RESPONSE); - } - } - - function getRecordDefinition(product) { - for (let j = 0; j < bot.products.length; j++) { - if (bot.products[j].codeName === product) { - return bot.products[j].recordDefinition - } } } @@ -229,17 +248,19 @@ let record = records[i]; fileContent = fileContent + recordSeparator + '[' let propertySeparator = "" - for (let j = 0; j < recordDefinition.length; j++) { - let property = recordDefinition[j] - fileContent = fileContent + propertySeparator - if (property.isNumeric !== true) { - fileContent = fileContent + '"' + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + fileContent = fileContent + propertySeparator + if (property.code.isNumeric !== true) { + fileContent = fileContent + '"' + } + fileContent = fileContent + record[property.code.codeName] + if (property.code.isNumeric !== true) { + fileContent = fileContent + '"' + } + if (propertySeparator === "") { propertySeparator = ","; } } - fileContent = fileContent + record[property.name] - if (property.isNumeric !== true) { - fileContent = fileContent + '"' - } - if (recordSeparator === "") { propertySeparator = ","; } } fileContent = fileContent + ']' if (recordSeparator === "") { recordSeparator = ","; } @@ -253,7 +274,7 @@ } } - function writeFile(product, dataset, fileContent) { + function writeFile(contextSummary, fileContent) { try { @@ -261,11 +282,11 @@ let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + product + "/" + dataset + "/" + outputPeriodLabel; + let filePathRoot = contextSummary.devTeam + "/" + contextSummary.bot + "." + contextSummary.botVersion.major + "." + contextSummary.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + contextSummary.dataSetVersion; + let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + outputPeriodLabel; filePath += '/' + fileName - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + fileStorage.createTextFile(contextSummary.devTeam, filePath, fileContent + '\n', onFileCreated); function onFileCreated(err) { From ef189dc76d7b935b9f2f6a574827ddf365a8ac48 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 14 Nov 2019 12:27:53 +0100 Subject: [PATCH 17/38] IndicatorBot working with Multi Period Market after reorg --- IndicatorBotMarket.js => IndicatorBot.js | 278 ++++++++++++++-- IndicatorBotCommons.js | 201 ----------- IndicatorBotDaily.js | 405 ----------------------- MultiPeriodDaily.js | 15 +- MultiPeriodMarket.js | 15 +- Project.njsproj | 10 +- 6 files changed, 268 insertions(+), 656 deletions(-) rename IndicatorBotMarket.js => IndicatorBot.js (50%) delete mode 100644 IndicatorBotCommons.js delete mode 100644 IndicatorBotDaily.js diff --git a/IndicatorBotMarket.js b/IndicatorBot.js similarity index 50% rename from IndicatorBotMarket.js rename to IndicatorBot.js index ec7b1e019..ab38cd4c0 100644 --- a/IndicatorBotMarket.js +++ b/IndicatorBot.js @@ -1,29 +1,22 @@ -exports.newIndicatorBotMarket = function newIndicatorBotMarket(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) { +exports.newIndicatorBot = function newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; - const MODULE_NAME = "User Bot"; - - const BOLLINGER_CHANNELS_FOLDER_NAME = "Bollinger-Channels"; - const BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Channels"; - const BOLLINGER_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Sub-Channels"; - const BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Sub-Channels"; - - const commons = COMMONS.newIndicatorBotCommons(bot, logger, UTILITIES); + const MODULE_NAME = "Indicator Bot"; thisObject = { initialize: initialize, start: start }; + let utilities = UTILITIES.newCloudUtilities(logger); let fileStorage = FILE_STORAGE.newFileStorage(logger); - let processConfig; return thisObject; - function initialize(pProcessConfig, callBackFunction) { + function initialize(callBackFunction) { try { @@ -32,7 +25,6 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - processConfig = pProcessConfig; callBackFunction(global.DEFAULT_OK_RESPONSE); } catch (err) { @@ -41,7 +33,7 @@ } } - function start(dataFiles, timePeriod, outputPeriodLabel, startDate, endDate, callBackFunction) { + function start(dataFiles, timePeriod, outputPeriodLabel, currentDay, startDate, endDate, interExecutionMemory, callBackFunction) { try { @@ -50,6 +42,13 @@ let market = global.MARKET; let products = {} let mainDependency + let processingDailyFiles + + if (currentDay !== undefined) { + processingDailyFiles = true + } else { + processingDailyFiles = false + } /* The first phase is about transforming the inputs into a format that can be used to apply the user defined code. */ let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies @@ -88,10 +87,10 @@ recordDefinition = dataDependencyNode.parentNode.record singularVariableName = dataDependencyNode.parentNode.code.singularVariableName /* Transform the raw data into JSON objects */ - jsonData = commons.jsonifyDataFile(dataFile, recordDefinition) + jsonData = jsonifyDataFile(dataFile, recordDefinition) /* Add the calculated properties */ if (dataDependencyNode.parentNode.calculations !== undefined) { - inputData = commons.calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.parentNode.calculations, singularVariableName, timePeriod) + inputData = calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.parentNode.calculations, singularVariableName, timePeriod) } else { inputData = jsonData } @@ -108,11 +107,12 @@ for (let i = 0; i < outputDatasets.length; i++) { let outputDatasetNode = outputDatasets[i] - let jsonData // Just build data as Json objects - let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure - let singularVariableName // Name of the variable for this product - let recordDefinition // Record as defined by the user at the UI - let fileContent // Here we store the contents of the new data built just before writing it to a file. + let jsonData // Just build data as Json objects + let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure + let singularVariableName // Name of the variable for this product + let recordDefinition // Record as defined by the user at the UI + let fileContent // Here we store the contents of the new data built just before writing it to a file. + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. /* For each outputDatasetNode in our process output, we will build the information based on our input products. @@ -148,12 +148,17 @@ recordDefinition = outputDatasetNode.referenceParent.parentNode.record singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName + /* Check Irregular Periods */ + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + /* Build the data */ - jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod) + jsonData = dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) /* Add the calculated properties */ if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { - outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) + outputData = calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) } else { outputData = jsonData } @@ -165,6 +170,7 @@ for (let i = 0; i < outputDatasets.length; i++) { let outputDatasetNode = outputDatasets[i] let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. let contextSummary = {} /* Some very basic validations that we have all the information needed. */ @@ -210,6 +216,11 @@ return } + /* Check Irregular Periods */ + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + /* Simplifying the access to basic info */ contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName @@ -224,18 +235,202 @@ } contextSummary.dataSetVersion = "dataSet.V1" - let fileContent = generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record) + let fileContent = generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods) writeFile(contextSummary, fileContent) } + function jsonifyDataFile(dataFile, recordDefinition) { + + /* + This function has as an input the raw data on files and creates with it an array of JSON objects + with not calculated properties for later being consumed by Formulas + */ + + let jsonifiedArray = [] + + for (let i = 0; i < dataFile.length; i++) { + + let record = {} + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + record[property.code.codeName] = dataFile[i][j] + } + } + + jsonifiedArray.push(record); + } + + return jsonifiedArray + } + + function calculationsProcedure(jsonArray, recordDefinition, calculationsProcedure, variableName, timePeriod) { + + /* + This function has as an input an array of JSON objects, and it adds calculated properties to + complete the set of properties that will be available for Formulas. + */ + + let system = { // These are the available system variables to be used in User Code and Formulas + timePeriod: timePeriod, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] + + /* This is Initialization Code */ + if (calculationsProcedure.initialization !== undefined) { + if (calculationsProcedure.initialization.code !== undefined) { + try { + eval(calculationsProcedure.initialization.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = " + calculationsProcedure.initialization.code.code); + throw ("Error Executing User Code.") + } + } + } + + /* This is Initialization Code */ + if (calculationsProcedure.loop !== undefined) { + if (calculationsProcedure.loop.code !== undefined) { + for (let index = 0; index < jsonArray.length; index++) { + + let product = {} + product[variableName] = jsonArray[index] + + /* This is Loop Code */ + try { + eval(calculationsProcedure.loop.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = " + calculationsProcedure.loop.code.code); + throw ("Error Executing User Code.") + } + + /* For each calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated === true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + try { + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + } + } + } + } + + /* Adding the new element to the resulting array */ + results.push(product[variableName]); + } + } + } + return results + } + + function dataBuildingProcedure(products, mainDependency, recordDefinition, dataBuildingProcedure, variableName, timePeriod, resultsWithIrregularPeriods) { + + /* + This function has as an input the products object, with all the information + of all products calculated so far by the process. Based on that information + the function will evaluate user supplied code and formulas in order to build + a new set of information. + */ + + let system = { // These are the available system variables to be used in User Code and Formulas + timePeriod: timePeriod, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] + + /* This is Initialization Code */ + if (dataBuildingProcedure.initialization !== undefined) { + if (dataBuildingProcedure.initialization.code !== undefined) { + try { + eval(dataBuildingProcedure.initialization.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Code = " + dataBuildingProcedure.initialization.code.code); + throw ("Error Executing User Code.") + } + } + } + + /* This is Initialization Code */ + if (dataBuildingProcedure.loop !== undefined) { + if (dataBuildingProcedure.loop.code !== undefined) { + for (let index = 0; index < mainDependency.length; index++) { + + let record = { + previous: mainDependency[index - 1], + current: mainDependency[index], + next: mainDependency[index + 1] + } + + let product = {} + product[variableName] = {} + + /* This is Loop Code */ + try { + eval(dataBuildingProcedure.loop.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Code = " + dataBuildingProcedure.loop.code.code); + throw ("Error Executing User Code.") + } + + /* For each non-calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + try { + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + } + } + } + } + + /* If within the code the new data structure was not yet pushed to the results array, we do it */ + if (resultsWithIrregularPeriods !== true) { + /* Adding the new element to the resulting array */ + results.push(product[variableName]); + } + } + } + } + return results + } + function anotherFileWritten() { totalFilesWritten++ - if (totalFilesWritten === processConfig.updatesDatasets.length) { + if (totalFilesWritten === outputDatasets.length) { callBackFunction(global.DEFAULT_OK_RESPONSE); } } - function generateFileContent(records, recordDefinition) { + function generateFileContent(records, recordDefinition, resultsWithIrregularPeriods) { try { @@ -246,6 +441,32 @@ for (let i = 0; i < records.length; i++) { let record = records[i]; + + if (processingDailyFiles === true) { + + if (resultsWithIrregularPeriods === true) { + /* + Here we have an special problem that occurs when an object spans several time peridos. If not taken care of + it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of + its properties. + + To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it + at the next day, in whole, even if it starts in the previous day. + */ + + let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + + if (record.end < currentDay.valueOf() - 1) { continue; } + if (record.end === lastInstantOdDay) { continue; } + } else { + /* + The normal situation is that objects are respecting the time size of its main dependency, in which case we only + care of not adding objects of the previous day we are currently processing (while on daily files) + */ + if (record.end < currentDay.valueOf()) { continue; } + } + } + fileContent = fileContent + recordSeparator + '[' let propertySeparator = "" for (let j = 0; j < recordDefinition.properties.length; j++) { @@ -281,9 +502,14 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> Entering function."); } let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + let dateForPath = '' + + if (processingDailyFiles === true) { + dateForPath = "/" + currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + } let filePathRoot = contextSummary.devTeam + "/" + contextSummary.bot + "." + contextSummary.botVersion.major + "." + contextSummary.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + contextSummary.dataSetVersion; - let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + outputPeriodLabel; + let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + outputPeriodLabel + dateForPath; filePath += '/' + fileName fileStorage.createTextFile(contextSummary.devTeam, filePath, fileContent + '\n', onFileCreated); diff --git a/IndicatorBotCommons.js b/IndicatorBotCommons.js deleted file mode 100644 index 384cf0a2f..000000000 --- a/IndicatorBotCommons.js +++ /dev/null @@ -1,201 +0,0 @@ -exports.newIndicatorBotCommons = function newIndicatorBotCommons(bot, logger, UTILITIES) { - - const FULL_LOG = true; - const LOG_FILE_CONTENT = false; - - const MODULE_NAME = "Commons"; - const GMT_SECONDS = ':00.000 GMT+0000'; - const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; - - let thisObject = { - jsonifyDataFile: jsonifyDataFile, - calculationsProcedure: calculationsProcedure, - dataBuildingProcedure: dataBuildingProcedure - }; - - return thisObject; - - function jsonifyDataFile(dataFile, recordDefinition) { - - /* - This function has as an input the raw data on files and creates with it an array of JSON objects - with not calculated properties for later being consumed by Formulas - */ - - let jsonifiedArray = [] - - for (let i = 0; i < dataFile.length; i++) { - - let record = {} - for (let j = 0; j < recordDefinition.properties.length; j++) { - let property = recordDefinition.properties[j] - if (property.code.isCalculated !== true) { - record[property.code.codeName] = dataFile[i][j] - } - } - - jsonifiedArray.push(record); - } - - return jsonifiedArray - } - - function calculationsProcedure(jsonArray, recordDefinition, calculationsProcedure, variableName, timePeriod) { - - /* - This function has as an input an array of JSON objects, and it adds calculated properties to - complete the set of properties that will be available for Formulas. - */ - - let system = { // These are the available system variables to be used in User Code and Formulas - timePeriod: timePeriod, - ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS - } - let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. - let results = [] - - /* This is Initialization Code */ - if (calculationsProcedure.initialization !== undefined) { - if (calculationsProcedure.initialization.code !== undefined) { - try { - eval(calculationsProcedure.initialization.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = " + calculationsProcedure.initialization.code.code); - throw("Error Executing User Code.") - } - } - } - - /* This is Initialization Code */ - if (calculationsProcedure.loop !== undefined) { - if (calculationsProcedure.loop.code !== undefined) { - for (let index = 0; index < jsonArray.length; index++) { - - let product = {} - product[variableName] = jsonArray[index] - - /* This is Loop Code */ - try { - eval(calculationsProcedure.loop.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = " + calculationsProcedure.loop.code.code); - throw ("Error Executing User Code.") - } - - /* For each calculated property we apply its formula*/ - for (let j = 0; j < recordDefinition.properties.length; j++) { - let property = recordDefinition.properties[j] - if (property.code.isCalculated === true) { - if (property.formula !== undefined) { - if (property.formula.code !== undefined) { - try { - let newValue = eval(property.formula.code) - let currentRecord = product[variableName] - currentRecord[property.code.codeName] = newValue - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); - throw ("Error Executing User Code.") - } - } - } - } - } - - /* Adding the new element to the resulting array */ - results.push(product[variableName]); - } - } - } - return results - } - - function dataBuildingProcedure(products, mainDependency, recordDefinition, dataBuildingProcedure, variableName, timePeriod) { - - /* - This function has as an input the products object, with all the information - of all products calculated so far by the process. Based on that information - the function will evaluate user supplied code and formulas in order to build - a new set of information. - */ - - let system = { // These are the available system variables to be used in User Code and Formulas - timePeriod: timePeriod, - ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS - } - let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. - let results = [] - - /* This is Initialization Code */ - if (dataBuildingProcedure.initialization !== undefined) { - if (dataBuildingProcedure.initialization.code !== undefined) { - try { - eval(dataBuildingProcedure.initialization.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Code = " + dataBuildingProcedure.initialization.code.code); - throw ("Error Executing User Code.") - } - } - } - - /* This is Initialization Code */ - if (dataBuildingProcedure.loop !== undefined) { - if (dataBuildingProcedure.loop.code !== undefined) { - for (let index = 0; index < mainDependency.length; index++) { - - let record = { - previous: mainDependency[index-1], - current: mainDependency[index], - next: mainDependency[index+1] - } - - let product = {} - product[variableName] = {} - - /* This is Loop Code */ - try { - eval(dataBuildingProcedure.loop.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Code = " + dataBuildingProcedure.loop.code.code); - throw ("Error Executing User Code.") - } - - /* For each non-calculated property we apply its formula*/ - for (let j = 0; j < recordDefinition.properties.length; j++) { - let property = recordDefinition.properties[j] - if (property.code.isCalculated !== true) { - if (property.formula !== undefined) { - if (property.formula.code !== undefined) { - try { - let newValue = eval(property.formula.code) - let currentRecord = product[variableName] - currentRecord[property.code.codeName] = newValue - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); - throw ("Error Executing User Code.") - } - } - } - } - } - - /* If within the code the new data structure was not yet pushed to the results array, we do it */ - if (dataBuildingProcedure.loop.code.code.indexOf('results.push') < 0) { - /* Adding the new element to the resulting array */ - results.push(product[variableName]); - } - } - } - } - return results - } -}; diff --git a/IndicatorBotDaily.js b/IndicatorBotDaily.js deleted file mode 100644 index c06f99347..000000000 --- a/IndicatorBotDaily.js +++ /dev/null @@ -1,405 +0,0 @@ -exports.newIndicatorBotDaily = function newIndicatorBotDaily(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) { - - const FULL_LOG = true; - const LOG_FILE_CONTENT = false; - const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; - - const MODULE_NAME = "User Bot"; - - const BOLLINGER_CHANNELS_FOLDER_NAME = "Bollinger-Channels"; - const BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Channels"; - const BOLLINGER_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Sub-Channels"; - const BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Sub-Channels"; - - const commons = COMMONS.newIndicatorBotCommons(bot, logger, UTILITIES); - - thisObject = { - initialize: initialize, - start: start - }; - - let utilities = UTILITIES.newCloudUtilities(bot, logger); - - let fileStorage = FILE_STORAGE.newFileStorage(logger); - let processConfig; - - return thisObject; - - function initialize(pProcessConfig, callBackFunction) { - - try { - - logger.fileName = MODULE_NAME; - logger.initialize(); - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - - processConfig = pProcessConfig; - callBackFunction(global.DEFAULT_OK_RESPONSE); - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function start(dataFiles, timePeriod, outputPeriodLabel, currentDay, startDate, endDate, interExecutionMemory, callBackFunction) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } - - let market = global.MARKET; - let dataFile; - - let bands = []; - let channels = []; - let standardChannels = []; - let subChannels = []; - let standardSubChannels = []; - - dataFile = dataFiles[0]; // We only need the bollinger bands. - - commons.buildBandsArray(dataFile, bands, timePeriod, callBackFunction); - commons.buildChannels(bands, channels, callBackFunction); - commons.buildStandardChannels(bands, standardChannels, callBackFunction); - commons.buildSubChannels(bands, subChannels, callBackFunction); - commons.buildStandardSubChannels(bands, standardSubChannels, callBackFunction); - - writeChannelsFile(); - - function writeChannelsFile() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; - - let fileContent = ""; - - for (let i = 0; i < channels.length; i++) { - - let channel = channels[i]; - - /* - Here we have an special problem that occurs when an object spans several time peridos. If not taken care of - it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of - its properties. - - To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it - at the next day, in whole, even if it starts in the previous day. - */ - - let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; - - if (channel.end < currentDay.valueOf() - 1) { continue; } - if (channel.end === lastInstantOdDay) { continue; } - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - channel.period + "," + - channel.firstMovingAverage + "," + - channel.lastMovingAverage + "," + - channel.firstDeviation + "," + - channel.lastDeviation + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - - } - - fileContent = "[" + fileContent + "]"; - - let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; - filePath += '/' + fileName - - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeChannelsFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - writeStandardChannelsFile(); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeChannelsFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeStandardChannelsFile() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; - - let fileContent = ""; - - for (i = 0; i < standardChannels.length; i++) { - - let channel = standardChannels[i]; - - if (channel.end < currentDay.valueOf()) { continue; } - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - channel.period + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - - } - - fileContent = "[" + fileContent + "]"; - - let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; - filePath += '/' + fileName - - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardChannelsFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - writeSubChannelsFile(); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardChannelsFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeSubChannelsFile() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; - - let fileContent = ""; - - for (i = 0; i < subChannels.length; i++) { - - let channel = subChannels[i]; - - /* - Here we have an special problem that occurs when an object spans several time peridos. If not taken care of - it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of - its properties. - - To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it - at the next day, in whole, even if it starts in the previous day. - */ - - let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; - - if (channel.end < currentDay.valueOf() - 1) { continue; } - if (channel.end === lastInstantOdDay) { continue; } - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - '"' + channel.slope + '"' + "," + - channel.period + "," + - channel.firstMovingAverage + "," + - channel.lastMovingAverage + "," + - channel.firstDeviation + "," + - channel.lastDeviation + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - - } - - fileContent = "[" + fileContent + "]"; - - let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; - filePath += '/' + fileName - - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - writeStandardSubChannelsFile(); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeSubChannelsFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeStandardSubChannelsFile() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> Entering function."); } - - let separator = ""; - let fileRecordCounter = 0; - - let fileContent = ""; - - for (i = 0; i < standardSubChannels.length; i++) { - - let channel = standardSubChannels[i]; - - if (channel.end < currentDay.valueOf()) { continue; } - - fileContent = fileContent + separator + '[' + - - channel.begin + "," + - channel.end + "," + - '"' + channel.direction + '"' + "," + - '"' + channel.slope + '"' + "," + - channel.period + "]"; - - if (separator === "") { separator = ","; } - - fileRecordCounter++; - } - - fileContent = "[" + fileContent + "]"; - - let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - - let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; - let filePath = filePathRoot + "/Output/" + BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; - filePath += '/' + fileName - - fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStandardSubChannelsFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - callBackFunction(global.DEFAULT_OK_RESPONSE); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeStandardSubChannelsFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } -}; diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 33588ac3b..e9c5c10df 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -19,7 +19,7 @@ let datasets = []; let dataFiles = []; - let usertBot; + let botInstance; let fileStorage = FILE_STORAGE.newFileStorage(logger); @@ -46,7 +46,7 @@ let key; let dataset; - let dependency = dataDependencies.config[i]; + let dependency = dataDependencies.nodeArray[i]; key = dependency.devTeam + "-" + dependency.bot + "-" + @@ -60,11 +60,10 @@ } - let COMMONS_MODULE = require("./IndicatorBotCommons") - let USER_BOT_MODULE = require("./IndicatorBotDaily") + let USER_BOT_MODULE = require("./IndicatorBot") - usertBot = USER_BOT_MODULE.newIndicatorBotDaily(bot, logger, COMMONS_MODULE, UTILITIES, FILE_STORAGE); - usertBot.initialize(processConfig, callBackFunction); + botInstance = USER_BOT_MODULE.newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE); + botInstance.initialize(callBackFunction); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); @@ -77,7 +76,7 @@ dataFiles = undefined statusDependencies = undefined dataDependencies = undefined - usertBot = undefined + botInstance = undefined fileStorage = undefined processConfig = undefined thisObject = undefined @@ -602,7 +601,7 @@ const timePeriod = global.dailyFilePeriods[n][0]; const outputPeriodLabel = global.dailyFilePeriods[n][1]; - usertBot.start( + botInstance.start( dataFiles, timePeriod, outputPeriodLabel, diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index a5f286233..8133547bb 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -17,7 +17,7 @@ let datasets = []; let dataFiles = []; - let usertBot; + let botInstance; let processConfig; @@ -54,11 +54,10 @@ } - let COMMONS_MODULE = require("./IndicatorBotCommons") - let USER_BOT_MODULE = require("./IndicatorBotMarket") + let USER_BOT_MODULE = require("./IndicatorBot") - usertBot = USER_BOT_MODULE.newIndicatorBotMarket(bot, logger, COMMONS_MODULE, UTILITIES, FILE_STORAGE); - usertBot.initialize(processConfig, callBackFunction); + botInstance = USER_BOT_MODULE.newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE); + botInstance.initialize(callBackFunction); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); @@ -71,7 +70,7 @@ dataFiles = undefined statusDependencies = undefined dataDependencies = undefined - usertBot = undefined + botInstance = undefined processConfig = undefined thisObject = undefined } @@ -256,12 +255,14 @@ const timePeriod = global.marketFilesPeriods[n][0]; const outputPeriodLabel = global.marketFilesPeriods[n][1]; - usertBot.start( + botInstance.start( dataFiles, timePeriod, outputPeriodLabel, + undefined, contextVariables.dateBeginOfMarket, contextVariables.dateEndOfMarket, + undefined, onBotFinished); function onBotFinished(err) { diff --git a/Project.njsproj b/Project.njsproj index 5128f194e..bfe531e65 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -25,17 +25,9 @@ Code - - Code - - - Code - + - - Code - Code From a982dbb8fc50c4758957c8204de3d82c4400737b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 14 Nov 2019 14:30:00 +0100 Subject: [PATCH 18/38] MultiPeriod Daily Working --- MultiPeriodDaily.js | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index e9c5c10df..4eed634dd 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -127,12 +127,7 @@ We look first for the bot who knows the begining of the marke in order to get when the market starts. */ - let botWhoKnowsTheBeginingOfTheMarket = statusDependencies.nodeArray[processConfig.framework.startDate.takeItFromStatusDependency]; - - reportKey = botWhoKnowsTheBeginingOfTheMarket.devTeam + "-" + botWhoKnowsTheBeginingOfTheMarket.bot + "-" + botWhoKnowsTheBeginingOfTheMarket.process + "-" + "dataSet.V1"; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } - - statusReport = statusDependencies.statusReports.get(reportKey); + statusReport = statusDependencies.reportsByMainUtility.get("Market Starting Point") 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. "); @@ -146,7 +141,7 @@ return; } - thisReport = statusDependencies.statusReports.get(reportKey).file; + thisReport = statusReport.file; if (thisReport.lastFile === undefined) { logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); @@ -169,7 +164,7 @@ /* The ending date is fixed, we will end there. */ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> We have got a user defined endDate. -> endDate = " + processConfig.framework.endDate.fixedDate); } - contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate); + contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate); } else { @@ -177,12 +172,7 @@ Here we get the status report from the bot who knows which is the end of the market. */ - let botWhoKnowsTheEndOfTheMarket = statusDependencies.nodeArray[processConfig.framework.endDate.takeItFromStatusDependency]; - - reportKey = botWhoKnowsTheEndOfTheMarket.devTeam + "-" + botWhoKnowsTheEndOfTheMarket.bot + "-" + botWhoKnowsTheEndOfTheMarket.process + "-" + "dataSet.V1"; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } - - statusReport = statusDependencies.statusReports.get(reportKey); + statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point") 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. "); @@ -196,7 +186,7 @@ return; } - thisReport = statusDependencies.statusReports.get(reportKey).file; + thisReport = statusReport.file; if (thisReport.lastFile === undefined) { logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); From ea78106ade83bd20d5068d8fff02d66debdda61b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 14 Nov 2019 20:15:10 +0100 Subject: [PATCH 19/38] Processes with Framework dont need to have a configuration file or code in file, taken from UI --- IndicatorBotProcessMainLoop.js | 15 +- Root.js | 582 ++++++++++++++++++--------------- SensorBotProcessMainLoop.js | 9 +- TradingBotProcessMainLoop.js | 10 +- server.js | 4 - 5 files changed, 339 insertions(+), 281 deletions(-) diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index 76f8675ab..55a4ba13a 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -29,15 +29,21 @@ return thisObject; function initialize(pProcessConfig, callBackFunction) { - - /* This function is exactly the same in the 3 modules representing the 2 different bot types loops. */ - + try { if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } processConfig = pProcessConfig; - let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name + if (bot.definedByUI === true) { + /* The code of the bot is defined at the UI. No need to load a file with the code. */ + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + + /* This bot is not ready for taking its code from the UI, then we need to load it from its repo. */ + + let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName filePath += "/User.Bot.js" fileStorage.getTextFile(bot.devTeam, filePath, onBotDownloaded); @@ -47,6 +53,7 @@ if (err.result !== global.DEFAULT_OK_RESPONSE.result) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message); + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return; } diff --git a/Root.js b/Root.js index 19d741e01..65a883533 100644 --- a/Root.js +++ b/Root.js @@ -137,17 +137,75 @@ const SENSOR_BOT_MAIN_LOOP_MODULE = require('./SensorBotProcessMainLoop'); const TRADING_ENGINE_MAIN_LOOP_MODULE = require('./TradingBotProcessMainLoop'); - /* Loop through all the processes configured to be run by this Node.js Instance. */ - /* Now we will read the config of the bot from the path we obtained at the Task Server config. */ - let botConfig; + let processInstance = global.TASK_NODE.bot.processes[processIndex] - getBotConfig(); + /* Some very basic validations that we have all the information needed. */ + if (processInstance.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] start-> Process Instance without Reference Parent. Process Instance = " + JSON.stringify(processInstance)); + global.EXIT_NODE_PROCESS() + return + } - function getBotConfig() { + if (processInstance.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start-> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(processInstance.referenceParent)); + global.EXIT_NODE_PROCESS() + return + } + + if (processInstance.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start-> Process Definition not attached to a Bot Definition. Process Definition = " + JSON.stringify(processInstance.referenceParent)); + global.EXIT_NODE_PROCESS() + return + } + + if (processInstance.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start-> Bot Definition witn no codeName defined. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); + global.EXIT_NODE_PROCESS() + return + } + + if (processInstance.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start-> Bot Definition not attached to a Team. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); + global.EXIT_NODE_PROCESS() + return + } + + if (processInstance.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start-> Team witn no codeName defined. Team = " + JSON.stringify(processInstance.referenceParent.parentNode.parentNode)); + global.EXIT_NODE_PROCESS() + return + } + + let basicInfo = {} + + /* Simplifying the access to basic info */ + basicInfo.process = processInstance.referenceParent.code.codeName + basicInfo.bot = processInstance.referenceParent.parentNode.code.codeName + basicInfo.devTeam = processInstance.referenceParent.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + basicInfo.version = { + "major": 1, + "minor": 0 + } + basicInfo.dataSetVersion = "dataSet.V1" + + /* Here we will check if we need to load the configuration and code of the bot from a file or we will take that from the UI. */ + if (processInstance.referenceParent.code.framework !== undefined) { + botConfig = processInstance.referenceParent.parentNode.code + botConfig.definedByUI = true + bootingBot(processIndex) + return + } else { + getBotConfigFromFile(); + return + } + + function getBotConfigFromFile() { try { - console.log(logDisplace + "Root : [INFO] start -> getBotConfig -> Entering function. "); + console.log(logDisplace + "Root : [INFO] start -> getBotConfigFromFile -> Entering function. "); const FILE_STORAGE = require('./FileStorage.js'); let fileStorage = FILE_STORAGE.newFileStorage(); @@ -159,268 +217,250 @@ function onFileReceived(err, text) { if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); - console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> filePath = " + filePath); - console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> team = " + global.TASK_NODE.bot.code.team); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> filePath = " + filePath); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> team = " + global.TASK_NODE.bot.code.team); return; } try { botConfig = JSON.parse(text); botConfig.repo = global.TASK_NODE.bot.code.repo; - findProcess(processIndex); + bootingBot(processIndex); } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); return; } } } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> err = " + err.stack); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> err = " + err.stack); return; } } - function findProcess(processIndex) { + function bootingBot(processIndex) { try { - if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> Entering function. "); } + if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> Entering function. "); } - botConfig.process = global.TASK_NODE.bot.processes[processIndex].code.process; + botConfig.process = global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName botConfig.debug = {}; botConfig.processNode = global.TASK_NODE.bot.processes[processIndex] /* Loop Counter */ - - botConfig.loopCounter = 0; + botConfig.loopCounter = 0; /* File Path Root */ - - botConfig.filePathRoot = botConfig.devTeam + "/" + botConfig.codeName + "." + botConfig.version.major + "." + botConfig.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + botConfig.dataSetVersion; + botConfig.filePathRoot = basicInfo.devTeam + "/" + basicInfo.bot + "." + basicInfo.version.major + "." + basicInfo.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + basicInfo.dataSetVersion; /* Process Key */ - botConfig.processKey = global.TASK_NODE.bot.processes[processIndex].name + '-' + global.TASK_NODE.bot.processes[processIndex].type + '-' + global.TASK_NODE.bot.processes[processIndex].id - if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> filePathRoot = " + botConfig.filePathRoot); } + /* Bot Type */ + botConfig.type = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.type - /* Now we loop throug all the configured processes at each bots configuration until we find the one we are supposed to run at this Node.js process. */ + if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> filePathRoot = " + botConfig.filePathRoot); } + if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> Process found at the bot configuration file. -> process = " + global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName); } - let processFound = false; + let processConfig = global.TASK_NODE.bot.processes[processIndex].referenceParent.code - for (let i = 0; i < botConfig.processes.length; i++) { - - if (botConfig.processes[i].name === global.TASK_NODE.bot.processes[processIndex].code.process) { - - processFound = true; - if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> Process found at the bot configuration file. -> process = " + global.TASK_NODE.bot.processes[processIndex].code.process); } - - let processConfig = botConfig.processes[i]; - - if (processConfig.framework !== undefined) { - if (processConfig.framework.name === "Multi-Period-Daily" || processConfig.framework.name === "Multi-Period-Market" || processConfig.framework.name === "Multi-Period") { - processConfig.framework.startDate.resumeExecution = true; - if (processConfig.startMode.noTime !== undefined) { - if (processConfig.startMode.noTime.run === "true") { - if (processConfig.startMode.noTime.beginDatetime !== undefined) { - processConfig.framework.startDate.fixedDate = processConfig.startMode.noTime.beginDatetime; - processConfig.framework.startDate.resumeExecution = true; - } - } - } - if (processConfig.startMode.userDefined !== undefined) { - if (processConfig.startMode.userDefined.run === "true") { - if (processConfig.startMode.userDefined.beginDatetime !== undefined) { - processConfig.framework.startDate.fixedDate = processConfig.startMode.userDefined.beginDatetime; - processConfig.framework.startDate.resumeExecution = processConfig.startMode.userDefined.resumeExecution; - } - } + if (processConfig.framework !== undefined) { + if (processConfig.framework.name === "Multi-Period-Daily" || processConfig.framework.name === "Multi-Period-Market" || processConfig.framework.name === "Multi-Period") { + processConfig.framework.startDate.resumeExecution = true; + if (processConfig.startMode.noTime !== undefined) { + if (processConfig.startMode.noTime.run === "true") { + if (processConfig.startMode.noTime.beginDatetime !== undefined) { + processConfig.framework.startDate.fixedDate = processConfig.startMode.noTime.beginDatetime; + processConfig.framework.startDate.resumeExecution = true; } } } - - try { - - /* We test each type of start Mode to get what to run and how. */ - - if (processConfig.startMode.allMonths !== undefined) { - - if (processConfig.startMode.allMonths.run === "true") { - - if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> allMonths start mode detected. "); } - - for (let year = processConfig.startMode.allMonths.maxYear; year >= processConfig.startMode.allMonths.minYear; year--) { - - for (let month = 12; month > 0; month--) { - - let padMonth = pad(month, 2) - - let newInstanceBotConfig = JSON.parse(JSON.stringify(botConfig)); - - newInstanceBotConfig.debug = { - month: pad(month, 2), - year: pad(year, 4) - }; - - let timeDelay = Math.random() * 10 * 1000; // We introduce a short delay so as to not overload the machine. - setTimeout(startProcess, timeDelay); - - function startProcess() { - - if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> startProcess -> Ready to start process."); } - - switch (botConfig.type) { - case 'Sensor Bot': { - runSensorBot(newInstanceBotConfig, processConfig, padMonth, year); - break; - } - case 'Indicator Bot': { - runIndicatorBot(newInstanceBotConfig, processConfig, padMonth, year); - break; - } - default: { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); - } - } - } - } - } + if (processConfig.startMode.userDefined !== undefined) { + if (processConfig.startMode.userDefined.run === "true") { + if (processConfig.startMode.userDefined.beginDatetime !== undefined) { + processConfig.framework.startDate.fixedDate = processConfig.startMode.userDefined.beginDatetime; + processConfig.framework.startDate.resumeExecution = processConfig.startMode.userDefined.resumeExecution; } } - - if (processConfig.startMode.oneMonth !== undefined) { - - if (processConfig.startMode.oneMonth.run === "true") { - - if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> oneMonth start mode detected. "); } - - startProcess(); - - function startProcess() { - - if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> startProcess -> Ready to start process."); } - - let month = pad(processConfig.startMode.oneMonth.month, 2); - let year = processConfig.startMode.oneMonth.year; - - botConfig.debug = { - month: pad(month, 2), - year: pad(year, 4) - }; - - switch (botConfig.type) { - case 'Sensor Bot': { - runSensorBot(botConfig, processConfig, month, year); - break; - } - case 'Indicator Bot': { - runIndicatorBot(botConfig, processConfig, month, year); - break; - } - default: { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); - } - } - } - } - } - - if (processConfig.startMode.noTime !== undefined) { - - if (processConfig.startMode.noTime.run === "true") { - - let month = pad((new Date()).getUTCMonth() + 1, 2); - let year = (new Date()).getUTCFullYear(); - - if (processConfig.startMode.noTime.resumeExecution === true) { - botConfig.hasTheBotJustStarted = false; - } else { - botConfig.hasTheBotJustStarted = true; - } - - switch (botConfig.type) { - case 'Sensor Bot': { - runSensorBot(botConfig, processConfig, month, year); - break; - } - case 'Indicator Bot': { - runIndicatorBot(botConfig, processConfig, month, year); - break; - } - case 'Trading Bot': { - runTradingBot(botConfig, processConfig); - break; - } - default: { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); - } - } - } - } - - if (processConfig.startMode.fixedInterval !== undefined) { - - if (processConfig.startMode.fixedInterval.run === "true") { - - botConfig.runAtFixedInterval = true; - botConfig.fixedInterval = processConfig.startMode.fixedInterval.interval; - - let month = pad((new Date()).getUTCMonth() + 1, 2); - let year = (new Date()).getUTCFullYear(); - - switch (botConfig.type) { - case 'Sensor Bot': { - runSensorBot(botConfig, processConfig, month, year); - break; - } - default: { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); - } - } - } - } - - if (processConfig.startMode.userDefined !== undefined) { - - if (processConfig.startMode.userDefined.run === "true") { - - botConfig.startMode = "User Defined"; - console.log(logDisplace + "Root : [INFO] start -> findProcess -> Process found at the bot configuration file. -> Start Mode = " + botConfig.startMode); - - let month = pad((new Date()).getUTCMonth() + 1, 2); - let year = (new Date()).getUTCFullYear(); - - if (processConfig.startMode.userDefined.resumeExecution === true) { - botConfig.hasTheBotJustStarted = false; - } else { - botConfig.hasTheBotJustStarted = true; - } - - switch (botConfig.type) { - case 'Trading Bot': { - runTradingBot(botConfig, processConfig); - break; - } - default: { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); - } - } - } - } - - } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected exception. -> err = " + err.stack); } } } - if (processFound === false) { + try { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Process listed at the configuration file of Task Server not found at the configuration file of the bot."); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> process = " + global.TASK_NODE.bot.processes[processIndex].code.process); + /* We test each type of start Mode to get what to run and how. */ + if (processConfig.startMode.allMonths !== undefined) { + + if (processConfig.startMode.allMonths.run === "true") { + + if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> allMonths start mode detected. "); } + + for (let year = processConfig.startMode.allMonths.maxYear; year >= processConfig.startMode.allMonths.minYear; year--) { + + for (let month = 12; month > 0; month--) { + + let padMonth = pad(month, 2) + + let newInstanceBotConfig = JSON.parse(JSON.stringify(botConfig)); + + newInstanceBotConfig.debug = { + month: pad(month, 2), + year: pad(year, 4) + }; + + let timeDelay = Math.random() * 10 * 1000; // We introduce a short delay so as to not overload the machine. + setTimeout(startProcess, timeDelay); + + function startProcess() { + + if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> startProcess -> Ready to start process."); } + + switch (botConfig.type) { + case 'Sensor Bot': { + runSensorBot(newInstanceBotConfig, processConfig, padMonth, year); + break; + } + case 'Indicator Bot': { + runIndicatorBot(newInstanceBotConfig, processConfig, padMonth, year); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + } + } + } + + if (processConfig.startMode.oneMonth !== undefined) { + + if (processConfig.startMode.oneMonth.run === "true") { + + if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> oneMonth start mode detected. "); } + + startProcess(); + + function startProcess() { + + if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> startProcess -> Ready to start process."); } + + let month = pad(processConfig.startMode.oneMonth.month, 2); + let year = processConfig.startMode.oneMonth.year; + + botConfig.debug = { + month: pad(month, 2), + year: pad(year, 4) + }; + + switch (botConfig.type) { + case 'Sensor Bot': { + runSensorBot(botConfig, processConfig, month, year); + break; + } + case 'Indicator Bot': { + runIndicatorBot(botConfig, processConfig, month, year); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + } + + if (processConfig.startMode.noTime !== undefined) { + + if (processConfig.startMode.noTime.run === "true") { + + let month = pad((new Date()).getUTCMonth() + 1, 2); + let year = (new Date()).getUTCFullYear(); + + if (processConfig.startMode.noTime.resumeExecution === true) { + botConfig.hasTheBotJustStarted = false; + } else { + botConfig.hasTheBotJustStarted = true; + } + + switch (botConfig.type) { + case 'Sensor Bot': { + runSensorBot(botConfig, processConfig, month, year); + break; + } + case 'Indicator Bot': { + runIndicatorBot(botConfig, processConfig, month, year); + break; + } + case 'Trading Bot': { + runTradingBot(botConfig, processConfig); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + if (processConfig.startMode.fixedInterval !== undefined) { + + if (processConfig.startMode.fixedInterval.run === "true") { + + botConfig.runAtFixedInterval = true; + botConfig.fixedInterval = processConfig.startMode.fixedInterval.interval; + + let month = pad((new Date()).getUTCMonth() + 1, 2); + let year = (new Date()).getUTCFullYear(); + + switch (botConfig.type) { + case 'Sensor Bot': { + runSensorBot(botConfig, processConfig, month, year); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + if (processConfig.startMode.userDefined !== undefined) { + + if (processConfig.startMode.userDefined.run === "true") { + + botConfig.startMode = "User Defined"; + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> Process found at the bot configuration file. -> Start Mode = " + botConfig.startMode); + + let month = pad((new Date()).getUTCMonth() + 1, 2); + let year = (new Date()).getUTCFullYear(); + + if (processConfig.startMode.userDefined.resumeExecution === true) { + botConfig.hasTheBotJustStarted = false; + } else { + botConfig.hasTheBotJustStarted = true; + } + + switch (botConfig.type) { + case 'Trading Bot': { + runTradingBot(botConfig, processConfig); + break; + } + default: { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + } catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected exception. -> err = " + err.stack); } + function runSensorBot(pBotConfig, pProcessConfig, pMonth, pYear) { try { @@ -432,9 +472,9 @@ logger = DEBUG_MODULE.newDebugLog(); logger.bot = pBotConfig; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> Entering function."); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> pMonth = " + pMonth); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> pYear = " + pYear); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> pMonth = " + pMonth); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> pYear = " + pYear); } let extractionBotMainLoop = SENSOR_BOT_MAIN_LOOP_MODULE.newSensorBotProcessMainLoop(pBotConfig, logger); extractionBotMainLoop.initialize(pProcessConfig, onInitializeReady); @@ -458,31 +498,31 @@ if (err.result === global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); logger.persist(); } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bye."); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution was aborted."); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution was aborted."); logger.persist(); } setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> Bot will not be started. "); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> Bot will not be started. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> err = " + err.message); logger.persist(); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) @@ -490,7 +530,7 @@ } } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> err = " + err.stack); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> err = " + err.stack); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } @@ -506,9 +546,9 @@ logger = DEBUG_MODULE.newDebugLog(); logger.bot = pBotConfig; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> Entering function."); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pMonth = " + pMonth); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pYear = " + pYear); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> pMonth = " + pMonth); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> pYear = " + pYear); } let indicatorBotMainLoop = INDICATOR_BOT_MAIN_LOOP_MODULE.newIndicatorBotProcessMainLoop(pBotConfig, logger); indicatorBotMainLoop.initialize(pProcessConfig, onInitializeReady); @@ -532,36 +572,36 @@ if (err.result === global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); logger.persist(); } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bye."); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); logger.persist(); } setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> Failed to initialize the bot. "); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> err = " + err.message); logger.persist(); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> err = " + err.stack); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> err = " + err.stack); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } @@ -577,7 +617,7 @@ logger = DEBUG_MODULE.newDebugLog(); logger.bot = pBotConfig; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingBot -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> Entering function."); } let tradingBotMainLoop = TRADING_ENGINE_MAIN_LOOP_MODULE.newTradingBotProcessMainLoop(pBotConfig, logger); tradingBotMainLoop.initialize(pProcessConfig, onInitializeReady); @@ -598,42 +638,42 @@ if (err.result === global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); - logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); - console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId); + console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); logger.persist(); } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bye."); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); logger.persist(); } setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } else { - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> Failed to initialize the bot. "); - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> err = " + err.message); logger.persist(); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingBot -> err = " + err.stack); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> err = " + err.stack); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> findProcess -> err = " + err.stack); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> err = " + err.stack); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } diff --git a/SensorBotProcessMainLoop.js b/SensorBotProcessMainLoop.js index 3bd2fc6fd..29e6ab980 100644 --- a/SensorBotProcessMainLoop.js +++ b/SensorBotProcessMainLoop.js @@ -35,7 +35,13 @@ processConfig = pProcessConfig; - let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name; + if (bot.definedByUI === true) { + /* The code of the bot is defined at the UI. No need to load a file with the code. */ + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + + let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName filePath += "/User.Bot.js" fileStorage.getTextFile(bot.devTeam, filePath, onBotDownloaded); @@ -45,6 +51,7 @@ if (err.result !== global.DEFAULT_OK_RESPONSE.result) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message); + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return; } diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index af2787b80..0bd3e5899 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -40,9 +40,16 @@ if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } processConfig = pProcessConfig; + + if (bot.definedByUI === true) { + /* The code of the bot is defined at the UI. No need to load a file with the code. */ + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } + bot.resumeExecution = processConfig.framework.startDate.resumeExecution // We are inherating this from root, but from here we need it at bot - let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name + let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName filePath += "/User.Bot.js" fileStorage.getTextFile(bot.devTeam, filePath, onBotDownloaded); @@ -52,6 +59,7 @@ if (err.result !== global.DEFAULT_OK_RESPONSE.result) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message); + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return; } diff --git a/server.js b/server.js index 9697386e5..a46aa6830 100644 --- a/server.js +++ b/server.js @@ -192,10 +192,6 @@ function bootLoader() { let key = global.TASK_NODE.bot.code.team + "-" + global.TASK_NODE.bot.code.bot + "-" + code.process global.SYSTEM_EVENT_HANDLER.createEventHandler(key) - if (global.TASK_NODE.bot.code.repo === undefined) { - global.TASK_NODE.bot.code.repo = global.TASK_NODE.bot.code.bot + "-" + global.TASK_NODE.bot.type + "-Bot" - } - startRoot(processIndex); } } From d192e5e62d9249eabfb5283a6cd2dbb815a73dbb Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 15 Nov 2019 08:36:26 +0100 Subject: [PATCH 20/38] Receiving task definition by events --- Root.js | 26 +++++++++++--------------- server.js | 39 +++++++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/Root.js b/Root.js index 65a883533..86be2ba22 100644 --- a/Root.js +++ b/Root.js @@ -177,20 +177,6 @@ return } - let basicInfo = {} - - /* Simplifying the access to basic info */ - basicInfo.process = processInstance.referenceParent.code.codeName - basicInfo.bot = processInstance.referenceParent.parentNode.code.codeName - basicInfo.devTeam = processInstance.referenceParent.parentNode.parentNode.code.codeName - - /* This stuff is still hardcoded and unresolved. */ - basicInfo.version = { - "major": 1, - "minor": 0 - } - basicInfo.dataSetVersion = "dataSet.V1" - /* Here we will check if we need to load the configuration and code of the bot from a file or we will take that from the UI. */ if (processInstance.referenceParent.code.framework !== undefined) { botConfig = processInstance.referenceParent.parentNode.code @@ -248,11 +234,21 @@ botConfig.debug = {}; botConfig.processNode = global.TASK_NODE.bot.processes[processIndex] + /* Simplifying the access to basic info */ + botConfig.devTeam = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + botConfig.version = { + "major": 1, + "minor": 0 + } + botConfig.dataSetVersion = "dataSet.V1" + /* Loop Counter */ botConfig.loopCounter = 0; /* File Path Root */ - botConfig.filePathRoot = basicInfo.devTeam + "/" + basicInfo.bot + "." + basicInfo.version.major + "." + basicInfo.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + basicInfo.dataSetVersion; + botConfig.filePathRoot = botConfig.devTeam + "/" + botConfig.bot + "." + botConfig.version.major + "." + botConfig.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + botConfig.dataSetVersion; /* Process Key */ botConfig.processKey = global.TASK_NODE.bot.processes[processIndex].name + '-' + global.TASK_NODE.bot.processes[processIndex].type + '-' + global.TASK_NODE.bot.processes[processIndex].id diff --git a/server.js b/server.js index a46aa6830..3de7dd0ca 100644 --- a/server.js +++ b/server.js @@ -65,7 +65,7 @@ process.on('message', message => { */ console.log('[INFO] Task Server -> server -> process.on -> Executing order received from Task Manager to Stop this Task. Nodejs process will be exited in less than 1 minute.') setTimeout(global.EXIT_NODE_PROCESS, 60000); - } + } }); global.EXIT_NODE_PROCESS = function exitProcess() { @@ -117,7 +117,7 @@ We read the first string sent as an argument when the process was created by the of this Task and know exactly what to run within this server instance. */ -global.TASK_NODE = process.argv[2] +let taskId = process.argv[2] // reading what comes as an argument of the nodejs process. /* Setting up the global Event Handler */ @@ -128,22 +128,33 @@ global.SYSTEM_EVENT_HANDLER.initialize('Task Server', preLoader) global.STOP_TASK_GRACEFULLY = false; function preLoader() { - if (global.TASK_NODE !== undefined) { - /* The Task Manager sent the info via a process argument. */ + if (taskId !== undefined) { + /* The Task Manager sent the info via a process argument. In this case we listen to an event with the Task Info that should be emitted at the UI */ try { - global.TASK_NODE = JSON.parse(global.TASK_NODE) - console.log('[INFO] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE)) + console.log('[Info] Task Server -> server -> preLoader -> Listening to starting event -> key = ' + 'Task Server - ' + taskId) + global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server - ' + taskId, 'Run Task', undefined, undefined, undefined, eventReceived) + function eventReceived(message) { + global.TASK_NODE = message + global.TASK_NODE = JSON.parse(message.event.definition) + bootLoader() + } } catch (err) { - console.log('[ERROR] Task Server -> server -> preLoader -> vglobal.TASK_NODE -> ' + err.stack) + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack) + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE)) } - bootLoader() } - else { /* This process was started not by the Task Manager, but independently (most likely for debugging purposes). In this case we listen to an event with the Task Info */ - console.log('[INFO] Task Server -> server -> preLoader -> Waiting for event to start debugging...') - global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server', 'Debug Task Started', undefined, undefined, undefined, startDebugging) - function startDebugging(message) { - global.TASK_NODE = JSON.parse(message.event.definition) - bootLoader() + else { /* This process was started not by the Task Manager, but independently (most likely for debugging purposes). In this case we listen to an event with the Task Info that should be emitted at the UI */ + try { + console.log('[INFO] Task Server -> server -> preLoader -> Waiting for event to start debugging...') + global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server', 'Debug Task Started', undefined, undefined, undefined, startDebugging) + function startDebugging(message) { + global.TASK_NODE = message + global.TASK_NODE = JSON.parse(message.event.definition) + bootLoader() + } + } catch (err) { + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack) + console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE)) } } } From 7ea6db1017e21bfae5d381eb01059285314a1eb0 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 15 Nov 2019 09:08:38 +0100 Subject: [PATCH 21/38] Bug fixed --- Root.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Root.js b/Root.js index 86be2ba22..e7e21d57d 100644 --- a/Root.js +++ b/Root.js @@ -248,7 +248,7 @@ botConfig.loopCounter = 0; /* File Path Root */ - botConfig.filePathRoot = botConfig.devTeam + "/" + botConfig.bot + "." + botConfig.version.major + "." + botConfig.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + botConfig.dataSetVersion; + botConfig.filePathRoot = botConfig.devTeam + "/" + botConfig.codeName + "." + botConfig.version.major + "." + botConfig.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + botConfig.dataSetVersion; /* Process Key */ botConfig.processKey = global.TASK_NODE.bot.processes[processIndex].name + '-' + global.TASK_NODE.bot.processes[processIndex].type + '-' + global.TASK_NODE.bot.processes[processIndex].id From 5890b62ec20a714a077a0b4e5a216a54467dbd96 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 15 Nov 2019 10:23:27 +0100 Subject: [PATCH 22/38] Bug fixed writting data range --- MultiPeriod.js | 14 ++++++-------- MultiPeriodDaily.js | 12 +++++------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/MultiPeriod.js b/MultiPeriod.js index a2707c4e3..01269d5a4 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -959,21 +959,19 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } - let productIndex = 0; - productLoopBody(); + let outputDatasetIndex = -1; + controlLoop(); function productLoopBody() { - - let product = bot.products[productIndex]; - - writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodProcessDatetime, product, currentOutputPeriodName, controlLoop); + let folderName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; + writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, folderName, controlLoop); } function controlLoop() { - productIndex++; + outputDatasetIndex++; - if (productIndex < bot.products.length) { + if (outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length) { productLoopBody(); } else { callBack(global.DEFAULT_OK_RESPONSE); diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 4eed634dd..d10d0c40b 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -692,21 +692,19 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); } - let productIndex = 0; - productLoopBody(); + let outputDatasetIndex = -1; + controlLoop(); function productLoopBody() { - - let folderName = bot.products[productIndex].codeName; - + let folderName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, folderName, controlLoop); } function controlLoop() { - productIndex++; + outputDatasetIndex++; - if (productIndex < bot.products.length) { + if (outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length) { productLoopBody(); } else { callBack(global.DEFAULT_OK_RESPONSE); From 5b5a752e79fb4632ac2d137ac032c90bebb5a5e4 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 15 Nov 2019 14:30:26 +0100 Subject: [PATCH 23/38] Log Auto Mantainance Implemented --- DebugLog.js | 21 +++++++------------ FileStorage.js | 57 +++++++++++++++++++++++++++++++++++++++++++++++++- Root.js | 4 ++++ 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/DebugLog.js b/DebugLog.js index 87137a604..a51b02b0b 100644 --- a/DebugLog.js +++ b/DebugLog.js @@ -21,9 +21,7 @@ exports.newDebugLog = function newDebugLog() { }; let accumulatedLog = "["; - let disableLogging; - let internalLoopCounter = -1; return thisObject; @@ -80,35 +78,32 @@ exports.newDebugLog = function newDebugLog() { } let fileName; - if (internalLoopCounter >= 0) { - fileName = "Loop." + pad(thisObject.bot.loopCounter, 8) + "." + pad(internalLoopCounter, 4) + ".json"; - } else { - fileName = "Loop." + pad(thisObject.bot.loopCounter, 8) + ".json"; - } writeLog(); + /* This is the implementation of the mechanism to auto-mantain logs. */ + thisObject.bot.LOGS_TO_DELETE_QUEUE.push(filePath + '/' + fileName) + if (thisObject.bot.LOGS_TO_DELETE_QUEUE.length > thisObject.bot.DELETE_QUEUE_SIZE) { + let fileToDelete = thisObject.bot.LOGS_TO_DELETE_QUEUE[0] + thisObject.bot.LOGS_TO_DELETE_QUEUE.splice(0,1) + fileStorage.deleteTextFile(thisObject.bot.devTeam, fileToDelete); + } + function writeLog() { fileStorage.createTextFile(thisObject.bot.devTeam, filePath + '/' + fileName, contentToPersist + '\r\n' + "]", onFileCreated); - function onFileCreated(err) { - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - console.log("[ERROR] DebugLog -> persist -> onInizialized -> onFileCreated -> err = "+ err.message); - setTimeout(writeLog, 10000); // Lets retry until we make it. return; } - contentToPersist = ""; - //thisObject = {}; } } } catch (err) { diff --git a/FileStorage.js b/FileStorage.js index 5f5534c3d..ee784f5fa 100644 --- a/FileStorage.js +++ b/FileStorage.js @@ -19,7 +19,8 @@ exports.newFileStorage = function newFileStorage(logger) { let thisObject = { getTextFile: getTextFile, - createTextFile: createTextFile + createTextFile: createTextFile, + deleteTextFile: deleteTextFile } return thisObject @@ -207,6 +208,60 @@ exports.newFileStorage = function newFileStorage(logger) { } } + function deleteTextFile(container, filePath, callBackFunction) { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> Entering Function.') + + container = container.toLowerCase() + + /* Choose path for either logs or data */ + let fileLocation + if (filePath.indexOf("/Logs/") > 0) { + fileLocation = process.env.LOG_PATH + '/' + container + '/' + filePath + } else { + fileLocation = process.env.STORAGE_PATH + '/' + container + '/' + filePath + } + + try { + + logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> fileLocation: ' + fileLocation) + + const fs = require('fs') + fs.unlink(fileLocation, onUnlinked) + + function onUnlinked(err) { + if (err) { + if (callBackFunction !== undefined) { + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } else { + if (callBackFunction !== undefined) { + callBackFunction(global.DEFAULT_OK_RESPONSE) + } + } + } + } catch (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> Error writing file -> err = ' + err.stack) + if (callBackFunction !== undefined) { + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } + + function retry() { + if (currentRetryWriteTextFile < MAX_RETRY) { + currentRetryWriteTextFile++ + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> retry -> Will try to write the file again -> Retry #: ' + currentRetryWriteTextFile) + createTextFile(container, filePath, fileContent, callBackFunction) + } else { + currentRetryWriteTextFile = 0 + logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> Max retries reached writting a file. Giving up.') + logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> file = ' + fileLocation) + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } + } + /* Function to create folders of missing folders at any path. */ function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) { diff --git a/Root.js b/Root.js index e7e21d57d..263742304 100644 --- a/Root.js +++ b/Root.js @@ -234,6 +234,10 @@ botConfig.debug = {}; botConfig.processNode = global.TASK_NODE.bot.processes[processIndex] + /* Logs Mantainance Stuff */ + botConfig.LOGS_TO_DELETE_QUEUE = [] + botConfig.DELETE_QUEUE_SIZE = 10 // This number represents how many log files can be at the queue at any point in time, which means how many logs are not still deleted. + /* Simplifying the access to basic info */ botConfig.devTeam = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName From ce1264dd3922d352cadc678575427e92129dcaeb Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 16 Nov 2019 11:08:27 +0100 Subject: [PATCH 24/38] Bug Fixes to make Trading Bots start again --- ProcessExecutionEvents.js | 13 +++++++++---- Root.js | 17 ++++++++++------- Session.js | 2 -- TradingBotProcessMainLoop.js | 2 +- server.js | 2 +- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ProcessExecutionEvents.js b/ProcessExecutionEvents.js index 47f131ab3..8ff8c2f54 100644 --- a/ProcessExecutionEvents.js +++ b/ProcessExecutionEvents.js @@ -22,6 +22,7 @@ let month; let year; let currentProcessKey + let datetimeForLogs = '' return thisObject; @@ -33,6 +34,10 @@ month = pMonth; year = pYear; + if (month && year) { + datetimeForLogs = "Month: " + month + " Year: " + year + } + let name = 'Not Depends on any Process' if (bot.processNode.referenceParent !== undefined) { @@ -48,7 +53,7 @@ if (processThisDependsOn.code.monthlyInstances === true) { logger.fileName = MODULE_NAME + "." + name + "." + year + "." + month; } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> " + currentProcessKey + "Month: " + month + " Year: " + year + " depends on " + name); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> " + currentProcessKey + datetimeForLogs + " depends on " + name); } } } } @@ -107,7 +112,7 @@ global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started') global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> " + currentProcessKey + " Process Execution Started because " + key + " Month: " + month + " Year: " + year + " Finished."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> " + currentProcessKey + " Process Execution Started because " + key + datetimeForLogs + " Finished."); } } callBackFunction(message.event.err); @@ -121,7 +126,7 @@ global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started') global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> " + currentProcessKey + " Process Execution Started Month: " + month + " Year: " + year); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> " + currentProcessKey + " Process Execution Started " + datetimeForLogs); } callBackFunction(global.DEFAULT_OK_RESPONSE); } @@ -144,7 +149,7 @@ global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Finished') global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Finished', event) - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> " + currentProcessKey + " Process Execution Finished Month: " + month + " Year: " + year); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> " + currentProcessKey + " Process Execution Finished " + datetimeForLogs); } callBackFunction(global.DEFAULT_OK_RESPONSE); diff --git a/Root.js b/Root.js index 263742304..bd73471d9 100644 --- a/Root.js +++ b/Root.js @@ -142,43 +142,46 @@ /* Some very basic validations that we have all the information needed. */ if (processInstance.referenceParent === undefined) { - logger.write(MODULE_NAME, "[ERROR] start-> Process Instance without Reference Parent. Process Instance = " + JSON.stringify(processInstance)); + console.log(logDisplace + "Root : [INFO] start -> Process Instance without Reference Parent. Process Instance = " + JSON.stringify(processInstance)); global.EXIT_NODE_PROCESS() return } if (processInstance.referenceParent.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start-> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(processInstance.referenceParent)); + console.log(logDisplace + "Root : [INFO] start -> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(processInstance.referenceParent)); global.EXIT_NODE_PROCESS() return } if (processInstance.referenceParent.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] start-> Process Definition not attached to a Bot Definition. Process Definition = " + JSON.stringify(processInstance.referenceParent)); + console.log(logDisplace + "Root : [INFO] start -> Process Definition not attached to a Bot Definition. Process Definition = " + JSON.stringify(processInstance.referenceParent)); global.EXIT_NODE_PROCESS() return } if (processInstance.referenceParent.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start-> Bot Definition witn no codeName defined. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); + console.log(logDisplace + "Root : [INFO] start -> Bot Definition witn no codeName defined. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); global.EXIT_NODE_PROCESS() return } if (processInstance.referenceParent.parentNode.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] start-> Bot Definition not attached to a Team. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); + console.log(logDisplace + "Root : [INFO] start -> Bot Definition not attached to a Team. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); global.EXIT_NODE_PROCESS() return } if (processInstance.referenceParent.parentNode.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start-> Team witn no codeName defined. Team = " + JSON.stringify(processInstance.referenceParent.parentNode.parentNode)); + console.log(logDisplace + "Root : [INFO] start -> Team witn no codeName defined. Team = " + JSON.stringify(processInstance.referenceParent.parentNode.parentNode)); global.EXIT_NODE_PROCESS() return } /* Here we will check if we need to load the configuration and code of the bot from a file or we will take that from the UI. */ - if (processInstance.referenceParent.code.framework !== undefined) { + if ( + processInstance.referenceParent.code.framework !== undefined && + (processInstance.referenceParent.code.framework.name === 'Multi-Period-Market' || processInstance.referenceParent.code.framework.name === 'Multi-Period-Daily') + ) { botConfig = processInstance.referenceParent.parentNode.code botConfig.definedByUI = true bootingBot(processIndex) diff --git a/Session.js b/Session.js index 9a34d5899..6c791f314 100644 --- a/Session.js +++ b/Session.js @@ -45,8 +45,6 @@ function runSession(message) { - console.log(message.event.definition) - /* We are going to run the Definition comming at the event. */ bot.DEFINITION = JSON.parse(message.event.definition) bot.SESSION = JSON.parse(message.event.session) diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index 0bd3e5899..cb321b0ca 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -204,7 +204,7 @@ processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) - processExecutionEvents.initialize(pMonth, pYear, onInizialized); + processExecutionEvents.initialize(undefined, undefined, onInizialized); function onInizialized(err) { diff --git a/server.js b/server.js index 3de7dd0ca..5c371209c 100644 --- a/server.js +++ b/server.js @@ -131,7 +131,7 @@ function preLoader() { if (taskId !== undefined) { /* The Task Manager sent the info via a process argument. In this case we listen to an event with the Task Info that should be emitted at the UI */ try { - console.log('[Info] Task Server -> server -> preLoader -> Listening to starting event -> key = ' + 'Task Server - ' + taskId) + console.log('[INFO] Task Server -> server -> preLoader -> Listening to starting event -> key = ' + 'Task Server - ' + taskId) global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server - ' + taskId, 'Run Task', undefined, undefined, undefined, eventReceived) function eventReceived(message) { global.TASK_NODE = message From 3527b09d229bfb209d2b03482865dc0c60b6bffb Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 16 Nov 2019 16:51:43 +0100 Subject: [PATCH 25/38] Trading Bot Running again after Reading stuff from UI --- MultiPeriod.js | 26 +++++++++----------------- MultiPeriodDaily.js | 15 ++++++--------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/MultiPeriod.js b/MultiPeriod.js index 01269d5a4..9d40e4433 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -125,12 +125,7 @@ Here we get the status report from the bot who knows which is the end of the market. */ - let botWhoKnowsTheEndOfTheMarket = statusDependencies.nodeArray[processConfig.framework.endDate.takeItFromStatusDependency]; - - reportKey = botWhoKnowsTheEndOfTheMarket.devTeam + "-" + botWhoKnowsTheEndOfTheMarket.bot + "-" + botWhoKnowsTheEndOfTheMarket.process + "-" + "dataSet.V1"; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } - - statusReport = statusDependencies.statusReports.get(reportKey); + statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point") 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. "); @@ -144,7 +139,7 @@ return; } - thisReport = statusDependencies.statusReports.get(reportKey).file; + thisReport = statusReport.file; if (thisReport.lastFile === undefined) { logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey); @@ -174,10 +169,7 @@ /* Finally we get our own Status Report. */ - reportKey = bot.devTeam + "-" + bot.codeName + "-" + "Multi-Period" + "-" + "dataSet.V1"; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } - - statusReport = statusDependencies.statusReports.get(reportKey); + statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") 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. "); @@ -191,7 +183,7 @@ return; } - thisReport = statusDependencies.statusReports.get(reportKey).file; + thisReport = statusReport.file; if (thisReport.lastFile !== undefined) { @@ -963,8 +955,8 @@ controlLoop(); function productLoopBody() { - let folderName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; - writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, folderName, controlLoop); + let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; + writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodProcessDatetime, productCodeName, currentOutputPeriodName, controlLoop); } function controlLoop() { @@ -985,7 +977,7 @@ } - function writeDataRange(pBegin, pEnd, product, currentOutputPeriodName, callBack) { + function writeDataRange(pBegin, pEnd, productCodeName, currentOutputPeriodName, callBack) { try { @@ -999,7 +991,7 @@ let fileContent = JSON.stringify(dataRange); let fileName = '/Data.Range.' + market.assetA + '_' + market.assetB + '.json'; - let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + product.codeName + "/" + 'Multi-Period-Daily' + fileName; + let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName; fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); @@ -1017,7 +1009,7 @@ logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent); } - let key = bot.devTeam + "-" + bot.codeName + "-" + product.codeName + "-" + currentOutputPeriodName + let key = bot.devTeam + "-" + bot.codeName + "-" + productCodeName + "-" + currentOutputPeriodName let event = { dateRange: dataRange } diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index d10d0c40b..fd00ee330 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -205,10 +205,7 @@ /* Finally we get our own Status Report. */ - reportKey = bot.devTeam + "-" + bot.codeName + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1"; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); } - - statusReport = statusDependencies.statusReports.get(reportKey); + statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") 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. "); @@ -222,7 +219,7 @@ return; } - thisReport = statusDependencies.statusReports.get(reportKey).file; + thisReport = statusReport.file; if (thisReport.lastFile !== undefined) { @@ -696,8 +693,8 @@ controlLoop(); function productLoopBody() { - let folderName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; - writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, folderName, controlLoop); + let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName; + writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, productCodeName, controlLoop); } function controlLoop() { @@ -718,7 +715,7 @@ } - function writeDataRange(pBegin, pEnd, pProductFolder, callBack) { + function writeDataRange(pBegin, pEnd, productCodeName, callBack) { try { @@ -732,7 +729,7 @@ let fileContent = JSON.stringify(dataRange); let fileName = '/Data.Range.' + market.assetA + '_' + market.assetB + '.json'; - let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process + fileName; + let filePath = bot.filePathRoot + "/Output/" + productCodeName + "/" + bot.process + fileName; fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); From db6db6409ea7edc219ab1a44c537ac0b980604e4 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 19 Nov 2019 13:13:02 +0100 Subject: [PATCH 26/38] Trading Bot introduced, and Indicator bot splitted into Indicator and commons. --- Commons.js | 460 +++++++++++++++++++++++++++++++++ IndicatorBot.js | 451 ++------------------------------ IndicatorBotProcessMainLoop.js | 4 +- MultiPeriodDaily.js | 4 +- MultiPeriodMarket.js | 4 +- Project.njsproj | 6 + TradingBot.js | 166 ++++++++++++ 7 files changed, 656 insertions(+), 439 deletions(-) create mode 100644 Commons.js create mode 100644 TradingBot.js diff --git a/Commons.js b/Commons.js new file mode 100644 index 000000000..2a52f8c6f --- /dev/null +++ b/Commons.js @@ -0,0 +1,460 @@ +exports.newCommons = function newCommons(bot, logger, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "Commons"; + + let thisObject = { + validateDataDependencies: validateDataDependencies, + validateOutputDatasets: validateOutputDatasets, + inflateDatafiles: inflateDatafiles, + dataBuildingProcedure: dataBuildingProcedure, + calculationsProcedure: calculationsProcedure, + generateFileContent: generateFileContent, + writeFile: writeFile + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + return thisObject; + + function validateDataDependencies(dataDependencies, callBackFunction) { + for (let i = 0; i < dataDependencies.length; i++) { + + let dataDependencyNode = dataDependencies[i].referenceParent + + /* Basic validations to see if we have everything we need. */ + if (dataDependencyNode.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataDependencyNode.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (dataDependencyNode.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + return true + } + + function validateOutputDatasets(outputDatasets, callBackFunction) { + for (let i = 0; i < outputDatasets.length; i++) { + let outputDatasetNode = outputDatasets[i] + + if (outputDatasetNode.referenceParent === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Output Dataset without Reference Parent. Output Dataset = " + JSON.stringify(outputDatasetNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.parentNode.dataBuilding === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + if (outputDatasetNode.referenceParent.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot witn no codeName defined. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Team. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Team witn no codeName defined. Team = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode.parentNode)); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + return true + } + + function inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod) { + /* + For each dataDependencyNode in our data dependencies, we should have a dataFile containing the records needed as an imput for this process. + What we need to do first is transform those records into JSON objects that can be used by user-defined formulas. + The first step does that but with the not calculated properties, the second step adds the calculated properties. + */ + for (let i = 0; i < dataDependencies.length; i++) { + + let dataFile = dataFiles[i] + let jsonData // Datafile converted into Json objects + let inputData // Includes calculated properties + let singularVariableName // name of the variable for this product + let recordDefinition + + let dataDependencyNode = dataDependencies[i].referenceParent + + recordDefinition = dataDependencyNode.parentNode.record + singularVariableName = dataDependencyNode.parentNode.code.singularVariableName + /* Transform the raw data into JSON objects */ + jsonData = jsonifyDataFile(dataFile, recordDefinition) + /* Add the calculated properties */ + if (dataDependencyNode.parentNode.calculations !== undefined) { + inputData = calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.parentNode.calculations, singularVariableName, timePeriod) + } else { + inputData = jsonData + } + products[dataDependencyNode.parentNode.code.pluralVariableName] = inputData + + /* The main dependency is defined as the first dependency processed. */ + if (mainDependency.records === undefined) { + mainDependency.records = inputData + } + } + } + + function jsonifyDataFile(dataFile, recordDefinition) { + + /* + This function has as an input the raw data on files and creates with it an array of JSON objects + with not calculated properties for later being consumed by Formulas + */ + + let jsonifiedArray = [] + + for (let i = 0; i < dataFile.length; i++) { + + let record = {} + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + record[property.code.codeName] = dataFile[i][j] + } + } + + jsonifiedArray.push(record); + } + + return jsonifiedArray + } + + function calculationsProcedure(jsonArray, recordDefinition, calculationsProcedure, variableName, timePeriod) { + + /* + This function has as an input an array of JSON objects, and it adds calculated properties to + complete the set of properties that will be available for Formulas. + */ + + let system = { // These are the available system variables to be used in User Code and Formulas + timePeriod: timePeriod, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] + + /* This is Initialization Code */ + if (calculationsProcedure.initialization !== undefined) { + if (calculationsProcedure.initialization.code !== undefined) { + try { + eval(calculationsProcedure.initialization.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = " + calculationsProcedure.initialization.code.code); + throw ("Error Executing User Code.") + } + } + } + + /* This is Initialization Code */ + if (calculationsProcedure.loop !== undefined) { + if (calculationsProcedure.loop.code !== undefined) { + for (let index = 0; index < jsonArray.length; index++) { + + let product = {} + product[variableName] = jsonArray[index] + + /* This is Loop Code */ + try { + eval(calculationsProcedure.loop.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = " + calculationsProcedure.loop.code.code); + throw ("Error Executing User Code.") + } + + /* For each calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated === true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + try { + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + } + } + } + } + + /* Adding the new element to the resulting array */ + results.push(product[variableName]); + } + } + } + return results + } + + function dataBuildingProcedure(products, mainDependency, recordDefinition, dataBuildingProcedure, variableName, timePeriod, resultsWithIrregularPeriods) { + + /* + This function has as an input the products object, with all the information + of all products calculated so far by the process. Based on that information + the function will evaluate user supplied code and formulas in order to build + a new set of information. + */ + + let system = { // These are the available system variables to be used in User Code and Formulas + timePeriod: timePeriod, + ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS + } + let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. + let results = [] + + /* This is Initialization Code */ + if (dataBuildingProcedure.initialization !== undefined) { + if (dataBuildingProcedure.initialization.code !== undefined) { + try { + eval(dataBuildingProcedure.initialization.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Code = " + dataBuildingProcedure.initialization.code.code); + throw ("Error Executing User Code.") + } + } + } + + /* This is Initialization Code */ + if (dataBuildingProcedure.loop !== undefined) { + if (dataBuildingProcedure.loop.code !== undefined) { + for (let index = 0; index < mainDependency.records.length; index++) { + + let record = { + previous: mainDependency.records[index - 1], + current: mainDependency.records[index], + next: mainDependency.records[index + 1] + } + + let product = {} + product[variableName] = {} + + /* This is Loop Code */ + try { + eval(dataBuildingProcedure.loop.code.code) + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Code = " + dataBuildingProcedure.loop.code.code); + throw ("Error Executing User Code.") + } + + /* For each non-calculated property we apply its formula*/ + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + if (property.formula !== undefined) { + if (property.formula.code !== undefined) { + try { + let newValue = eval(property.formula.code) + let currentRecord = product[variableName] + currentRecord[property.code.codeName] = newValue + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) + logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); + throw ("Error Executing User Code.") + } + } + } + } + } + + /* If within the code the new data structure was not yet pushed to the results array, we do it */ + if (resultsWithIrregularPeriods !== true) { + /* Adding the new element to the resulting array */ + results.push(product[variableName]); + } + } + } + } + return results + } + + function generateFileContent(records, recordDefinition, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> generateFileContent -> Entering function."); } + + let fileContent = ""; + let recordSeparator = ""; + + for (let i = 0; i < records.length; i++) { + let record = records[i]; + + if (processingDailyFiles === true) { + + if (resultsWithIrregularPeriods === true) { + /* + Here we have an special problem that occurs when an object spans several time peridos. If not taken care of + it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of + its properties. + + To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it + at the next day, in whole, even if it starts in the previous day. + */ + + let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + + if (record.end < currentDay.valueOf() - 1) { continue; } + if (record.end === lastInstantOdDay) { continue; } + } else { + /* + The normal situation is that objects are respecting the time size of its main dependency, in which case we only + care of not adding objects of the previous day we are currently processing (while on daily files) + */ + if (record.end < currentDay.valueOf()) { continue; } + } + } + + fileContent = fileContent + recordSeparator + '[' + let propertySeparator = "" + for (let j = 0; j < recordDefinition.properties.length; j++) { + let property = recordDefinition.properties[j] + if (property.code.isCalculated !== true) { + fileContent = fileContent + propertySeparator + if (property.code.isNumeric !== true) { + fileContent = fileContent + '"' + } + fileContent = fileContent + record[property.code.codeName] + if (property.code.isNumeric !== true) { + fileContent = fileContent + '"' + } + if (propertySeparator === "") { propertySeparator = ","; } + } + } + fileContent = fileContent + ']' + if (recordSeparator === "") { recordSeparator = ","; } + } + fileContent = "[" + fileContent + "]"; + return fileContent + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> generateFileContent -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, outputPeriodLabel, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> Entering function."); } + + let market = global.MARKET; + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + let dateForPath = '' + + if (processingDailyFiles === true) { + dateForPath = "/" + currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + } + + let filePathRoot = contextSummary.devTeam + "/" + contextSummary.bot + "." + contextSummary.botVersion.major + "." + contextSummary.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + contextSummary.dataSetVersion; + let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + outputPeriodLabel + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(contextSummary.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + anotherFileWritten(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +} + diff --git a/IndicatorBot.js b/IndicatorBot.js index ab38cd4c0..117d8fe30 100644 --- a/IndicatorBot.js +++ b/IndicatorBot.js @@ -6,7 +6,7 @@ const MODULE_NAME = "Indicator Bot"; - thisObject = { + let thisObject = { initialize: initialize, start: start }; @@ -14,6 +14,9 @@ let utilities = UTILITIES.newCloudUtilities(logger); let fileStorage = FILE_STORAGE.newFileStorage(logger); + const COMMONS = require('./Commons.js'); + let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE); + return thisObject; function initialize(callBackFunction) { @@ -33,15 +36,14 @@ } } - function start(dataFiles, timePeriod, outputPeriodLabel, currentDay, startDate, endDate, interExecutionMemory, callBackFunction) { + function start(dataFiles, timePeriod, outputPeriodLabel, currentDay, interExecutionMemory, callBackFunction) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } - let market = global.MARKET; let products = {} - let mainDependency + let mainDependency = {} let processingDailyFiles if (currentDay !== undefined) { @@ -50,60 +52,17 @@ processingDailyFiles = false } - /* The first phase is about transforming the inputs into a format that can be used to apply the user defined code. */ - let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies - for (let i = 0; i < dataDependencies.length; i++) { + /* The first phase here is about checking that we have everything we need at the definition level. */ + let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies + if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) { return } - let dataDependencyNode = dataDependencies[i].referenceParent - let dataFile = dataFiles[i] - let jsonData // Datafile converted into Json objects - let inputData // Includes calculated properties - let singularVariableName // name of the variable for this product - let recordDefinition - - /* - For each dataDependencyNode in our data dependencies, we should have da dataFile containing the records needed as an imput for this process. - What we need to do first is transform those records into JSON objects that can be used by user-defined formulas. - The first step does that but with the not calculated properties, the second step adds the calculated properties. - */ - - /* Basic validations to see if we have everything we need. */ - if (dataDependencyNode.parentNode.code.singularVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - if (dataDependencyNode.parentNode.code.pluralVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - if (dataDependencyNode.parentNode.record === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - - recordDefinition = dataDependencyNode.parentNode.record - singularVariableName = dataDependencyNode.parentNode.code.singularVariableName - /* Transform the raw data into JSON objects */ - jsonData = jsonifyDataFile(dataFile, recordDefinition) - /* Add the calculated properties */ - if (dataDependencyNode.parentNode.calculations !== undefined) { - inputData = calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.parentNode.calculations, singularVariableName, timePeriod) - } else { - inputData = jsonData - } - products[dataDependencyNode.parentNode.code.pluralVariableName] = inputData - - /* The main dependency is defined as the first dependency processed. */ - if (mainDependency === undefined) { - mainDependency = inputData - } - } - - /* During the second phase, we need to generate the data of the different products this process produces an output */ let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets + if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return } + + /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */ + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod) + + /* During the third phase, we need to generate the data of the different products this process produces an output */ for (let i = 0; i < outputDatasets.length; i++) { let outputDatasetNode = outputDatasets[i] @@ -118,33 +77,6 @@ For each outputDatasetNode in our process output, we will build the information based on our input products. */ - /* Basic validations to see if we have everything we need. */ - if (outputDatasetNode.referenceParent === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Output Dataset without Reference Parent. Output Dataset = " + JSON.stringify(outputDatasetNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - if (outputDatasetNode.referenceParent.parentNode.code.singularVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - if (outputDatasetNode.referenceParent.parentNode.code.pluralVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - if (outputDatasetNode.referenceParent.parentNode.record === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - if (outputDatasetNode.referenceParent.parentNode.dataBuilding === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - recordDefinition = outputDatasetNode.referenceParent.parentNode.record singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName @@ -154,18 +86,18 @@ } /* Build the data */ - jsonData = dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) + jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) /* Add the calculated properties */ if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { - outputData = calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) + outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) } else { outputData = jsonData } products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData } - /*At the third and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ + /*At the fourth and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ let totalFilesWritten = 0 for (let i = 0; i < outputDatasets.length; i++) { let outputDatasetNode = outputDatasets[i] @@ -173,49 +105,6 @@ let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. let contextSummary = {} - /* Some very basic validations that we have all the information needed. */ - if (outputDatasetNode.referenceParent.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - - if (outputDatasetNode.referenceParent.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - - if (outputDatasetNode.referenceParent.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - - if (outputDatasetNode.referenceParent.parentNode.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - - if (outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Bot witn no codeName defined. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - - if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Team. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - - if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Team witn no codeName defined. Team = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - } - /* Check Irregular Periods */ if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { resultsWithIrregularPeriods = true @@ -235,193 +124,10 @@ } contextSummary.dataSetVersion = "dataSet.V1" - let fileContent = generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods) - writeFile(contextSummary, fileContent) + let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) + commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, outputPeriodLabel, callBackFunction) } - function jsonifyDataFile(dataFile, recordDefinition) { - - /* - This function has as an input the raw data on files and creates with it an array of JSON objects - with not calculated properties for later being consumed by Formulas - */ - - let jsonifiedArray = [] - - for (let i = 0; i < dataFile.length; i++) { - - let record = {} - for (let j = 0; j < recordDefinition.properties.length; j++) { - let property = recordDefinition.properties[j] - if (property.code.isCalculated !== true) { - record[property.code.codeName] = dataFile[i][j] - } - } - - jsonifiedArray.push(record); - } - - return jsonifiedArray - } - - function calculationsProcedure(jsonArray, recordDefinition, calculationsProcedure, variableName, timePeriod) { - - /* - This function has as an input an array of JSON objects, and it adds calculated properties to - complete the set of properties that will be available for Formulas. - */ - - let system = { // These are the available system variables to be used in User Code and Formulas - timePeriod: timePeriod, - ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS - } - let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. - let results = [] - - /* This is Initialization Code */ - if (calculationsProcedure.initialization !== undefined) { - if (calculationsProcedure.initialization.code !== undefined) { - try { - eval(calculationsProcedure.initialization.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = " + calculationsProcedure.initialization.code.code); - throw ("Error Executing User Code.") - } - } - } - - /* This is Initialization Code */ - if (calculationsProcedure.loop !== undefined) { - if (calculationsProcedure.loop.code !== undefined) { - for (let index = 0; index < jsonArray.length; index++) { - - let product = {} - product[variableName] = jsonArray[index] - - /* This is Loop Code */ - try { - eval(calculationsProcedure.loop.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = " + calculationsProcedure.loop.code.code); - throw ("Error Executing User Code.") - } - - /* For each calculated property we apply its formula*/ - for (let j = 0; j < recordDefinition.properties.length; j++) { - let property = recordDefinition.properties[j] - if (property.code.isCalculated === true) { - if (property.formula !== undefined) { - if (property.formula.code !== undefined) { - try { - let newValue = eval(property.formula.code) - let currentRecord = product[variableName] - currentRecord[property.code.codeName] = newValue - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); - throw ("Error Executing User Code.") - } - } - } - } - } - - /* Adding the new element to the resulting array */ - results.push(product[variableName]); - } - } - } - return results - } - - function dataBuildingProcedure(products, mainDependency, recordDefinition, dataBuildingProcedure, variableName, timePeriod, resultsWithIrregularPeriods) { - - /* - This function has as an input the products object, with all the information - of all products calculated so far by the process. Based on that information - the function will evaluate user supplied code and formulas in order to build - a new set of information. - */ - - let system = { // These are the available system variables to be used in User Code and Formulas - timePeriod: timePeriod, - ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS - } - let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas. - let results = [] - - /* This is Initialization Code */ - if (dataBuildingProcedure.initialization !== undefined) { - if (dataBuildingProcedure.initialization.code !== undefined) { - try { - eval(dataBuildingProcedure.initialization.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Code = " + dataBuildingProcedure.initialization.code.code); - throw ("Error Executing User Code.") - } - } - } - - /* This is Initialization Code */ - if (dataBuildingProcedure.loop !== undefined) { - if (dataBuildingProcedure.loop.code !== undefined) { - for (let index = 0; index < mainDependency.length; index++) { - - let record = { - previous: mainDependency[index - 1], - current: mainDependency[index], - next: mainDependency[index + 1] - } - - let product = {} - product[variableName] = {} - - /* This is Loop Code */ - try { - eval(dataBuildingProcedure.loop.code.code) - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Code = " + dataBuildingProcedure.loop.code.code); - throw ("Error Executing User Code.") - } - - /* For each non-calculated property we apply its formula*/ - for (let j = 0; j < recordDefinition.properties.length; j++) { - let property = recordDefinition.properties[j] - if (property.code.isCalculated !== true) { - if (property.formula !== undefined) { - if (property.formula.code !== undefined) { - try { - let newValue = eval(property.formula.code) - let currentRecord = product[variableName] - currentRecord[property.code.codeName] = newValue - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product)) - logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code); - throw ("Error Executing User Code.") - } - } - } - } - } - - /* If within the code the new data structure was not yet pushed to the results array, we do it */ - if (resultsWithIrregularPeriods !== true) { - /* Adding the new element to the resulting array */ - results.push(product[variableName]); - } - } - } - } - return results - } function anotherFileWritten() { totalFilesWritten++ @@ -429,123 +135,6 @@ callBackFunction(global.DEFAULT_OK_RESPONSE); } } - - function generateFileContent(records, recordDefinition, resultsWithIrregularPeriods) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> generateFileContent -> Entering function."); } - - let fileContent = ""; - let recordSeparator = ""; - - for (let i = 0; i < records.length; i++) { - let record = records[i]; - - if (processingDailyFiles === true) { - - if (resultsWithIrregularPeriods === true) { - /* - Here we have an special problem that occurs when an object spans several time peridos. If not taken care of - it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of - its properties. - - To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it - at the next day, in whole, even if it starts in the previous day. - */ - - let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; - - if (record.end < currentDay.valueOf() - 1) { continue; } - if (record.end === lastInstantOdDay) { continue; } - } else { - /* - The normal situation is that objects are respecting the time size of its main dependency, in which case we only - care of not adding objects of the previous day we are currently processing (while on daily files) - */ - if (record.end < currentDay.valueOf()) { continue; } - } - } - - fileContent = fileContent + recordSeparator + '[' - let propertySeparator = "" - for (let j = 0; j < recordDefinition.properties.length; j++) { - let property = recordDefinition.properties[j] - if (property.code.isCalculated !== true) { - fileContent = fileContent + propertySeparator - if (property.code.isNumeric !== true) { - fileContent = fileContent + '"' - } - fileContent = fileContent + record[property.code.codeName] - if (property.code.isNumeric !== true) { - fileContent = fileContent + '"' - } - if (propertySeparator === "") { propertySeparator = ","; } - } - } - fileContent = fileContent + ']' - if (recordSeparator === "") { recordSeparator = ","; } - } - fileContent = "[" + fileContent + "]"; - return fileContent - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> generateFileContent -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeFile(contextSummary, fileContent) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> Entering function."); } - - let fileName = '' + market.assetA + '_' + market.assetB + '.json'; - let dateForPath = '' - - if (processingDailyFiles === true) { - dateForPath = "/" + currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); - } - - let filePathRoot = contextSummary.devTeam + "/" + contextSummary.bot + "." + contextSummary.botVersion.major + "." + contextSummary.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + contextSummary.dataSetVersion; - let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + outputPeriodLabel + dateForPath; - filePath += '/' + fileName - - fileStorage.createTextFile(contextSummary.devTeam, filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> Entering function."); } - if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> fileContent = " + fileContent); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); - logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); - - callBackFunction(err); - return; - - } - - anotherFileWritten(); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index 55a4ba13a..962719483 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -412,12 +412,12 @@ switch (processConfig.framework.name) { case 'Multi-Period-Market': { - processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE); + processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE); intitializeProcessFramework(); break; } case 'Multi-Period-Daily': { - processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE); + processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE); intitializeProcessFramework(); break; } diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index fd00ee330..898c092aa 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -1,4 +1,4 @@ -exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE) { +exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; @@ -593,8 +593,6 @@ timePeriod, outputPeriodLabel, bot.multiPeriodDailyProcessDatetime, - contextVariables.dateBeginOfMarket, - contextVariables.dateEndOfMarket, interExecutionMemoryArray[n], onBotFinished); diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 8133547bb..2b6c480a3 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -1,4 +1,4 @@ -exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE, FILE_STORAGE) { +exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; @@ -260,8 +260,6 @@ timePeriod, outputPeriodLabel, undefined, - contextVariables.dateBeginOfMarket, - contextVariables.dateEndOfMarket, undefined, onBotFinished); diff --git a/Project.njsproj b/Project.njsproj index bfe531e65..8d7e2a509 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -25,6 +25,9 @@ Code + + Code + @@ -46,6 +49,9 @@ Code + + Code + Code diff --git a/TradingBot.js b/TradingBot.js new file mode 100644 index 000000000..6ad3f07e2 --- /dev/null +++ b/TradingBot.js @@ -0,0 +1,166 @@ +exports.newTradingBot = function newTradingBot(bot, logger, USER_BOT_COMMONS, UTILITIES, FILE_STORAGE) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + const MODULE_NAME = "Trading Bot"; + + let thisObject = { + initialize: initialize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + const COMMONS = require('./Commons.js'); + let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE); + + return thisObject; + + function initialize(callBackFunction) { + + try { + + logger.fileName = MODULE_NAME; + logger.initialize(); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(multiPeriodDataFiles, timePeriod, outputPeriodLabel, currentDay, interExecutionMemory, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } + + let products = {} + let mainDependency = {} + let processingDailyFiles + + if (currentDay !== undefined) { + processingDailyFiles = true + } else { + processingDailyFiles = false + } + + /* The first phase here is about checking that we have everything we need at the definition level. */ + let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies + if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) {return} + + let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets + if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return } + + /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */ + for (let j = 0; j < global.marketFilesPeriods.length; j++) { + + let mapKey = marketFilesPeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(mapKey) + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Building Dataset for timePeriod = " + mapKey); } + + if (dataFiles !== undefined) { + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod) + } + } + + for (let j = 0; j < global.dailyFilePeriods.length; j++) { + + let mapKey = global.dailyFilePeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(mapKey) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Building Dataset for timePeriod = " + mapKey); } + + if (dataFiles !== undefined) { + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod) + } + } + + /* During the third phase, we need to generate the data of the different products this process produces an output */ + for (let i = 0; i < outputDatasets.length; i++) { + + let outputDatasetNode = outputDatasets[i] + let jsonData // Just build data as Json objects + let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure + let singularVariableName // Name of the variable for this product + let recordDefinition // Record as defined by the user at the UI + let fileContent // Here we store the contents of the new data built just before writing it to a file. + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. + + /* + For each outputDatasetNode in our process output, we will build the information based on our input products. + */ + + recordDefinition = outputDatasetNode.referenceParent.parentNode.record + singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName + + /* Check Irregular Periods */ + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + + /* Build the data */ + jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) + + /* Add the calculated properties */ + if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { + outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) + } else { + outputData = jsonData + } + products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData + } + + /*At the fourth and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ + let totalFilesWritten = 0 + for (let i = 0; i < outputDatasets.length; i++) { + let outputDatasetNode = outputDatasets[i] + let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. + let contextSummary = {} + + /* Check Irregular Periods */ + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + + /* Simplifying the access to basic info */ + + contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName + contextSummary.product = outputDatasetNode.referenceParent.parentNode.code.codeName + contextSummary.bot = outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName + contextSummary.devTeam = outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + contextSummary.botVersion = { + "major": 1, + "minor": 0 + } + contextSummary.dataSetVersion = "dataSet.V1" + + let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) + commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, outputPeriodLabel, callBackFunction) + } + + + function anotherFileWritten() { + totalFilesWritten++ + if (totalFilesWritten === outputDatasets.length) { + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; + From 3da386abcee56c84a42a2aea719ce078261e29a4 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 19 Nov 2019 14:56:56 +0100 Subject: [PATCH 27/38] TradingBot can be executed --- IndicatorBot.js | 8 ++++++++ IndicatorBotProcessMainLoop.js | 4 ++-- MultiPeriod.js | 16 +++++++++------- MultiPeriodDaily.js | 2 +- MultiPeriodMarket.js | 2 +- TradingBot.js | 33 +++++++++++++++++++++++++-------- TradingBotProcessMainLoop.js | 20 +++++--------------- 7 files changed, 51 insertions(+), 34 deletions(-) diff --git a/IndicatorBot.js b/IndicatorBot.js index 117d8fe30..ae0ac799b 100644 --- a/IndicatorBot.js +++ b/IndicatorBot.js @@ -8,6 +8,7 @@ let thisObject = { initialize: initialize, + finalize: finalize, start: start }; @@ -19,6 +20,13 @@ return thisObject; + function finalize() { + thisObject = undefined + utilities = undefined + fileStorage = undefined + commons = undefined + } + function initialize(callBackFunction) { try { diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js index 962719483..e29c7ac60 100644 --- a/IndicatorBotProcessMainLoop.js +++ b/IndicatorBotProcessMainLoop.js @@ -412,12 +412,12 @@ switch (processConfig.framework.name) { case 'Multi-Period-Market': { - processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE); + processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, UTILITIES, FILE_STORAGE); intitializeProcessFramework(); break; } case 'Multi-Period-Daily': { - processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE); + processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, UTILITIES, FILE_STORAGE); intitializeProcessFramework(); break; } diff --git a/MultiPeriod.js b/MultiPeriod.js index 9d40e4433..f2aeb825d 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -1,4 +1,4 @@ -exports.newMultiPeriod = function newMultiPeriod(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE) { +exports.newMultiPeriod = function newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; @@ -20,7 +20,7 @@ let dataFiles = []; let multiPeriodDataFiles = new Map(); - let usertBot; + let botInstance; const FILE_STORAGE = require('./FileStorage.js'); let fileStorage = FILE_STORAGE.newFileStorage(logger); @@ -60,8 +60,10 @@ } - usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage); - usertBot.initialize(callBackFunction, pAssistant); + let USER_BOT_MODULE = require("./TradingBot") + + botInstance = USER_BOT_MODULE.newTradingBot(bot, logger, UTILITIES, FILE_STORAGE); + botInstance.initialize(callBackFunction); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); @@ -75,8 +77,8 @@ multiPeriodDataFiles = undefined statusDependencies = undefined dataDependencies = undefined - usertBot.finalize() - usertBot = undefined + botInstance.finalize() + botInstance = undefined fileStorage = undefined processConfig = undefined thisObject = undefined @@ -839,7 +841,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> callTheBot -> Entering function."); } - usertBot.start( + botInstance.start( multiPeriodDataFiles, currentTimePeriod, currentOutputPeriodName, diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 898c092aa..ea4a030f9 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -1,4 +1,4 @@ -exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE) { +exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, UTILITIES, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 2b6c480a3..11dadbe2a 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -1,4 +1,4 @@ -exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, UTILITIES, USER_BOT_MODULE, FILE_STORAGE) { +exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, UTILITIES, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; diff --git a/TradingBot.js b/TradingBot.js index 6ad3f07e2..a8e1d7c2c 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -1,4 +1,4 @@ -exports.newTradingBot = function newTradingBot(bot, logger, USER_BOT_COMMONS, UTILITIES, FILE_STORAGE) { +exports.newTradingBot = function newTradingBot(bot, logger, UTILITIES, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; @@ -8,6 +8,7 @@ let thisObject = { initialize: initialize, + finalize: finalize, start: start }; @@ -19,6 +20,13 @@ return thisObject; + function finalize() { + thisObject = undefined + utilities = undefined + fileStorage = undefined + commons = undefined + } + function initialize(callBackFunction) { try { @@ -42,7 +50,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } - let products = {} + let chart = {} let mainDependency = {} let processingDailyFiles @@ -62,24 +70,33 @@ /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */ for (let j = 0; j < global.marketFilesPeriods.length; j++) { - let mapKey = marketFilesPeriods[j][1] - let dataFiles = multiPeriodDataFiles.get(mapKey) - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Building Dataset for timePeriod = " + mapKey); } + let timePeriodLabel = marketFilesPeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(timePeriodLabel) + let products = {} + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Inflating Data Files for timePeriod = " + timePeriodLabel); } if (dataFiles !== undefined) { commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod) + + let propertyName = 'at' + timePeriodLabel.replace('-', ''); + chart[propertyName] = products } } for (let j = 0; j < global.dailyFilePeriods.length; j++) { - let mapKey = global.dailyFilePeriods[j][1] - let dataFiles = multiPeriodDataFiles.get(mapKey) + let timePeriodLabel = global.dailyFilePeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(timePeriodLabel) + let products = {} - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Building Dataset for timePeriod = " + mapKey); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Inflating Data Files for timePeriod = " + timePeriodLabel); } if (dataFiles !== undefined) { commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod) + + let propertyName = 'at' + timePeriodLabel.replace('-', ''); + chart[propertyName] = products } } diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index cb321b0ca..bbb5f013b 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -6,7 +6,7 @@ const FULL_LOG = true; let USER_BOT_MODULE; - let COMMONS_MODULE; + let USER_BOT_COMMONS; const MULTI_PERIOD_MARKET = require(ROOT_DIR + 'MultiPeriodMarket'); const MULTI_PERIOD_DAILY = require(ROOT_DIR + 'MultiPeriodDaily'); @@ -81,9 +81,9 @@ return; } - COMMONS_MODULE = {} - //COMMONS_MODULE.newCommons = require(process.env.BOTS_PATH + '/aamasters/AAMasters/bots/AAJason-Trading-Engine-Bot/Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. - COMMONS_MODULE.newCommons = eval(text); // Use this for production + USER_BOT_COMMONS = {} + //USER_BOT_COMMONS.newCommons = require(process.env.BOTS_PATH + '/aamasters/AAMasters/bots/AAJason-Trading-Engine-Bot/Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. + USER_BOT_COMMONS.newCommons = eval(text); // Use this for production session.initialize(onSessionInitialized) @@ -673,18 +673,8 @@ } switch (processConfig.framework.name) { - case 'Multi-Period-Market': { - processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); - intitializeProcessFramework(); - break; - } - case 'Multi-Period-Daily': { - processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); - intitializeProcessFramework(); - break; - } case 'Multi-Period': { - processFramework = MULTI_PERIOD.newMultiPeriod(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE); + processFramework = MULTI_PERIOD.newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS); intitializeProcessFramework(); break; } From 0fb685a34ff073569833e283396e05e44b43d175 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 20 Nov 2019 09:47:48 +0100 Subject: [PATCH 28/38] Ready to Import Run Simulation --- Commons.js | 35 ++++++++++++++++++----------------- MultiPeriod.js | 10 +++++----- MultiPeriodDaily.js | 6 +++--- MultiPeriodMarket.js | 6 +++--- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Commons.js b/Commons.js index 2a52f8c6f..706409c71 100644 --- a/Commons.js +++ b/Commons.js @@ -24,21 +24,21 @@ function validateDataDependencies(dataDependencies, callBackFunction) { for (let i = 0; i < dataDependencies.length; i++) { - let dataDependencyNode = dataDependencies[i].referenceParent + let dataDependencyNode = dataDependencies[i] /* Basic validations to see if we have everything we need. */ - if (dataDependencyNode.parentNode.code.singularVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); + if (dataDependencyNode.referenceParent.parentNode.code.singularVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataDependencyNode.parentNode.code.pluralVariableName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); + if (dataDependencyNode.referenceParent.parentNode.code.pluralVariableName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } - if (dataDependencyNode.parentNode.record === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode)); + if (dataDependencyNode.referenceParent.parentNode.record === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } @@ -71,9 +71,7 @@ return } if (outputDatasetNode.referenceParent.parentNode.dataBuilding === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return + logger.write(MODULE_NAME, "[WARN] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); } if (outputDatasetNode.referenceParent.code.codeName === undefined) { logger.write(MODULE_NAME, "[ERROR] start -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); @@ -128,25 +126,28 @@ */ for (let i = 0; i < dataDependencies.length; i++) { - let dataFile = dataFiles[i] + let dataFile let jsonData // Datafile converted into Json objects let inputData // Includes calculated properties let singularVariableName // name of the variable for this product let recordDefinition - let dataDependencyNode = dataDependencies[i].referenceParent + let dataDependencyNode = dataDependencies[i] + dataFile = dataFiles.get(dataDependencyNode.id) - recordDefinition = dataDependencyNode.parentNode.record - singularVariableName = dataDependencyNode.parentNode.code.singularVariableName + if (dataFile === undefined) { continue } // When a datafile is not found it might be because we are processing market or daily and at the dependency array there are both types mixed up. + + recordDefinition = dataDependencyNode.referenceParent.parentNode.record + singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName /* Transform the raw data into JSON objects */ jsonData = jsonifyDataFile(dataFile, recordDefinition) /* Add the calculated properties */ - if (dataDependencyNode.parentNode.calculations !== undefined) { - inputData = calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.parentNode.calculations, singularVariableName, timePeriod) + if (dataDependencyNode.referenceParent.parentNode.calculations !== undefined) { + inputData = calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) } else { inputData = jsonData } - products[dataDependencyNode.parentNode.code.pluralVariableName] = inputData + products[dataDependencyNode.referenceParent.parentNode.code.pluralVariableName] = inputData /* The main dependency is defined as the first dependency processed. */ if (mainDependency.records === undefined) { diff --git a/MultiPeriod.js b/MultiPeriod.js index f2aeb825d..e877b2286 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -17,7 +17,7 @@ let statusDependencies; let dataDependencies; let datasets = []; - let dataFiles = []; + let dataFiles = new Map(); let multiPeriodDataFiles = new Map(); let botInstance; @@ -287,7 +287,7 @@ const outputPeriodLabel = global.marketFilesPeriods[n][1]; let dependencyIndex = 0; - dataFiles = []; + dataFiles = new Map(); if (bot.VALUES_TO_USE.timePeriod === outputPeriodLabel) { currentTimePeriod = global.marketFilesPeriods[n][0]; @@ -339,7 +339,7 @@ } let dataFile = JSON.parse(text); - dataFiles.push(dataFile); + dataFiles.set(dependency.id, dataFile); dependencyControlLoop(); @@ -596,7 +596,7 @@ } let dependencyIndex = 0; - dataFiles = []; + dataFiles = new Map(); dependencyLoopBody(); function dependencyLoopBody() { @@ -748,7 +748,7 @@ let dataFile = previousFile.concat(currentFile); - dataFiles.push(dataFile); + dataFiles.set(dependency.id, dataFile); dependencyControlLoop(); diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index ea4a030f9..f11198fa7 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -17,7 +17,7 @@ let statusDependencies; let dataDependencies; let datasets = []; - let dataFiles = []; + let dataFiles = new Map; let botInstance; @@ -386,7 +386,7 @@ } let dependencyIndex = 0; - dataFiles = []; + dataFiles = new Map(); dependencyLoopBody(); @@ -533,7 +533,7 @@ let dataFile = previousFile.concat(currentFile); - dataFiles.push(dataFile); + dataFiles.set(dependency.id, dataFile); dependencyControlLoop(); } diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 11dadbe2a..37311cef4 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -15,7 +15,7 @@ let statusDependencies; let dataDependencies; let datasets = []; - let dataFiles = []; + let dataFiles = new Map(); let botInstance; @@ -151,7 +151,7 @@ const outputPeriodLabel = global.marketFilesPeriods[n][1]; let dependencyIndex = 0; - dataFiles = []; + dataFiles = new Map; dependencyLoopBody(); @@ -198,7 +198,7 @@ } let dataFile = JSON.parse(text); - dataFiles.push(dataFile); + dataFiles.set(dependency.id, dataFile); dependencyControlLoop(); From 3a25feef8879a64801f08ed1ca7c39597e5dea23 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 20 Nov 2019 12:04:22 +0100 Subject: [PATCH 29/38] Trading Simulation consuming indicators with new scheme --- Commons.js | 7 +- IndicatorBot.js | 4 +- MultiPeriod.js | 20 +- MultiPeriodDaily.js | 14 +- MultiPeriodMarket.js | 8 +- Project.njsproj | 3 + TradingBot.js | 139 +- TradingSimulation.js | 2893 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 3004 insertions(+), 84 deletions(-) create mode 100644 TradingSimulation.js diff --git a/Commons.js b/Commons.js index 706409c71..d38d935eb 100644 --- a/Commons.js +++ b/Commons.js @@ -164,6 +164,7 @@ */ let jsonifiedArray = [] + let previous; for (let i = 0; i < dataFile.length; i++) { @@ -175,7 +176,9 @@ } } + record.previous = previous; jsonifiedArray.push(record); + previous = record; } return jsonifiedArray @@ -405,7 +408,7 @@ } } - function writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, outputPeriodLabel, callBackFunction) { + function writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, callBackFunction) { try { @@ -420,7 +423,7 @@ } let filePathRoot = contextSummary.devTeam + "/" + contextSummary.bot + "." + contextSummary.botVersion.major + "." + contextSummary.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + contextSummary.dataSetVersion; - let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + outputPeriodLabel + dateForPath; + let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + timePeriodLabel + dateForPath; filePath += '/' + fileName fileStorage.createTextFile(contextSummary.devTeam, filePath, fileContent + '\n', onFileCreated); diff --git a/IndicatorBot.js b/IndicatorBot.js index ae0ac799b..74a547e44 100644 --- a/IndicatorBot.js +++ b/IndicatorBot.js @@ -44,7 +44,7 @@ } } - function start(dataFiles, timePeriod, outputPeriodLabel, currentDay, interExecutionMemory, callBackFunction) { + function start(dataFiles, timePeriod, timePeriodLabel, currentDay, interExecutionMemory, callBackFunction) { try { @@ -133,7 +133,7 @@ contextSummary.dataSetVersion = "dataSet.V1" let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) - commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, outputPeriodLabel, callBackFunction) + commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, callBackFunction) } diff --git a/MultiPeriod.js b/MultiPeriod.js index e877b2286..00961e350 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -63,7 +63,7 @@ let USER_BOT_MODULE = require("./TradingBot") botInstance = USER_BOT_MODULE.newTradingBot(bot, logger, UTILITIES, FILE_STORAGE); - botInstance.initialize(callBackFunction); + botInstance.initialize(pAssistant, callBackFunction); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); @@ -284,12 +284,12 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsMarketFiles -> periodsLoopBody -> Entering function."); } const timePeriod = global.marketFilesPeriods[n][0]; - const outputPeriodLabel = global.marketFilesPeriods[n][1]; + const timePeriodLabel = global.marketFilesPeriods[n][1]; let dependencyIndex = 0; dataFiles = new Map(); - if (bot.VALUES_TO_USE.timePeriod === outputPeriodLabel) { + if (bot.VALUES_TO_USE.timePeriod === timePeriodLabel) { currentTimePeriod = global.marketFilesPeriods[n][0]; currentOutputPeriodName = global.marketFilesPeriods[n][1]; } @@ -320,7 +320,7 @@ return } - let filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel; + let filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel; dataset.getTextFile(filePath, fileName, onFileReceived); @@ -575,22 +575,22 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> Entering function."); } const timePeriod = global.dailyFilePeriods[n][0]; - const outputPeriodLabel = global.dailyFilePeriods[n][1]; + const timePeriodLabel = global.dailyFilePeriods[n][1]; if (processConfig.framework.validPeriods !== undefined) { let validPeriod = false; for (let i = 0; i < processConfig.framework.validPeriods.length; i++) { let period = processConfig.framework.validPeriods[i]; - if (period === outputPeriodLabel) { validPeriod = true } + if (period === timePeriodLabel) { validPeriod = true } } if (validPeriod === false) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> outputPeriodLabel = " + outputPeriodLabel); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> timePeriodLabel = " + timePeriodLabel); } periodsControlLoop(); return; } } - if (bot.VALUES_TO_USE.timePeriod === outputPeriodLabel) { + if (bot.VALUES_TO_USE.timePeriod === timePeriodLabel) { currentTimePeriod = global.dailyFilePeriods[n][0]; currentOutputPeriodName = global.dailyFilePeriods[n][1]; } @@ -634,7 +634,7 @@ let filePath if (dependency.dataSet === "Multi-Period-Daily") { - filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath; + filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath; } else { filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; } @@ -710,7 +710,7 @@ let dateForPath = bot.multiPeriodProcessDatetime.getUTCFullYear() + '/' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCDate(), 2); let filePath if (dependency.dataSet === "Multi-Period-Daily") { - filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath; + filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath; } else { filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; } diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index f11198fa7..a19d0c52c 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -370,16 +370,16 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Entering function."); } const timePeriod = global.dailyFilePeriods[n][0]; - const outputPeriodLabel = global.dailyFilePeriods[n][1]; + const timePeriodLabel = global.dailyFilePeriods[n][1]; if (processConfig.framework.validPeriods !== undefined) { let validPeriod = false; for (let i = 0; i < processConfig.framework.validPeriods.length; i++) { let period = processConfig.framework.validPeriods[i]; - if (period === outputPeriodLabel) { validPeriod = true } + if (period === timePeriodLabel) { validPeriod = true } } if (validPeriod === false) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> outputPeriodLabel = " + outputPeriodLabel); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> timePeriodLabel = " + timePeriodLabel); } periodsControlLoop(); return; } @@ -418,7 +418,7 @@ let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2); let filePath if (dependency.dataSet === "Multi-Period-Daily") { - filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath; + filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath; } else { filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; } @@ -488,7 +488,7 @@ let dateForPath = bot.multiPeriodDailyProcessDatetime.getUTCFullYear() + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCDate(), 2); let filePath if (dependency.dataSet === "Multi-Period-Daily") { - filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath; + filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath; } else { filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; } @@ -586,12 +586,12 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> Entering function."); } const timePeriod = global.dailyFilePeriods[n][0]; - const outputPeriodLabel = global.dailyFilePeriods[n][1]; + const timePeriodLabel = global.dailyFilePeriods[n][1]; botInstance.start( dataFiles, timePeriod, - outputPeriodLabel, + timePeriodLabel, bot.multiPeriodDailyProcessDatetime, interExecutionMemoryArray[n], onBotFinished); diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js index 37311cef4..4ee15f3e8 100644 --- a/MultiPeriodMarket.js +++ b/MultiPeriodMarket.js @@ -148,7 +148,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Entering function."); } const timePeriod = global.marketFilesPeriods[n][0]; - const outputPeriodLabel = global.marketFilesPeriods[n][1]; + const timePeriodLabel = global.marketFilesPeriods[n][1]; let dependencyIndex = 0; dataFiles = new Map; @@ -177,7 +177,7 @@ let filePath if (dependency.dataSet === "Multi-Period-Market") { - filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel; + filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel; } else { filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath; } @@ -253,12 +253,12 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> Entering function."); } const timePeriod = global.marketFilesPeriods[n][0]; - const outputPeriodLabel = global.marketFilesPeriods[n][1]; + const timePeriodLabel = global.marketFilesPeriods[n][1]; botInstance.start( dataFiles, timePeriod, - outputPeriodLabel, + timePeriodLabel, undefined, undefined, onBotFinished); diff --git a/Project.njsproj b/Project.njsproj index 8d7e2a509..22992fbc9 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -55,6 +55,9 @@ Code + + Code + Code diff --git a/TradingBot.js b/TradingBot.js index a8e1d7c2c..cb4891579 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -18,6 +18,8 @@ const COMMONS = require('./Commons.js'); let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE); + let assistant; + return thisObject; function finalize() { @@ -27,7 +29,7 @@ commons = undefined } - function initialize(callBackFunction) { + function initialize(pAssistant, callBackFunction) { try { @@ -36,6 +38,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } + assistant = pAssistant; callBackFunction(global.DEFAULT_OK_RESPONSE); } catch (err) { @@ -44,7 +47,7 @@ } } - function start(multiPeriodDataFiles, timePeriod, outputPeriodLabel, currentDay, interExecutionMemory, callBackFunction) { + function start(multiPeriodDataFiles, timePeriod, timePeriodLabel, currentDay, interExecutionMemory, callBackFunction) { try { @@ -100,73 +103,91 @@ } } - /* During the third phase, we need to generate the data of the different products this process produces an output */ - for (let i = 0; i < outputDatasets.length; i++) { + const TRADING_SIMULATION = require('./TradingSimulation.js'); + let tradingSimulation = TRADING_SIMULATION.newTradingSimulation(bot, logger, UTILITIES); - let outputDatasetNode = outputDatasets[i] - let jsonData // Just build data as Json objects - let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure - let singularVariableName // Name of the variable for this product - let recordDefinition // Record as defined by the user at the UI - let fileContent // Here we store the contents of the new data built just before writing it to a file. - let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. + tradingSimulation.runSimulation( + chart, + dataDependencies, + timePeriod, + timePeriodLabel, + currentDay, + interExecutionMemory, + assistant, + onSimulationFinished, + callBackFunction) - /* - For each outputDatasetNode in our process output, we will build the information based on our input products. - */ - recordDefinition = outputDatasetNode.referenceParent.parentNode.record - singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName + function onSimulationFinished() { + console.log("Finished") - /* Check Irregular Periods */ - if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { - resultsWithIrregularPeriods = true + /* During the third phase, we need to generate the data of the different products this process produces an output */ + for (let i = 0; i < outputDatasets.length; i++) { + + let outputDatasetNode = outputDatasets[i] + let jsonData // Just build data as Json objects + let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure + let singularVariableName // Name of the variable for this product + let recordDefinition // Record as defined by the user at the UI + let fileContent // Here we store the contents of the new data built just before writing it to a file. + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. + + /* + For each outputDatasetNode in our process output, we will build the information based on our input products. + */ + + recordDefinition = outputDatasetNode.referenceParent.parentNode.record + singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName + + /* Check Irregular Periods */ + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + + /* Build the data */ + jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) + + /* Add the calculated properties */ + if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { + outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) + } else { + outputData = jsonData + } + products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData } - /* Build the data */ - jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) + /*At the fourth and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ + let totalFilesWritten = 0 + for (let i = 0; i < outputDatasets.length; i++) { + let outputDatasetNode = outputDatasets[i] + let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] + let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. + let contextSummary = {} - /* Add the calculated properties */ - if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { - outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) - } else { - outputData = jsonData + /* Check Irregular Periods */ + if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { + resultsWithIrregularPeriods = true + } + + /* Simplifying the access to basic info */ + + contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName + contextSummary.product = outputDatasetNode.referenceParent.parentNode.code.codeName + contextSummary.bot = outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName + contextSummary.devTeam = outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName + + /* This stuff is still hardcoded and unresolved. */ + contextSummary.botVersion = { + "major": 1, + "minor": 0 + } + contextSummary.dataSetVersion = "dataSet.V1" + + let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) + commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, callBackFunction) } - products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData } - /*At the fourth and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ - let totalFilesWritten = 0 - for (let i = 0; i < outputDatasets.length; i++) { - let outputDatasetNode = outputDatasets[i] - let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] - let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. - let contextSummary = {} - - /* Check Irregular Periods */ - if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { - resultsWithIrregularPeriods = true - } - - /* Simplifying the access to basic info */ - - contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName - contextSummary.product = outputDatasetNode.referenceParent.parentNode.code.codeName - contextSummary.bot = outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName - contextSummary.devTeam = outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName - - /* This stuff is still hardcoded and unresolved. */ - contextSummary.botVersion = { - "major": 1, - "minor": 0 - } - contextSummary.dataSetVersion = "dataSet.V1" - - let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) - commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, outputPeriodLabel, callBackFunction) - } - - function anotherFileWritten() { totalFilesWritten++ if (totalFilesWritten === outputDatasets.length) { diff --git a/TradingSimulation.js b/TradingSimulation.js new file mode 100644 index 000000000..a018512a7 --- /dev/null +++ b/TradingSimulation.js @@ -0,0 +1,2893 @@ +exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + const MODULE_NAME = "Trading Simulation -> " + bot.SESSION.name; + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + const GMT_SECONDS = ':00.000 GMT+0000'; + + let thisObject = { + finalize: finalize, + runSimulation: runSimulation + }; + + let utilities = UTILITIES.newCloudUtilities(bot, logger); + + let percentageBandwidthAt = {} + let bollingerBandsAt = {} + let bollingerChannelsAt = {} + let bollingerSubChannelsAt = {} + let candlesAt = {} + + + return thisObject; + + function finalize() { + percentageBandwidthAt = undefined + bollingerBandsAt = undefined + bollingerChannelsAt = undefined + bollingerSubChannelsAt = undefined + candlesAt = undefined + thisObject = undefined + } + + function runSimulation( + chart, + dataDependencies, + timePeriod, + timePeriodLabel, + currentDay, + interExecutionMemory, + assistant, + callback, + callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> Entering function."); } + let processingDailyFiles + if (timePeriod > global.dailyFilePeriods[0][0]) { + processingDailyFiles = false + } else { + processingDailyFiles = true + } + + let recordsArray = []; + let conditionsArray = []; + let strategiesArray = []; + let tradesArray = []; + + let tradingSystem = bot.DEFINITION.tradingSystem; + + /* Initial Default Values */ + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.initialDatetime = " + bot.VALUES_TO_USE.timeRange.initialDatetime); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.finalDatetime = " + bot.VALUES_TO_USE.timeRange.finalDatetime); } + + let timerToCloseStage = 0 + + /* Stop Loss Management */ + + const MIN_STOP_LOSS_VALUE = 1 // We can not let the stop be zero to avoid division by 0 error or infinity numbers as a result. + const MAX_STOP_LOSS_VALUE = Number.MAX_SAFE_INTEGER + let stopLoss = 0; + + /* Take Profit Management */ + + const MIN_TAKE_PROFIT_VALUE = 1 // We can not let the buy order be zero to avoid division by 0 error or infinity numbers as a result. + const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER + let takeProfit = 0; + + /* Simulation Records */ + + let hitRatio = 0; + let ROI = 0; + let days = 0; + let positionDays = 0; + let anualizedRateOfReturn = 0; + let type = '""'; + let marketRate = 0; + let takePositionNow = false + let closePositionNow = false + + /* In some cases we need to know if we are positioned at the last candle of the calendar day, for that we need these variables. */ + + let lastInstantOfTheDay = 0 + if (currentDay) { + lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + } + + /* + The following counters need to survive multiple executions of the similator and keep themselves reliable. + This is challenging when the simulation is executed using Daily Files, since each execution means a new + day and these counters are meant to be kept along the whole market. + + To overcome this problem, we use the interExecutionMemory to record the values of the current execution + when finish. But there are a few details: + + 1. When the process is at the head of the market, it executes multple times at the same day. + 2. The same code serves execution from Market Files. + 3. In Daily Files we are receiving candles from the current day and previous day, so we need to take care of + not adding to the counters duplicate info when processing the day before candles. + + To overcome these challenges we record the values of the counters and variables on the interExecutionMemory only when + the day is complete and if we have a current Day. That menas that for Market Files we will never use + interExecutionMemory. + */ + + /*Needed for statistics */ + let previousBalanceAssetA = 0; + let previousBalanceAssetB = 0; + + /* Position Management */ + let tradePositionRate = 0; + let tradePositionSize = 0; + + /* Strategy and Phase Management */ + let currentStrategyIndex = -1; + let strategyStage = 'No Stage'; + + let stopLossPhase = -1; + let stopLossStage = 'No Stage'; + + let takeProfitPhase = -1; + let takeProfitStage = 'No Stage'; + + /* These 2 objects will allow us to create separate files for each one of them. */ + + let currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + let currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + let balanceAssetA = bot.VALUES_TO_USE.initialBalanceA; + let balanceAssetB = bot.VALUES_TO_USE.initialBalanceB; + + let lastTradeProfitLoss = 0; + let profit = 0; + let lastTradeROI = 0; + + let roundtrips = 0; + let fails = 0; + let hits = 0; + let periods = 0; + let positionPeriods = 0; + + /* Usefull counters for conditions and formulas */ + + let distanceToLast = { + triggerOn: 0, + triggerOff: 0, + takePosition: 0, + closePosition: 0 + } + + /* Message to the Simulation Executor */ + + let orderId = 0; + let messageId = 0; + + let yesterday = {}; + + /* Initialization */ + + yesterday.stopLoss = 0 + yesterday.takeProfit = 0 + + yesterday.previousBalanceAssetA = previousBalanceAssetA + yesterday.previousBalanceAssetB = previousBalanceAssetB + + yesterday.tradePositionRate = tradePositionRate + yesterday.tradePositionSize = tradePositionSize + + yesterday.currentStrategyIndex = currentStrategyIndex + yesterday.strategyStage = strategyStage + + yesterday.stopLossPhase = stopLossPhase + yesterday.stopLossStage = stopLossStage + + yesterday.takeProfitPhase = takeProfitPhase + yesterday.takeProfitStage = takeProfitStage + + yesterday.currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + yesterday.currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + yesterday.lastTradeProfitLoss = 0; + yesterday.profit = 0; + yesterday.lastTradeROI = 0; + + yesterday.roundtrips = 0; + yesterday.fails = 0; + yesterday.hits = 0; + yesterday.periods = 0; + yesterday.positionPeriods = 0; + + yesterday.distanceToLast = { + triggerOn: 0, + triggerOff: 0, + takePosition: 0, + closePosition: 0 + } + + yesterday.orderId = 0; + yesterday.messageId = 0; + + yesterday.hitRatio = 0; + yesterday.ROI = 0; + yesterday.anualizedRateOfReturn = 0; + + if (interExecutionMemory.roundtrips === undefined) { // This just means that the inter execution memory was never used before. + + /* Initialize the data structure we will use inter execution. */ + + interExecutionMemory.stopLoss = 0 + interExecutionMemory.takeProfit = 0 + + interExecutionMemory.previousBalanceAssetA = previousBalanceAssetA + interExecutionMemory.previousBalanceAssetB = previousBalanceAssetB + + interExecutionMemory.tradePositionRate = tradePositionRate + interExecutionMemory.tradePositionSize = tradePositionSize + + interExecutionMemory.currentStrategyIndex = currentStrategyIndex + interExecutionMemory.strategyStage = strategyStage + + interExecutionMemory.stopLossPhase = stopLossPhase + interExecutionMemory.stopLossStage = stopLossStage + + interExecutionMemory.takeProfitPhase = takeProfitPhase + interExecutionMemory.takeProfitStage = takeProfitStage + + interExecutionMemory.currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + interExecutionMemory.currentTrade = { + begin: 0, + end: 0, + status: 0, + profit: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + interExecutionMemory.balanceAssetA = balanceAssetA; + interExecutionMemory.balanceAssetB = balanceAssetB; + + interExecutionMemory.lastTradeProfitLoss = lastTradeProfitLoss; + interExecutionMemory.profit = profit; + interExecutionMemory.lastTradeROI = lastTradeROI; + + interExecutionMemory.roundtrips = 0; + interExecutionMemory.fails = 0; + interExecutionMemory.hits = 0; + interExecutionMemory.periods = 0; + interExecutionMemory.positionPeriods = 0; + + interExecutionMemory.distanceToLast = { + triggerOn: 0, + triggerOff: 0, + takePosition: 0, + closePosition: 0 + } + + interExecutionMemory.orderId = 0; + interExecutionMemory.messageId = 0; + + interExecutionMemory.hitRatio = 0; + interExecutionMemory.ROI = 0; + interExecutionMemory.anualizedRateOfReturn = 0; + + interExecutionMemory.announcements = [] + + } else { + + /* We get the initial values from the day previous to the candles we receive at the current execution */ + + stopLoss = interExecutionMemory.stopLoss + takeProfit = interExecutionMemory.takeProfit + + previousBalanceAssetA = interExecutionMemory.previousBalanceAssetA + previousBalanceAssetB = interExecutionMemory.previousBalanceAssetB + + tradePositionRate = interExecutionMemory.tradePositionRate + tradePositionSize = interExecutionMemory.tradePositionSize + + currentStrategyIndex = interExecutionMemory.currentStrategyIndex + strategyStage = interExecutionMemory.strategyStage + + stopLossPhase = interExecutionMemory.stopLossPhase + stopLossStage = interExecutionMemory.stopLossStage + + takeProfitPhase = interExecutionMemory.takeProfitPhase + takeProfitStage = interExecutionMemory.takeProfitStage + + currentStrategy = { + begin: interExecutionMemory.currentStrategy.begin, + end: interExecutionMemory.currentStrategy.end, + status: interExecutionMemory.currentStrategy.status, + number: interExecutionMemory.currentStrategy.number, + beginRate: interExecutionMemory.currentStrategy.beginRate, + endRate: interExecutionMemory.currentStrategy.endRate + } + + currentTrade = { + begin: interExecutionMemory.currentTrade.begin, + end: interExecutionMemory.currentTrade.end, + status: interExecutionMemory.currentTrade.status, + profit: interExecutionMemory.currentTrade.profit, + exitType: interExecutionMemory.currentTrade.exitType, + beginRate: interExecutionMemory.currentTrade.beginRate, + endRate: interExecutionMemory.currentTrade.endRate + } + + if (currentDay) { + if (currentDay.valueOf() >= bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() + ONE_DAY_IN_MILISECONDS) { // Only after the first day we start grabbing the balance from this memory. + + balanceAssetA = interExecutionMemory.balanceAssetA; + balanceAssetB = interExecutionMemory.balanceAssetB; + + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + } + } + + lastTradeProfitLoss = interExecutionMemory.lastTradeProfitLoss; + profit = interExecutionMemory.profit; + lastTradeROI = interExecutionMemory.lastTradeROI; + + roundtrips = interExecutionMemory.roundtrips; + fails = interExecutionMemory.fails; + hits = interExecutionMemory.hits; + periods = interExecutionMemory.periods; + positionPeriods = interExecutionMemory.positionPeriods; + + distanceToLast.triggerOn = interExecutionMemory.distanceToLast.triggerOn + distanceToLast.triggerOff = interExecutionMemory.distanceToLast.triggerOff + distanceToLast.takePosition = interExecutionMemory.distanceToLast.takePosition + distanceToLast.closePosition = interExecutionMemory.distanceToLast.closePosition + + orderId = interExecutionMemory.orderId; // to be deprecated + messageId = interExecutionMemory.messageId; // to be deprecated + + hitRatio = interExecutionMemory.hitRatio; + ROI = interExecutionMemory.ROI; + anualizedRateOfReturn = interExecutionMemory.anualizedRateOfReturn; + + /* For the case that any of these variables are not updated during the main loop, we need to store their value at the yesterday structure, otherwise it would be lost. */ + + yesterday.stopLoss = stopLoss + yesterday.takeProfit = takeProfit + + yesterday.previousBalanceAssetA = previousBalanceAssetA + yesterday.previousBalanceAssetB = previousBalanceAssetB + + yesterday.tradePositionRate = tradePositionRate + yesterday.tradePositionSize = tradePositionSize + + yesterday.currentStrategyIndex = currentStrategyIndex; + yesterday.strategyStage = strategyStage; + + yesterday.stopLossPhase = stopLossPhase; + yesterday.stopLossStage = stopLossStage; + + yesterday.takeProfitPhase = takeProfitPhase; + yesterday.takeProfitStage = takeProfitStage; + + yesterday.currentStrategy = { + begin: currentStrategy.begin, + end: currentStrategy.end, + status: currentStrategy.status, + number: currentStrategy.number, + beginRate: currentStrategy.beginRate, + endRate: currentStrategy.endRate + } + + yesterday.currentTrade = { + begin: currentTrade.begin, + end: currentTrade.end, + status: currentTrade.status, + profit: currentTrade.profit, + exitType: currentTrade.exitType, + beginRate: currentTrade.beginRate, + endRate: currentTrade.endRate + } + + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.profit = profit; + yesterday.lastTradeROI = lastTradeROI; + + yesterday.roundtrips = roundtrips; + yesterday.fails = fails; + yesterday.hits = hits; + yesterday.periods = periods; + yesterday.positionPeriods = positionPeriods; + + yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + yesterday.distanceToLast.takePosition = distanceToLast.takePosition + yesterday.distanceToLast.closePosition = distanceToLast.closePosition + + yesterday.hitRatio = hitRatio; + yesterday.ROI = ROI; + yesterday.anualizedRateOfReturn = anualizedRateOfReturn; + } + + /* Main Array and Maps */ + + let propertyName = 'at' + timePeriodLabel.replace('-', ''); + let candles = chart[propertyName].candles + let currentChart = chart[propertyName] + + /* Last Candle */ + let lastCandle = candles[candles.length - 1]; + + /* Main Simulation Loop: We go thourgh all the candles at this time period. */ + let i + + /* For Loop Level heartbeat */ + let loopingDay + let previousLoopingDay + + initializeLoop() + + function initializeLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> initializeLoop -> Entering function."); } + + /* Estimate Initial Candle */ + + let firstEnd = candles[0].end + let targetEnd = bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() + let diff = targetEnd - firstEnd + let amount = diff / timePeriod + + i = Math.trunc(amount) + if (i < 0) { i = 0 } + if (i > candles.length - 1) { + /* This will happen when the bot.VALUES_TO_USE.timeRange.initialDatetime is beyond the last candle available, meaning that the dataSet needs to be updated with more up-to-date data. */ + i = candles.length - 1 + } + + loop() + } + + function loop() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + i); } + + let candle = candles[i]; + + /* Not processing while out of user-defined time range */ + + if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle before the bot.VALUES_TO_USE.timeRange.initialDatetime."); } + controlLoop(); + return + } + if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle after the bot.VALUES_TO_USE.timeRange.finalDatetime."); } + afterLoop(); + return + } + + /* Here we update at the chart data structure the objects for each product representing where we are currently standing at the simulation loop */ + if (processingDailyFiles) { + for (let j = 0; j < global.dailyFilePeriods.length; j++) { + + let mapKey = dailyFilePeriods[j][1] + let propertyName = 'at' + mapKey.replace('-', ''); + let thisChart = chart[propertyName] + + for (let k = 0; k < dataDependencies.length; k++) { + let dataDependencyNode = dataDependencies[k] + let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName + let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName + let currentElement = getElement(thisChart[pluralVariableName], candle, 'Daily' + '-' + mapKey + '-' + pluralVariableName) + thisChart[singularVariableName] = currentElement + } + } + } + + for (let j = 0; j < global.marketFilesPeriods.length; j++) { + + let mapKey = marketFilesPeriods[j][1] + let propertyName = 'at' + mapKey.replace('-', ''); + let thisChart = chart[propertyName] + + for (let k = 0; k < dataDependencies.length; k++) { + let dataDependencyNode = dataDependencies[k] + let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName + let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName + let currentElement = getElement(thisChart[pluralVariableName], candle, 'Market' + '-' + mapKey + '-' + pluralVariableName) + thisChart[singularVariableName] = currentElement + } + } + + /* While we are processing the previous day. */ + let positionedAtYesterday = false + if (currentDay) { + positionedAtYesterday = (candle.end < currentDay.valueOf()) + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle Begin @ " + (new Date(candle.begin)).toLocaleString()) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle End @ " + (new Date(candle.end)).toLocaleString()) } + + /* Assistant Info */ + + let ticker = { + bid: candle.close, + ask: candle.close, + last: candle.close + } + + /* We will produce a simulation level heartbeat in order to inform the user this is running. */ + + loopingDay = new Date(Math.trunc(candle.begin / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + if (loopingDay.valueOf() !== previousLoopingDay) { + + let processingDate = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + i + " @ " + processingDate) } + console.log("Jason -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + i + " @ " + processingDate) + + bot.sessionHeartBeat(processingDate) // tell the world we are alive and doing well + } + previousLoopingDay = loopingDay.valueOf() + + /* If any of the needed data dependencies is missing for this particular candle, then we jump the candle*/ + for (let k = 0; k < dataDependencies.length; k++) { + let dataDependencyNode = dataDependencies[k] + let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName + let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName + let product = currentChart[pluralVariableName] + let currentElement = currentChart[singularVariableName] + + if (currentElement === undefined) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because " + singularVariableName + " is undefined."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> For some indicators it is normal to start after a few candles."); } + controlLoop(); + return + } + } + + periods++; + days = periods * timePeriod / ONE_DAY_IN_MILISECONDS; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.periods = periods + } + + /* We skip the candle at the head of the market because i has not closed yet. */ + let candlesPerDay = ONE_DAY_IN_MILISECONDS / timePeriod + if (i === candles.length - 1) { + if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) { + /*We are at the head of the market, thus we skip the last candle because it has not close yet. */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } + controlLoop(); + return + /* Note here that in the last candle of the first day or the second day it will use an incomplete candle and partially calculated indicators. + if we skip these two periods, then there will be a hole in the file since the last period will be missing. */ + } + } + + } else { // We are processing Market Files + if (i === candles.length - 1) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } + controlLoop(); + return + } + } + + let conditions = new Map; // Here we store the conditions values that will be use in the simulator for decision making. + let formulas = new Map; + let conditionsArrayRecord = []; // These are the records that will be saved in a file for the plotter to consume. + let conditionsArrayValues = []; // Here we store the conditions values that will be written on file for the plotter. + let formulasErrors = []; // Here we store the errors produced by all phase formulas. + let formulasValues = []; // Here we store the values produced by all phase formulas. + + /* We define and evaluate all conditions to be used later during the simulation loop. */ + + conditionsArrayRecord.push(candle.begin); + conditionsArrayRecord.push(candle.end); + + for (let j = 0; j < tradingSystem.strategies.length; j++) { + + let strategy = tradingSystem.strategies[j]; + + let positionSize = 0 + let positionRate = 0 + + /* Continue with the rest of the formulas and conditions */ + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOn !== undefined) { + + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + + let situation = triggerStage.triggerOn.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + + if (triggerStage.triggerOff !== undefined) { + + for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) { + + let situation = triggerStage.triggerOff.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + + if (triggerStage.takePosition !== undefined) { + + for (let k = 0; k < triggerStage.takePosition.situations.length; k++) { + + let situation = triggerStage.takePosition.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + + let openStage = strategy.openStage + + if (openStage !== undefined) { + + /* Default Values*/ + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + positionSize = balanceAssetA; + positionRate = candle.close; + } else { + positionSize = balanceAssetB; + positionRate = candle.close; + } + + let initialDefinition = openStage.initialDefinition + + if (initialDefinition !== undefined) { + + if (tradePositionSize !== 0) { + positionSize = tradePositionSize + } else { + if (initialDefinition.positionSize !== undefined) { + if (initialDefinition.positionSize.formula !== undefined) { + try { + positionSize = eval(initialDefinition.positionSize.formula.code); + } catch (err) { + initialDefinition.positionSize.formula.error = err.message + } + if (isNaN(positionSize)) { + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + positionSize = balanceAssetA; + } else { + positionSize = balanceAssetB; + } + } else { + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (positionSize > balanceAssetA) { positionSize = balanceAssetA } + } else { + if (positionSize > balanceAssetB) { positionSize = balanceAssetB } + } + } + } + } + } + + if (tradePositionRate !== 0) { + positionRate = tradePositionRate + } else { + if (initialDefinition.positionRate !== undefined) { + if (initialDefinition.positionRate.formula !== undefined) { + try { + positionRate = eval(initialDefinition.positionRate.formula.code); + } catch (err) { + initialDefinition.positionRate.formula.error = err.message + } + if (isNaN(positionRate)) { + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + positionRate = candle.close; + } else { + positionRate = candle.close; + } + } + } + } + } + + if (initialDefinition.stopLoss !== undefined) { + + for (let p = 0; p < initialDefinition.stopLoss.phases.length; p++) { + + let phase = initialDefinition.stopLoss.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_STOP_LOSS_VALUE + if (stopLossStage === 'Open Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.stopLoss -> MAX_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_STOP_LOSS_VALUE) { + formulaValue = MIN_STOP_LOSS_VALUE + if (stopLossStage === 'Open Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_STOP_LOSS_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.stopLoss -> MIN_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + + if (initialDefinition.takeProfit !== undefined) { + + for (let p = 0; p < initialDefinition.takeProfit.phases.length; p++) { + + let phase = initialDefinition.takeProfit.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaValue = MAX_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Open Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.takeProfit -> MAX_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_TAKE_PROFIT_VALUE) { + formulaValue = MIN_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Open Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_TAKE_PROFIT_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.takeProfit -> MIN_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + } + + strategy.positionSize = positionSize + strategy.positionRate = positionRate + } + + let manageStage = strategy.manageStage + + if (manageStage !== undefined) { + + if (manageStage.stopLoss !== undefined) { + + for (let p = 0; p < manageStage.stopLoss.phases.length; p++) { + + let phase = manageStage.stopLoss.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "" + formulaValue = MAX_STOP_LOSS_VALUE + if (stopLossStage === 'Manage Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.stopLoss -> MAX_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_STOP_LOSS_VALUE) { + formulaValue = MIN_STOP_LOSS_VALUE + if (stopLossStage === 'Manage Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_STOP_LOSS_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.stopLoss -> MIN_STOP_LOSS_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + + if (manageStage.takeProfit !== undefined) { + + for (let p = 0; p < manageStage.takeProfit.phases.length; p++) { + + let phase = manageStage.takeProfit.phases[p]; + + /* Evaluate Formula */ + let formulaValue + let formulaError = '' + + if (phase.formula !== undefined) { + try { + formulaValue = eval(phase.formula.code); + if (formulaValue === Infinity) { + formulaError = "Formula evaluates to Infinity." + formulaValue = MAX_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Manage Stage') { + formulaError = "WARNING: Formula evaluates to Infinity." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.takeProfit -> MAX_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + } catch (err) { + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } + } + if (isNaN(formulaValue)) { formulaValue = 0; } + if (formulaValue < MIN_TAKE_PROFIT_VALUE) { + formulaValue = MIN_TAKE_PROFIT_VALUE + if (takeProfitStage === 'Manage Stage') { + formulaError = "WARNING: Formula is evaluating below the MIN_TAKE_PROFIT_VALUE." + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.takeProfit -> MIN_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); } + } + } + + formulasErrors.push('"' + formulaError + '"') + formulasValues.push(formulaValue) + let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p; + formulas.set(key, formulaValue) + } + + /* next phase event */ + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + if (condition.code !== undefined) { + newCondition(key, condition.code); + } + } + } + } + } + } + } + + function newCondition(key, node) { + + let condition; + let error = '' + let value + + try { + value = eval(node.code); + } catch (err) { + /* + One possible error is that the conditions references a .previous that is undefined. For this + reason and others, we will simply set the value to false. + */ + value = false + + if (node.code.indexOf('previous') > -1 && err.message.indexOf('of undefined') > -1 || + node.code.indexOf('chart') > -1 && err.message.indexOf('of undefined') > -1 + ) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + node.error = err.message + " @ " + (new Date(candle.begin)).toLocaleString() + } + } + + condition = { + key: key, + value: value + }; + + conditions.set(condition.key, condition); + + if (condition.value) { + conditionsArrayValues.push(1); + } else { + conditionsArrayValues.push(0); + } + } + } + + /* Trigger On Conditions */ + if ( + strategyStage === 'No Stage' && + currentStrategyIndex === -1 + ) { + let minimumBalance + let maximumBalance + let balance + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + balance = balanceAssetA + minimumBalance = bot.VALUES_TO_USE.minimumBalanceA + maximumBalance = bot.VALUES_TO_USE.maximumBalanceA + } else { + balance = balanceAssetB + minimumBalance = bot.VALUES_TO_USE.minimumBalanceB + maximumBalance = bot.VALUES_TO_USE.maximumBalanceB + } + + if (balance > minimumBalance && balance < maximumBalance) { + + /* + Here we need to pick a strategy, or if there is not suitable strategy for the current + market conditions, we pass until the next period. + + To pick a new strategy we will evaluate what we call the trigger on. Once we enter + into one strategy, we will ignore market conditions for others. However there is also + a strategy trigger off which can be hit before taking a position. If hit, we would + be outside a strategy again and looking for the condition to enter all over again. + + */ + + for (let j = 0; j < tradingSystem.strategies.length; j++) { + + let strategy = tradingSystem.strategies[j]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOn !== undefined) { + + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + + let situation = triggerStage.triggerOn.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + strategyStage = 'Trigger Stage'; + currentStrategyIndex = j; + currentStrategy.begin = candle.begin; + currentStrategy.beginRate = candle.min; + currentStrategy.endRate = candle.min; // In case the strategy does not get exited + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.currentStrategyIndex = currentStrategyIndex; + yesterday.currentStrategy.begin = currentStrategy.begin; + yesterday.currentStrategy.beginRate = currentStrategy.beginRate; + yesterday.currentStrategy.endRate = currentStrategy.endRate; + } + } + + distanceToLast.triggerOn = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn + } + } + + checkAnnouncements(triggerStage.triggerOn) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Switching to Trigger Stage because conditions at Trigger On Event were met."); } + break; + } + } + } + } + } + } else { + let stopRunningDate = new Date(candle.begin) + if (balance <= minimumBalance) { + tradingSystem.error = "Min Balance @ " + stopRunningDate.toLocaleString() + } + if (balance >= maximumBalance) { + tradingSystem.error = "Max Balance @ " + stopRunningDate.toLocaleString() + } + } + } + + /* Trigger Off Condition */ + if (strategyStage === 'Trigger Stage') { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.triggerOff !== undefined) { + + for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) { + + let situation = triggerStage.triggerOff.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + currentStrategy.number = currentStrategyIndex + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; + strategyStage = 'No Stage'; + currentStrategyIndex = -1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentStrategy.number = currentStrategy.number; + yesterday.currentStrategy.end = currentStrategy.end; + yesterday.currentStrategy.endRate = currentStrategy.endRate; + yesterday.currentStrategy.status = currentStrategy.status; + yesterday.strategyStage = strategyStage; + yesterday.currentStrategyIndex = currentStrategyIndex; + } + } + + distanceToLast.triggerOff = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + } + } + + checkAnnouncements(triggerStage.triggerOff) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Switching to No Stage because conditions at the Trigger Off Event were met."); } + break; + } + } + } + } + } + + /* Take Position Condition */ + if (strategyStage === 'Trigger Stage') { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let triggerStage = strategy.triggerStage + + if (triggerStage !== undefined) { + + if (triggerStage.takePosition !== undefined) { + + for (let k = 0; k < triggerStage.takePosition.situations.length; k++) { + + let situation = triggerStage.takePosition.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + type = '"Take Position"'; + + strategyStage = 'Open Stage'; + stopLossStage = 'Open Stage'; + takeProfitStage = 'Open Stage'; + stopLossPhase = 0; + takeProfitPhase = 0; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + yesterday.stopLossPhase = stopLossPhase; + yesterday.takeProfitPhase = takeProfitPhase; + } + } + + takePositionNow = true + + checkAnnouncements(triggerStage.takePosition) + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Conditions at the Take Position Event were met."); } + break; + } + } + } + } + } + + /* Stop Loss Management */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + + checkStopPhases() + calculateStopLoss(); + + } + + function checkStopPhases() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let parentNode + let j = currentStrategyIndex + let stageKey + let initialDefinitionKey = '' + let p + + if (stopLossStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.stopLoss !== undefined) { + parentNode = openStage.initialDefinition + initialDefinitionKey = '-' + 'initialDefinition' + stageKey = 'openStage' + p = stopLossPhase + } + } + } + + if (stopLossStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.stopLoss !== undefined) { + parentNode = manageStage + stageKey = 'manageStage' + p = stopLossPhase - 1 + } + } + + let phase = parentNode.stopLoss.phases[p]; + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + stopLossPhase++; + stopLossStage = 'Manage Stage' + if (takeProfitPhase > 0) { strategyStage = 'Manage Stage' } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLossPhase = stopLossPhase; + yesterday.stopLossStage = stopLossStage; + yesterday.strategyStage = strategyStage; + } + } + + checkAnnouncements(nextPhaseEvent) + return; + } + } + } + } + + function calculateStopLoss() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let phase + let key + + if (stopLossStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.stopLoss !== undefined) { + phase = openStage.initialDefinition.stopLoss.phases[stopLossPhase]; + key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + (stopLossPhase); + } + } + } + + if (stopLossStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.stopLoss !== undefined) { + phase = manageStage.stopLoss.phases[stopLossPhase - 1]; + key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + (stopLossPhase - 1); + } + } + + if (phase !== undefined) { + if (phase.formula !== undefined) { + let previousValue = stopLoss + + stopLoss = formulas.get(key) + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + + if (stopLoss !== previousValue) { + checkAnnouncements(phase, stopLoss) + } + } + } + } + + /* Take Profit Management */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + + checkTakeProfitPhases(); + calculateTakeProfit(); + + } + + function checkTakeProfitPhases() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let parentNode + let j = currentStrategyIndex + let stageKey + let initialDefinitionKey = '' + let p + + if (takeProfitStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.takeProfit !== undefined) { + parentNode = openStage.initialDefinition + initialDefinitionKey = '-' + 'initialDefinition' + stageKey = 'openStage' + p = takeProfitPhase + } + } + } + + if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.takeProfit !== undefined) { + parentNode = manageStage + stageKey = 'manageStage' + p = takeProfitPhase - 1 + } + } + + let phase = parentNode.takeProfit.phases[p]; + + let nextPhaseEvent = phase.nextPhaseEvent; + if (nextPhaseEvent !== undefined) { + + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + + let situation = nextPhaseEvent.situations[k]; + let passed = true; + + for (let m = 0; m < situation.conditions.length; m++) { + + let condition = situation.conditions[m]; + let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m; + + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; + } + + if (value === false) { passed = false; } + } + + if (passed) { + + takeProfitPhase++; + takeProfitStage = 'Manage Stage' + if (stopLossPhase > 0) { strategyStage = 'Manage Stage' } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfitPhase = takeProfitPhase; + yesterday.takeProfitStage = takeProfitStage; + yesterday.strategyStage = strategyStage; + } + } + + checkAnnouncements(nextPhaseEvent) + return; + } + } + } + } + + function calculateTakeProfit() { + + let strategy = tradingSystem.strategies[currentStrategyIndex]; + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let phase + let key + + if (takeProfitStage === 'Open Stage' && openStage !== undefined) { + if (openStage.initialDefinition !== undefined) { + if (openStage.initialDefinition.takeProfit !== undefined) { + phase = openStage.initialDefinition.takeProfit.phases[takeProfitPhase]; + key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + (takeProfitPhase); + } + } + } + + if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) { + if (manageStage.takeProfit !== undefined) { + phase = manageStage.takeProfit.phases[takeProfitPhase - 1]; + key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + (takeProfitPhase - 1); + } + } + + if (phase !== undefined) { + if (phase.formula !== undefined) { + + let previousValue = stopLoss + + takeProfit = formulas.get(key) + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + + if (takeProfit !== previousValue) { + checkAnnouncements(phase, takeProfit) + } + } + } + } + + /* Keeping Position Counters Up-to-date */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') + ) { + + if (takePositionNow === true) { + positionPeriods = 0 + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.positionPeriods = 0 + } + } + } + + positionPeriods++; + positionDays = positionPeriods * timePeriod / ONE_DAY_IN_MILISECONDS; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.positionPeriods = positionPeriods + } + } + } else { + positionPeriods = 0 + positionDays = 0 + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.positionPeriods = 0 + } + } + } + + /* Keeping Distance Counters Up-to-date */ + if ( + distanceToLast.triggerOn > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.triggerOn++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn + } + } + } + + if ( + distanceToLast.triggerOff > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.triggerOff++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + } + } + } + + if ( + distanceToLast.takePosition > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.takePosition++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.takePosition = distanceToLast.takePosition + } + } + } + + if ( + distanceToLast.closePosition > 0 // with this we avoind counting before the first event happens. + ) { + distanceToLast.closePosition++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.takePosition = distanceToLast.closePosition + } + } + } + + /* Checking if Stop or Take Profit were hit */ + if ( + (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') && + takePositionNow !== true + ) { + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + /* Checking what happened since the last execution. We need to know if the Stop Loss + or our Take Profit were hit. */ + + /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ + + if ((bot.VALUES_TO_USE.baseAsset === 'BTC' && candle.max >= stopLoss) || (bot.VALUES_TO_USE.baseAsset !== 'BTC' && candle.min <= stopLoss)) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Stop Loss was hit."); } + /* + Hit Point Validation + + This prevents misscalculations when a formula places the stop loss in this case way beyond the market price. + If we take the stop loss value at those situation would be a huge distortion of facts. + */ + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (stopLoss < candle.min) { + stopLoss = candle.min + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + } + } else { + if (stopLoss > candle.max) { + stopLoss = candle.max + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + } + } + + let slippedStopLoss = stopLoss + + /* Apply the Slippage */ + let slippageAmount = slippedStopLoss * bot.VALUES_TO_USE.slippage.stopLoss / 100 + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + slippedStopLoss = slippedStopLoss + slippageAmount + } else { + slippedStopLoss = slippedStopLoss - slippageAmount + } + + let finalStopLoss = slippedStopLoss; + + let feePaid = 0 + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + strategy.positionSize = balanceAssetB / finalStopLoss; + strategy.positionRate = finalStopLoss; + + feePaid = balanceAssetB / finalStopLoss * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceAssetA = balanceAssetA + balanceAssetB / finalStopLoss - feePaid; + balanceAssetB = 0; + } else { + strategy.positionSize = balanceAssetA * finalStopLoss; + strategy.positionRate = finalStopLoss; + + feePaid = balanceAssetA * finalStopLoss * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceAssetB = balanceAssetB + balanceAssetA * finalStopLoss - feePaid; + balanceAssetA = 0; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + } + } + + marketRate = finalStopLoss; + type = '"Close@StopLoss"'; + strategyStage = 'Close Stage'; + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + currentTrade.end = candle.end; + currentTrade.status = 1; + currentTrade.exitType = 1; + currentTrade.endRate = finalStopLoss; + + closePositionNow = true; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + yesterday.currentTrade.end = currentTrade.end; + yesterday.currentTrade.status = currentTrade.status; + yesterday.currentTrade.exitType = currentTrade.exitType; + yesterday.currentTrade.endRate = currentTrade.endRate; + } + } + } + + /* Take Profit condition: Here we verify if the Take Profit was hit or not. */ + + if ((bot.VALUES_TO_USE.baseAsset === 'BTC' && candle.min <= takeProfit) || (bot.VALUES_TO_USE.baseAsset !== 'BTC' && candle.max >= takeProfit)) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Take Profit was hit."); } + /* + Hit Point Validation: + + This prevents misscalculations when a formula places the take profit in this case way beyond the market price. + If we take the stop loss value at those situation would be a huge distortion of facts. + */ + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (takeProfit > candle.max) { + takeProfit = candle.max + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + } + } else { + if (takeProfit < candle.min) { + takeProfit = candle.min + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + } + } + + let slippedTakeProfit = takeProfit + /* Apply the Slippage */ + let slippageAmount = slippedTakeProfit * bot.VALUES_TO_USE.slippage.takeProfit / 100 + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + slippedTakeProfit = slippedTakeProfit + slippageAmount + } else { + slippedTakeProfit = slippedTakeProfit - slippageAmount + } + + let finalTakeProfit = slippedTakeProfit; + + let feePaid = 0 + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + strategy.positionSize = balanceAssetB / finalTakeProfit; + strategy.positionRate = finalTakeProfit; + + feePaid = balanceAssetB / finalTakeProfit * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceAssetA = balanceAssetA + balanceAssetB / finalTakeProfit - feePaid; + balanceAssetB = 0; + } else { + strategy.positionSize = balanceAssetA * finalTakeProfit; + strategy.positionRate = finalTakeProfit; + + feePaid = balanceAssetA * finalTakeProfit * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceAssetB = balanceAssetB + balanceAssetA * finalTakeProfit - feePaid; + balanceAssetA = 0; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + } + } + + marketRate = finalTakeProfit; + type = '"Close@TakeProfit"'; + strategyStage = 'Close Stage'; + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + + currentTrade.end = candle.end; + currentTrade.status = 1; + currentTrade.exitType = 2; + currentTrade.endRate = finalTakeProfit; + + closePositionNow = true; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.strategyStage = strategyStage; + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + + yesterday.currentTrade.end = currentTrade.end; + yesterday.currentTrade.status = currentTrade.status; + yesterday.currentTrade.exitType = currentTrade.exitType; + yesterday.currentTrade.endRate = currentTrade.endRate; + } + } + } + } + + /* Taking a Position */ + if ( + takePositionNow === true + ) { + takePositionNow = false + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Entering code block."); } + + /* Inicializing this counter */ + distanceToLast.takePosition = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.takePosition = distanceToLast.takePosition + } + } + + /* Position size and rate */ + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + tradePositionSize = strategy.positionSize; + tradePositionRate = strategy.positionRate; + + /* We take what was calculated at the formula and apply the slippage. */ + let slippageAmount = tradePositionRate * bot.VALUES_TO_USE.slippage.positionRate / 100 + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + tradePositionRate = tradePositionRate - slippageAmount + } else { + tradePositionRate = tradePositionRate + slippageAmount + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.tradePositionSize = tradePositionSize; + yesterday.tradePositionRate = tradePositionRate; + } + } + + /* Update the trade record information. */ + currentTrade.begin = candle.begin; + currentTrade.beginRate = tradePositionRate; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentTrade.begin = currentTrade.begin; + yesterday.currentTrade.beginRate = currentTrade.beginRate; + } + } + + /* Check if we need to execute. */ + if (i > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + /* Check that we are in LIVE MODE */ + if (bot.startMode === "Live") { + /* We see if we need to put the actual order at the exchange. */ + if (interExecutionMemory.executionContext !== undefined) { + switch (interExecutionMemory.executionContext.status) { + case "Without a Position": { // We need to put the order because It was not put yet. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + break + } + case "Position Closed": { // Waiting for a confirmation that the position was closed. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + break + } + case "Taking Position": { // Waiting for a confirmation that the position was taken. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is Taking Position."); } + break + } + case "In a Position": { // This should mean that we already put the order at the exchange. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is In a Position."); } + break + } + } + } else { // The context does not exist so it means we are not in a position. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } + } + } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Not trading live."); } + } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Not the last closed candle."); } + } + + takePositionAtSimulation() + return + + function putOpeningOrder() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function."); } + + /* We wont take a position unless we are withing the bot.VALUES_TO_USE.timeRange.initialDatetime and the bot.VALUES_TO_USE.timeRange.finalDatetime range */ + if (bot.VALUES_TO_USE.timeRange.initialDatetime !== undefined) { + if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> bot.VALUES_TO_USE.timeRange.initialDatetime = " + bot.VALUES_TO_USE.timeRange.initialDatetime); } + takePositionAtSimulation() + return; + } + } + + /*We wont take a position if we are past the final datetime */ + if (bot.VALUES_TO_USE.timeRange.finalDatetime !== undefined) { + if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> bot.VALUES_TO_USE.timeRange.finalDatetime = " + bot.VALUES_TO_USE.timeRange.finalDatetime); } + takePositionAtSimulation() + return; + } + } + + + /* Checking the status of current positions */ + let positions = assistant.getPositions(); + if (positions.length > 0) { + let position = positions[positions.length - 1] // We are allways checking the the last position is not open. + if (position.status === 'open') { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the last position is still open. "); } + afterLoop(); + return + } + } + + /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ + + if (interExecutionMemory.executionContext !== undefined) { + if (interExecutionMemory.executionContext.periods !== undefined) { + if (periods <= interExecutionMemory.executionContext.periods) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution."); } + takePositionAtSimulation() + return; + } + } + } + + /* We are not going to place orders based on outdated information. The next filter prevents firing orders when backtesting. */ + if (currentDay) { + let today = new Date(Math.trunc((new Date().valueOf()) / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + let processDay = new Date(Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS) + if (today.valueOf() !== processDay.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the current candle belongs to the previous day and that is considered simulation and not live trading."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> today = " + today); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> processDay = " + processDay); } + takePositionAtSimulation() + return; + } + } + + let openPositionRate + let amountA + let amountB + let positionDirection + let availableBalance = assistant.getAvailableBalance() + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + positionDirection = "sell" + + openPositionRate = tradePositionRate - 100 // 100 is Provisional since we do have management of orders yet + + amountA = tradePositionSize * openPositionRate + amountB = tradePositionSize + + if (amountB > availableBalance.assetB) { // The assistant know what fees were paid. + amountB = availableBalance.assetB + } + } else { + positionDirection = "buy" + + openPositionRate = tradePositionRate + 100 // 100 is Provisional since we do have management of orders yet + + amountA = tradePositionSize + amountB = tradePositionSize / openPositionRate + + if (amountA > availableBalance.assetA) { // The assistant know what fees were paid. + amountA = availableBalance.assetA + } + } + + interExecutionMemory.executionContext = { + status: "Taking Position", + periods: periods, + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to put order."); } + assistant.putPosition(positionDirection, openPositionRate, amountA, amountB, onOrderPut) + + function onOrderPut(err) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Entering function."); } + + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); } + interExecutionMemory.executionContext = { + status: "In a Position", + periods: periods, + amountA: amountA, + amountB: amountB + } + takePositionAtSimulation() + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Message = " + err.message); } + strategy.openStage.openExecution.error = err.message + afterLoop() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Message = " + err.message); } + strategy.openStage.openExecution.error = err.message + afterLoop() + return; + } + } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + } + + function takePositionAtSimulation() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionAtSimulation -> Entering function."); } + + /* Continue with the simulation */ + calculateTakeProfit(); + calculateStopLoss(); + + marketRate = candle.close; + + previousBalanceAssetA = balanceAssetA; + previousBalanceAssetB = balanceAssetB; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.previousBalanceAssetA = previousBalanceAssetA; + yesterday.previousBalanceAssetB = previousBalanceAssetB; + } + } + + lastTradeProfitLoss = 0; + lastTradeROI = 0; + + let feePaid = 0 + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + + feePaid = tradePositionSize * tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceAssetB = balanceAssetB + tradePositionSize * tradePositionRate - feePaid; + balanceAssetA = balanceAssetA - tradePositionSize; + } else { + + feePaid = tradePositionSize / tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceAssetA = balanceAssetA + tradePositionSize / tradePositionRate - feePaid; + balanceAssetB = balanceAssetB - tradePositionSize; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.balanceAssetA = balanceAssetA; + yesterday.balanceAssetB = balanceAssetB; + + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.lastTradeROI = lastTradeROI; + } + } + + addRecord(); + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionAtSimulation -> Exiting Loop Body after taking position at simulation."); } + controlLoop(); + return + } + } + + if (closePositionNow === true) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Entering code block."); } + + closePositionNow = false + + /* Inicializing this counter */ + distanceToLast.closePosition = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.closePosition = distanceToLast.closePosition + } + } + + /* Position size and rate */ + let strategy = tradingSystem.strategies[currentStrategyIndex]; + + if (i > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + /* Check that we are in LIVE MODE */ + if (bot.startMode === "Live") { + /* We see if we need to put the actual order at the exchange. */ + if (interExecutionMemory.executionContext !== undefined) { + switch (interExecutionMemory.executionContext.status) { + case "Without a Position": { // No way to close anything at the exchange. + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Without a Position."); } + break + } + case "In a Position": { // This should mean that we already put the order at the exchange. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return + } + } + break + } + case "Closing Position": { // Waiting for a confirmation that the position was taken. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return + } + } + break + } + + case "Position Closed": { // + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Position Closed."); } + break + } + } + } + } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Not within the last 10 candles."); } + } + + closePositionAtSimulation() + return + + function putClosingOrder() { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> Entering function."); } + + /* Checking the status of current positions */ + let positions = assistant.getPositions(); + if (positions.length > 0) { + let position = positions[positions.length - 1] // We are allways checking the the last position is not open. + if (position.status === 'open') { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because status of last position is Open."); } + afterLoop(); + return + } + } + + /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ + + if (interExecutionMemory.executionContext !== undefined) { + if (interExecutionMemory.executionContext.periods !== undefined) { + if (periods <= interExecutionMemory.executionContext.periods) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution."); } + closePositionAtSimulation() + return; + } + } + } + + let closePositionRate + let amountA + let amountB + let positionDirection + let availableBalance = assistant.getAvailableBalance() + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + positionDirection = "buy" + + closePositionRate = ticker.last + 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + + amountA = availableBalance.assetA + amountB = availableBalance.assetA / closePositionRate + + } else { + positionDirection = "sell" + + closePositionRate = ticker.last - 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + + amountA = availableBalance.assetB * closePositionRate + amountB = availableBalance.assetB + + } + + interExecutionMemory.executionContext = { + status: "Closing Position", + periods: periods, + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange."); } + assistant.putPosition(positionDirection, closePositionRate, amountA, amountB, onOrderPut) + + function onOrderPut(err) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> Entering function."); } + + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); } + interExecutionMemory.executionContext = { + status: "Position Closed", + periods: periods, + amountA: amountA, + amountB: amountB + } + closePositionAtSimulation() + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); } + /* We will assume that the problem is temporary, and expect that it will work at the next execution.*/ + strategy.closeStage.closeExecution.error = err.message + afterLoop() + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); } + strategy.closeStage.closeExecution.error = err.message + afterLoop() + return; + } + } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderPut -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + } + + function closePositionAtSimulation() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> closePositionAtSimulation -> Entering function."); } + + roundtrips++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.roundtrips = roundtrips + } + } + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + lastTradeProfitLoss = balanceAssetA - previousBalanceAssetA; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; + if (isNaN(lastTradeROI)) { lastTradeROI = 0; } + profit = balanceAssetA - bot.VALUES_TO_USE.initialBalanceA; + } else { + lastTradeProfitLoss = balanceAssetB - previousBalanceAssetB; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; + if (isNaN(lastTradeROI)) { lastTradeROI = 0; } + profit = balanceAssetB - bot.VALUES_TO_USE.initialBalanceB; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.lastTradeProfitLoss = lastTradeProfitLoss; + yesterday.profit = profit; + yesterday.lastTradeROI = lastTradeROI; + } + } + + currentTrade.lastTradeROI = lastTradeROI; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentTrade.lastTradeROI = currentTrade.lastTradeROI; + } + } + + if (lastTradeProfitLoss > 0) { + hits++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.hits = hits + } + } + + } else { + fails++; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.fails = fails + } + } + } + + if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + ROI = (bot.VALUES_TO_USE.initialBalanceA + profit) / bot.VALUES_TO_USE.initialBalanceA - 1; + hitRatio = hits / roundtrips; + anualizedRateOfReturn = ROI / days * 365; + } else { + ROI = (bot.VALUES_TO_USE.initialBalanceB + profit) / bot.VALUES_TO_USE.initialBalanceB - 1; + hitRatio = hits / roundtrips; + anualizedRateOfReturn = ROI / days * 365; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.ROI = ROI; + yesterday.hitRatio = hitRatio; + yesterday.anualizedRateOfReturn = anualizedRateOfReturn; + } + } + + addRecord(); + + stopLoss = 0; + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLoss = stopLoss + } + } + takeProfit = 0; + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.takeProfit = takeProfit + } + } + + tradePositionRate = 0; + tradePositionSize = 0; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.tradePositionSize = tradePositionSize; + yesterday.tradePositionRate = tradePositionRate; + } + } + + timerToCloseStage = candle.begin + stopLossStage = 'No Stage'; + takeProfitStage = 'No Stage'; + stopLossPhase = -1; + takeProfitPhase = -1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.stopLossStage = stopLossStage; + yesterday.takeProfitStage = takeProfitStage; + yesterday.stopLossPhase = stopLossPhase; + yesterday.takeProfitPhase = takeProfitPhase; + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> closePositionAtSimulation -> Exiting Loop Body after closing position at simulation."); } + controlLoop(); + return + } + } + + /* Closing the Closing Stage */ + if (strategyStage === 'Close Stage') { + if (candle.begin - 5 * 60 * 1000 > timerToCloseStage) { + + currentStrategy.number = currentStrategyIndex + currentStrategy.end = candle.end; + currentStrategy.endRate = candle.min; + currentStrategy.status = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + + yesterday.currentStrategy.number = currentStrategy.number; + yesterday.currentStrategy.end = currentStrategy.end; + yesterday.currentStrategy.endRate = currentStrategy.endRate; + yesterday.currentStrategy.status = currentStrategy.status; + } + } + + currentStrategyIndex = -1; + strategyStage = 'No Stage'; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentStrategyIndex = currentStrategyIndex; + yesterday.strategyStage = strategyStage; + } + } + + timerToCloseStage = 0 + + distanceToLast.triggerOff = 1; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing the Closing Stage -> Exiting Close Stage."); } + } else { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing the Closing Stage -> Waiting for timer."); } + } + } + + /* Not a buy or sell condition */ + + marketRate = candle.close; + addRecord(); + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Exiting Loop Body after adding a record."); } + controlLoop(); + return + + function addRecord() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> addRecord -> Entering function."); } + + // Since we are going to write the message to a file that the Simulation Executor is going to read, we use the abbreviations. + let messageType; + let message; + let simulationRecord; + let executionRecord; + + let strategyStageNumber + switch (strategyStage) { + case 'No Stage': { + strategyStageNumber = 0 + break + } + case 'Trigger Stage': { + strategyStageNumber = 1 + break + } + case 'Open Stage': { + strategyStageNumber = 2 + break + } + case 'Manage Stage': { + strategyStageNumber = 3 + break + } + case 'Close Stage': { + strategyStageNumber = 4 + break + } + } + + if (balanceAssetA === Infinity) { + balanceAssetA = Number.MAX_SAFE_INTEGER + } + + if (balanceAssetB === Infinity) { + balanceAssetB = Number.MAX_SAFE_INTEGER + } + + let quotedBaseAsset = '"' + bot.VALUES_TO_USE.baseAsset + '"' + + simulationRecord = { + begin: candle.begin, + end: candle.end, + type: type, + marketRate: marketRate, + amount: 1, + balanceA: balanceAssetA, + balanceB: balanceAssetB, + profit: profit, + lastTradeProfitLoss: lastTradeProfitLoss, + stopLoss: stopLoss, + roundtrips: roundtrips, + hits: hits, + fails: fails, + hitRatio: hitRatio, + ROI: ROI, + periods: periods, + days: days, + anualizedRateOfReturn: anualizedRateOfReturn, + positionRate: tradePositionRate, + lastTradeROI: lastTradeROI, + strategy: currentStrategyIndex, + strategyStageNumber: strategyStageNumber, + takeProfit: takeProfit, + stopLossPhase: stopLossPhase, + takeProfitPhase: takeProfitPhase, + executionRecord: '', + positionSize: tradePositionSize, + initialBalanceA: bot.VALUES_TO_USE.initialBalanceA, + minimumBalanceA: bot.VALUES_TO_USE.minimumBalanceA, + maximumBalanceA: bot.VALUES_TO_USE.maximumBalanceA, + initialBalanceB: bot.VALUES_TO_USE.initialBalanceB, + minimumBalanceB: bot.VALUES_TO_USE.minimumBalanceB, + maximumBalanceB: bot.VALUES_TO_USE.maximumBalanceB, + baseAsset: quotedBaseAsset, + positionPeriods: positionPeriods, + positionDays: positionDays, + distanceToLastTriggerOn: distanceToLast.triggerOn, + distanceToLastTriggerOff: distanceToLast.triggerOff, + distanceToLastTakePosition: distanceToLast.takePosition, + distanceToLastClosePosition: distanceToLast.closePosition + } + + recordsArray.push(simulationRecord); + + type = '""'; + + /* Prepare the information for the Conditions File */ + + conditionsArrayRecord.push(currentStrategyIndex); + conditionsArrayRecord.push(strategyStageNumber); + conditionsArrayRecord.push(stopLossPhase); + conditionsArrayRecord.push(takeProfitPhase); + conditionsArrayRecord.push(conditionsArrayValues); + conditionsArrayRecord.push(formulasErrors); + conditionsArrayRecord.push(formulasValues); + + conditionsArray.push(conditionsArrayRecord); + + /* Prepare the information for the Strategies File*/ + + if ( + (currentStrategy.begin !== 0 && currentStrategy.end !== 0) || + (currentStrategy.begin !== 0 && i === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) + ) { + + strategiesArray.push(currentStrategy); + + currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentStrategy = { + begin: 0, + end: 0, + status: 0, + number: 0, + beginRate: 0, + endRate: 0 + } + } + } + } + + /* Prepare the information for the Trades File */ + + if ( + (currentTrade.begin !== 0 && currentTrade.end !== 0) || + (currentTrade.begin !== 0 && i === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) + ) { + + currentTrade.profit = lastTradeProfitLoss; + + tradesArray.push(currentTrade); + + currentTrade = { + begin: 0, + end: 0, + status: 0, + lastTradeROI: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentTrade = { + begin: 0, + end: 0, + status: 0, + lastTradeROI: 0, + exitType: 0, + beginRate: 0, + endRate: 0 + } + } + } + } + } + + function checkAnnouncements(node, value) { + /* + Value is an optional parameter that represents the value that the announcement is monitoring for change. + If we do receive this value, we will only make the annoucement if the variance is grater than the user pre-defined value + for this variance. + */ + + if (node.announcements !== undefined) { + for (let i = 0; i < node.announcements.length; i++) { + let announcement = node.announcements[i] + let key = node.type + "-" + announcement.name + "-" + announcement.id + + let lastPeriodAnnounced = -1 + let newAnnouncementRecord = {} + + for (let j = 0; j < interExecutionMemory.announcements.length; j++) { + let announcementRecord = interExecutionMemory.announcements[j] + if (announcementRecord.key === key) { + lastPeriodAnnounced = announcementRecord.periods + newAnnouncementRecord = announcementRecord + break + } + } + + if (periods > lastPeriodAnnounced) { + + /* The Value Variation is what tells us how much the value already announced must change in order to annouce it again. */ + let valueVariantion + try { + let code = JSON.parse(announcement.code) + valueVariantion = code.valueVariantion + } catch (err) { + announcement.error = err.message + } + + if (newAnnouncementRecord.value !== undefined && valueVariantion !== undefined) { + let upperLimit = newAnnouncementRecord.value + newAnnouncementRecord.value * valueVariantion / 100 + let lowerLimit = newAnnouncementRecord.value - newAnnouncementRecord.value * valueVariantion / 100 + if (value > lowerLimit && value < upperLimit) { + /* There is not enough variation to announce this again. */ + return + } + } + + /* Here we check if there is a formula attached to the annoucement, we evaluate it to get the annoucement text. */ + let formulaValue + if (announcement.formula !== undefined) { + try { + formulaValue = eval(announcement.formula.code); + } catch (err) { + announcement.formula.error = err.message + } + } + announcement.formulaValue = formulaValue + + bot.SESSION.socialBots.announce(announcement) + + /* Next, we will remmeber this announcement was already done, so that it is not announced again in further processing of the same day. */ + if (newAnnouncementRecord.periods !== undefined) { + newAnnouncementRecord.periods = periods + newAnnouncementRecord.value = value + } else { + newAnnouncementRecord = { + key: key, + periods: periods, + value: value + } + interExecutionMemory.announcements.push(newAnnouncementRecord) + } + } + } + } + } + } + + function controlLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> Entering function."); } + + /* Checking if we should continue processing this loop or not.*/ + if (bot.STOP_SESSION === true) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing."); } + console.log("[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing.") + afterLoop() + return + } + + i++ + if (i < candles.length) { + setImmediate(loop) // This will execute the next loop in the next iteration of the NodeJs event loop allowing for other callbacks to be executed. + } else { + afterLoop() + } + } + + function afterLoop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> afterLoop -> Entering function."); } + + /* + Before returning we need to see if we have to record some of our counters at the interExecutionMemory. + To do that, the condition to be met is that this execution must include all candles of the current day. + */ + + if (processingDailyFiles) { + + if (lastCandle.end === lastInstantOfTheDay) { + + interExecutionMemory.stopLoss = yesterday.stopLoss + interExecutionMemory.takeProfit = yesterday.takeProfit + + interExecutionMemory.previousBalanceAssetA = yesterday.previousBalanceAssetA + interExecutionMemory.previousBalanceAssetB = yesterday.previousBalanceAssetB + + interExecutionMemory.tradePositionRate = yesterday.tradePositionRate + interExecutionMemory.tradePositionSize = yesterday.tradePositionSize + + interExecutionMemory.currentStrategyIndex = yesterday.currentStrategyIndex + interExecutionMemory.strategyStage = yesterday.strategyStage + + interExecutionMemory.stopLossPhase = yesterday.stopLossPhase + interExecutionMemory.stopLossStage = yesterday.stopLossStage + + interExecutionMemory.takeProfitPhase = yesterday.takeProfitPhase + interExecutionMemory.takeProfitStage = yesterday.takeProfitStage + + interExecutionMemory.currentStrategy = { + begin: yesterday.currentStrategy.begin, + end: yesterday.currentStrategy.end, + status: yesterday.currentStrategy.status, + number: yesterday.currentStrategy.number, + beginRate: yesterday.currentStrategy.beginRate, + endRate: yesterday.currentStrategy.endRate + } + + interExecutionMemory.currentTrade = { + begin: yesterday.currentTrade.begin, + end: yesterday.currentTrade.end, + status: yesterday.currentTrade.status, + profit: yesterday.currentTrade.profit, + exitType: yesterday.currentTrade.exitType, + beginRate: yesterday.currentTrade.beginRate, + endRate: yesterday.currentTrade.endRate + } + + interExecutionMemory.balanceAssetA = yesterday.balanceAssetA; + interExecutionMemory.balanceAssetB = yesterday.balanceAssetB; + interExecutionMemory.lastTradeProfitLoss = yesterday.lastTradeProfitLoss; + interExecutionMemory.profit = yesterday.profit; + interExecutionMemory.lastTradeROI = yesterday.lastTradeROI; + + interExecutionMemory.roundtrips = yesterday.roundtrips; + interExecutionMemory.fails = yesterday.fails; + interExecutionMemory.hits = yesterday.hits; + interExecutionMemory.periods = yesterday.periods; + interExecutionMemory.positionPeriods = yesterday.positionPeriods; + + interExecutionMemory.distanceToLast.triggerOn = yesterday.distanceToLast.triggerOn + interExecutionMemory.distanceToLast.triggerOff = yesterday.distanceToLast.triggerOff + interExecutionMemory.distanceToLast.takePosition = yesterday.distanceToLast.takePosition + interExecutionMemory.distanceToLast.closePosition = yesterday.distanceToLast.closePosition + + interExecutionMemory.messageId = interExecutionMemory.messageId + yesterday.messageId; + interExecutionMemory.orderId = interExecutionMemory.orderId + yesterday.orderId; + + interExecutionMemory.hitRatio = yesterday.hitRatio; + interExecutionMemory.ROI = yesterday.ROI; + interExecutionMemory.anualizedRateOfReturn = yesterday.anualizedRateOfReturn; + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> callback -> recordsArray.length = " + recordsArray.length); } + + callback(tradingSystem, recordsArray, conditionsArray, strategiesArray, tradesArray); + } + + function getElement(pArray, currentCandle, datasetName) { + + try { + let element; + for (let i = 0; i < pArray.length; i++) { + element = pArray[i]; + + if (currentCandle.end === element.end) { // when there is an exact match at the end we take that element + return element + } else { + if ( + i > 0 && + element.end > currentCandle.end + ) { + let previousElement = pArray[i - 1] + if (previousElement.end < currentCandle.end) { + return previousElement // If one elements goes into the future of currentCandle, then we stop and take the previous element. + } else { + return + } + } + if ( + i === pArray.length - 1 // If we reach the end of the array, then we return the last element. + && + element.end < currentCandle.end + ) { + return element + } + } + } + return + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> getElement -> datasetName = " + datasetName); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] runSimulation -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; + + + From 740e0a92c701c934f7cd6ed03f125f6b69596128 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 20 Nov 2019 12:41:48 +0100 Subject: [PATCH 30/38] isNumeric turned into isString --- Commons.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Commons.js b/Commons.js index d38d935eb..bf9d4dde4 100644 --- a/Commons.js +++ b/Commons.js @@ -386,11 +386,11 @@ let property = recordDefinition.properties[j] if (property.code.isCalculated !== true) { fileContent = fileContent + propertySeparator - if (property.code.isNumeric !== true) { + if (property.code.isString === true) { fileContent = fileContent + '"' } fileContent = fileContent + record[property.code.codeName] - if (property.code.isNumeric !== true) { + if (property.code.isString === true) { fileContent = fileContent + '"' } if (propertySeparator === "") { propertySeparator = ","; } From 6023c3479962e3d764637224c2eca4cfb56f3fb5 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 20 Nov 2019 13:13:03 +0100 Subject: [PATCH 31/38] Saving Files Working --- TradingBot.js | 911 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 840 insertions(+), 71 deletions(-) diff --git a/TradingBot.js b/TradingBot.js index cb4891579..c04bad9d1 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -103,9 +103,26 @@ } } + /* From here we have more or less the same we used to have at JASON */ const TRADING_SIMULATION = require('./TradingSimulation.js'); let tradingSimulation = TRADING_SIMULATION.newTradingSimulation(bot, logger, UTILITIES); + let market = global.MARKET; + + const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation"; + const CONDITIONS_FOLDER_NAME = "Simulation-Conditions"; + const STRATEGIES_FOLDER_NAME = "Simulation-Strategies"; + const TRADES_FOLDER_NAME = "Simulation-Trades"; + + const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000; + + let recordsArray + let conditionsArray + let strategiesArray + let tradesArray + + let tradingSystem = {}; + tradingSimulation.runSimulation( chart, dataDependencies, @@ -114,85 +131,837 @@ currentDay, interExecutionMemory, assistant, - onSimulationFinished, + writeFiles, callBackFunction) + function writeFiles(pTradingSystem, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray) { - function onSimulationFinished() { - console.log("Finished") + tradingSystem = pTradingSystem + recordsArray = pRecordsArray + conditionsArray = pConditionsArray + strategiesArray = pStrategiesArray + tradesArray = pTradesArray - /* During the third phase, we need to generate the data of the different products this process produces an output */ - for (let i = 0; i < outputDatasets.length; i++) { - - let outputDatasetNode = outputDatasets[i] - let jsonData // Just build data as Json objects - let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure - let singularVariableName // Name of the variable for this product - let recordDefinition // Record as defined by the user at the UI - let fileContent // Here we store the contents of the new data built just before writing it to a file. - let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. - - /* - For each outputDatasetNode in our process output, we will build the information based on our input products. - */ - - recordDefinition = outputDatasetNode.referenceParent.parentNode.record - singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName - - /* Check Irregular Periods */ - if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { - resultsWithIrregularPeriods = true - } - - /* Build the data */ - jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) - - /* Add the calculated properties */ - if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { - outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod) - } else { - outputData = jsonData - } - products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData - } - - /*At the fourth and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/ - let totalFilesWritten = 0 - for (let i = 0; i < outputDatasets.length; i++) { - let outputDatasetNode = outputDatasets[i] - let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] - let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution. - let contextSummary = {} - - /* Check Irregular Periods */ - if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) { - resultsWithIrregularPeriods = true - } - - /* Simplifying the access to basic info */ - - contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName - contextSummary.product = outputDatasetNode.referenceParent.parentNode.code.codeName - contextSummary.bot = outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName - contextSummary.devTeam = outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName - - /* This stuff is still hardcoded and unresolved. */ - contextSummary.botVersion = { - "major": 1, - "minor": 0 - } - contextSummary.dataSetVersion = "dataSet.V1" - - let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) - commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, callBackFunction) + if (timePeriod > global.dailyFilePeriods[0][0]) { + writeMarketFiles() + } else { + writeDailyFiles() } } - function anotherFileWritten() { - totalFilesWritten++ - if (totalFilesWritten === outputDatasets.length) { - callBackFunction(global.DEFAULT_OK_RESPONSE); + function writeMarketFiles() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); } + + writeRecordsFile(); + + function writeRecordsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < recordsArray.length; i++) { + + let record = recordsArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.type + "," + + record.marketRate + "," + + record.amount + "," + + record.balanceA + "," + + record.balanceB + "," + + record.profit + "," + + record.lastTradeProfitLoss + "," + + record.stopLoss + "," + + record.roundtrips + "," + + record.hits + "," + + record.fails + "," + + record.hitRatio + "," + + record.ROI + "," + + record.periods + "," + + record.days + "," + + record.anualizedRateOfReturn + "," + + record.positionRate + "," + + record.lastTradeROI + "," + + record.strategy + "," + + record.strategyStageNumber + "," + + record.takeProfit + "," + + record.stopLossPhase + "," + + record.takeProfitPhase + "," + + JSON.stringify(record.executionRecord) + "," + + record.positionSize + "," + + record.initialBalanceA + "," + + record.minimumBalanceA + "," + + record.maximumBalanceA + "," + + record.initialBalanceB + "," + + record.minimumBalanceB + "," + + record.maximumBalanceB + "," + + record.baseAsset + "," + + record.positionPeriods + "," + + record.positionDays + "," + + record.distanceToLastTriggerOn + "," + + record.distanceToLastTriggerOff + "," + + record.distanceToLastTakePosition + "," + + record.distanceToLastClosePosition + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeConditionsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } } + + function writeConditionsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < conditionsArray.length; i++) { + + let record = conditionsArray[i]; + + let conditions = ""; + let conditionsSeparator = ""; + + for (let j = 0; j < record.length - 3; j++) { + conditions = conditions + conditionsSeparator + record[j]; + if (conditionsSeparator === "") { conditionsSeparator = ","; } + } + + conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues. + + fileContent = fileContent + separator + '[' + conditions + ']'; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + fileContent = "[" + JSON.stringify(tradingSystem) + "," + fileContent + "]"; + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeStrategiesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStrategiesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < strategiesArray.length; i++) { + let record = strategiesArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.number + "," + + record.beginRate + "," + + record.endRate + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeTradesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeTradesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < tradesArray.length; i++) { + + let record = tradesArray[i]; + if (record.stopRate === undefined) { record.stopRate = 0 } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.lastTradeROI + "," + + record.beginRate + "," + + record.endRate + "," + + record.exitType + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + /* Emit event that signals that simulation files were updated */ + global.SYSTEM_EVENT_HANDLER.raiseEvent('Jason-Multi-Period', 'Simulation Files Updated') + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + + function writeDailyFiles() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); } + + writeRecordsFile(); + + /* TODO : NEXT function is a work in progress, not used today */ + function writeFinalResults() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> Entering function."); } + + if ( // We only write this file if we are at the head of the market. + Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS + !== + Math.trunc((new Date()).valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS + ) { + writeRecordsFile(); + return; + } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < recordsArray.length; i++) { + + let record = recordsArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.type + "," + + record.marketRate + "," + + record.amount + "," + + record.balanceA + "," + + record.balanceB + "," + + record.profit + "," + + record.lastTradeProfitLoss + "," + + record.stopLoss + "," + + record.roundtrips + "," + + record.hits + "," + + record.fails + "," + + record.hitRatio + "," + + record.ROI + "," + + record.periods + "," + + record.days + "," + + record.anualizedRateOfReturn + "," + + record.positionRate + "," + + record.lastTradeROI + "," + + record.strategy + "," + + record.strategyStageNumber + "," + + record.takeProfit + "," + + record.stopLossPhase + "," + + record.takeProfitPhase + "," + + JSON.stringify(record.executionRecord) + "," + + record.positionSize + "," + + record.initialBalanceA + "," + + record.minimumBalanceA + "," + + record.maximumBalanceA + "," + + record.initialBalanceB + "," + + record.minimumBalanceB + "," + + record.maximumBalanceB + "," + + record.baseAsset + "," + + record.positionPeriods + "," + + record.positionDays + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeRecordsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeRecordsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < recordsArray.length; i++) { + + let record = recordsArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.type + "," + + record.marketRate + "," + + record.amount + "," + + record.balanceA + "," + + record.balanceB + "," + + record.profit + "," + + record.lastTradeProfitLoss + "," + + record.stopLoss + "," + + record.roundtrips + "," + + record.hits + "," + + record.fails + "," + + record.hitRatio + "," + + record.ROI + "," + + record.periods + "," + + record.days + "," + + record.anualizedRateOfReturn + "," + + record.positionRate + "," + + record.lastTradeROI + "," + + record.strategy + "," + + record.strategyStageNumber + "," + + record.takeProfit + "," + + record.stopLossPhase + "," + + record.takeProfitPhase + "," + + JSON.stringify(record.executionRecord) + "," + + record.positionSize + "," + + record.initialBalanceA + "," + + record.minimumBalanceA + "," + + record.maximumBalanceA + "," + + record.initialBalanceB + "," + + record.minimumBalanceB + "," + + record.maximumBalanceB + "," + + record.baseAsset + "," + + record.positionPeriods + "," + + record.positionDays + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeConditionsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeConditionsFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < conditionsArray.length; i++) { + + let record = conditionsArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + let conditions = ""; + let conditionsSeparator = ""; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + for (let j = 0; j < record.length - 3; j++) { + conditions = conditions + conditionsSeparator + record[j]; + if (conditionsSeparator === "") { conditionsSeparator = ","; } + } + + conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors. + conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues. + + fileContent = fileContent + separator + '[' + conditions + ']'; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + fileContent = "[" + JSON.stringify(tradingSystem) + "," + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeStrategiesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeStrategiesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < strategiesArray.length; i++) { + let record = strategiesArray[i]; + + /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends + at the current date. */ + + if (record.end < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.number + "," + + record.beginRate + "," + + record.endRate + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeTradesFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeTradesFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < tradesArray.length; i++) { + + let record = tradesArray[i]; + + /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends + at the current date. */ + + if (record.end < currentDay.valueOf()) { continue; } + if (record.stopRate === undefined) { record.stopRate = 0 } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + record.status + "," + + record.lastTradeROI + "," + + record.beginRate + "," + + record.endRate + "," + + record.exitType + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + /* Emit event that signals that simulation files were updated */ + global.SYSTEM_EVENT_HANDLER.raiseEvent('Jason-Multi-Period', 'Simulation Files Updated') + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } } catch (err) { From e1823f7f113f2c774b6916f49b0a2feac44b17d1 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 20 Nov 2019 14:45:48 +0100 Subject: [PATCH 32/38] candlesIndex --- TradingSimulation.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/TradingSimulation.js b/TradingSimulation.js index a018512a7..9c481ef1b 100644 --- a/TradingSimulation.js +++ b/TradingSimulation.js @@ -464,7 +464,7 @@ let lastCandle = candles[candles.length - 1]; /* Main Simulation Loop: We go thourgh all the candles at this time period. */ - let i + let candlesIndex /* For Loop Level heartbeat */ let loopingDay @@ -482,11 +482,11 @@ let diff = targetEnd - firstEnd let amount = diff / timePeriod - i = Math.trunc(amount) - if (i < 0) { i = 0 } - if (i > candles.length - 1) { + candlesIndex = Math.trunc(amount) + if (candlesIndex < 0) { candlesIndex = 0 } + if (candlesIndex > candles.length - 1) { /* This will happen when the bot.VALUES_TO_USE.timeRange.initialDatetime is beyond the last candle available, meaning that the dataSet needs to be updated with more up-to-date data. */ - i = candles.length - 1 + candlesIndex = candles.length - 1 } loop() @@ -495,9 +495,9 @@ function loop() { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Entering function."); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + i); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + candlesIndex); } - let candle = candles[i]; + let candle = candles[candlesIndex]; /* Not processing while out of user-defined time range */ @@ -569,8 +569,8 @@ let processingDate = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2); - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + i + " @ " + processingDate) } - console.log("Jason -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + i + " @ " + processingDate) + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + candlesIndex + " @ " + processingDate) } + console.log("Jason -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + candlesIndex + " @ " + processingDate) bot.sessionHeartBeat(processingDate) // tell the world we are alive and doing well } @@ -600,9 +600,9 @@ yesterday.periods = periods } - /* We skip the candle at the head of the market because i has not closed yet. */ + /* We skip the candle at the head of the market because candlesIndex has not closed yet. */ let candlesPerDay = ONE_DAY_IN_MILISECONDS / timePeriod - if (i === candles.length - 1) { + if (candlesIndex === candles.length - 1) { if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) { /*We are at the head of the market, thus we skip the last candle because it has not close yet. */ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } @@ -614,7 +614,7 @@ } } else { // We are processing Market Files - if (i === candles.length - 1) { + if (candlesIndex === candles.length - 1) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } controlLoop(); return @@ -1916,7 +1916,7 @@ } /* Check if we need to execute. */ - if (i > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + if (candlesIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ /* Check that we are in LIVE MODE */ if (bot.startMode === "Live") { /* We see if we need to put the actual order at the exchange. */ @@ -2180,7 +2180,7 @@ /* Position size and rate */ let strategy = tradingSystem.strategies[currentStrategyIndex]; - if (i > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + if (candlesIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ /* Check that we are in LIVE MODE */ if (bot.startMode === "Live") { /* We see if we need to put the actual order at the exchange. */ @@ -2605,7 +2605,7 @@ if ( (currentStrategy.begin !== 0 && currentStrategy.end !== 0) || - (currentStrategy.begin !== 0 && i === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) + (currentStrategy.begin !== 0 && candlesIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) ) { strategiesArray.push(currentStrategy); @@ -2637,7 +2637,7 @@ if ( (currentTrade.begin !== 0 && currentTrade.end !== 0) || - (currentTrade.begin !== 0 && i === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) + (currentTrade.begin !== 0 && candlesIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) ) { currentTrade.profit = lastTradeProfitLoss; @@ -2756,8 +2756,8 @@ return } - i++ - if (i < candles.length) { + candlesIndex++ + if (candlesIndex < candles.length) { setImmediate(loop) // This will execute the next loop in the next iteration of the NodeJs event loop allowing for other callbacks to be executed. } else { afterLoop() From 28f5f1793c2cbee6d67680679e0b06c3a85e1ea4 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 20 Nov 2019 18:35:00 +0100 Subject: [PATCH 33/38] Inter Process Memory Implementation at Indicators Variables --- Commons.js | 54 +++++++++++++++++++++++++++++++++++++++++++++++-- IndicatorBot.js | 13 +++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/Commons.js b/Commons.js index bf9d4dde4..1bf0d83dd 100644 --- a/Commons.js +++ b/Commons.js @@ -258,7 +258,18 @@ return results } - function dataBuildingProcedure(products, mainDependency, recordDefinition, dataBuildingProcedure, variableName, timePeriod, resultsWithIrregularPeriods) { + function dataBuildingProcedure( + products, + mainDependency, + recordDefinition, + dataBuildingProcedure, + variableName, + timePeriod, + resultsWithIrregularPeriods, + interExecutionMemory, + processingDailyFiles, + currentDay + ) { /* This function has as an input the products object, with all the information @@ -267,6 +278,8 @@ a new set of information. */ + let lastInstantOfTheDay + let yesterday = {} let system = { // These are the available system variables to be used in User Code and Formulas timePeriod: timePeriod, ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS @@ -287,9 +300,24 @@ } } + if (processingDailyFiles) { + /* Initialization of Last Instance */ + lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1; + + if (interExecutionMemory.variable === undefined) { + /* The first time the intialization variables goes to the Inter Execution Memory. */ + interExecutionMemory.variable = JSON.parse(JSON.stringify(variable)) + } + else { + /* We override the initialization, since the valid stuff is already at the Inter Execution Memory */ + variable = JSON.parse(JSON.stringify(interExecutionMemory.variable)) + } + } + /* This is Initialization Code */ if (dataBuildingProcedure.loop !== undefined) { if (dataBuildingProcedure.loop.code !== undefined) { + let lastRecord for (let index = 0; index < mainDependency.records.length; index++) { let record = { @@ -297,10 +325,16 @@ current: mainDependency.records[index], next: mainDependency.records[index + 1] } - + lastRecord = record let product = {} product[variableName] = {} + /* Here is how we know if we are processing Yesterday. */ + let positionedAtYesterday = false + if (processingDailyFiles) { + positionedAtYesterday = (record.current.end < currentDay.valueOf()) + } + /* This is Loop Code */ try { eval(dataBuildingProcedure.loop.code.code) @@ -337,6 +371,22 @@ /* Adding the new element to the resulting array */ results.push(product[variableName]); } + + /* While we are positioned at Yesterday, we keey updating this data structure. */ + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.variable = JSON.parse(JSON.stringify(variable)) + } + } + } + /* + Before returning we need to see if we have to record some of our counters at the interExecutionMemory. + To do that, the condition to be met is that this execution must include all candles of the current day. + */ + if (processingDailyFiles) { + if (lastRecord.current.end === lastInstantOfTheDay) { + interExecutionMemory.variable = JSON.parse(JSON.stringify(yesterday.variable)) + } } } } diff --git a/IndicatorBot.js b/IndicatorBot.js index 74a547e44..ba8ed9690 100644 --- a/IndicatorBot.js +++ b/IndicatorBot.js @@ -94,7 +94,18 @@ } /* Build the data */ - jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, resultsWithIrregularPeriods) + jsonData = commons.dataBuildingProcedure( + products, + mainDependency, + recordDefinition, + outputDatasetNode.referenceParent.parentNode.dataBuilding, + singularVariableName, + timePeriod, + resultsWithIrregularPeriods, + interExecutionMemory, + processingDailyFiles, + currentDay + ) /* Add the calculated properties */ if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) { From 512f6628d9aa4f87ada27d16a9f40213895c0239 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 21 Nov 2019 09:47:50 +0100 Subject: [PATCH 34/38] Bug Fixing + refactoring --- Commons.js | 4 ++-- IndicatorBot.js | 4 ++-- TradingSimulation.js | 36 ++++++++++++++++++------------------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Commons.js b/Commons.js index 1bf0d83dd..7b5eda849 100644 --- a/Commons.js +++ b/Commons.js @@ -393,7 +393,7 @@ return results } - function generateFileContent(records, recordDefinition, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) { + function generateFileContent(records, recordDefinition, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction) { try { @@ -458,7 +458,7 @@ } } - function writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, callBackFunction) { + function writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, currentDay, callBackFunction) { try { diff --git a/IndicatorBot.js b/IndicatorBot.js index ba8ed9690..46cb50b89 100644 --- a/IndicatorBot.js +++ b/IndicatorBot.js @@ -143,8 +143,8 @@ } contextSummary.dataSetVersion = "dataSet.V1" - let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, callBackFunction) - commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, callBackFunction) + let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction) + commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, currentDay, callBackFunction) } diff --git a/TradingSimulation.js b/TradingSimulation.js index 9c481ef1b..bb0b0e0d5 100644 --- a/TradingSimulation.js +++ b/TradingSimulation.js @@ -464,7 +464,7 @@ let lastCandle = candles[candles.length - 1]; /* Main Simulation Loop: We go thourgh all the candles at this time period. */ - let candlesIndex + let currentCandleIndex /* For Loop Level heartbeat */ let loopingDay @@ -482,11 +482,11 @@ let diff = targetEnd - firstEnd let amount = diff / timePeriod - candlesIndex = Math.trunc(amount) - if (candlesIndex < 0) { candlesIndex = 0 } - if (candlesIndex > candles.length - 1) { + currentCandleIndex = Math.trunc(amount) + if (currentCandleIndex < 0) { currentCandleIndex = 0 } + if (currentCandleIndex > candles.length - 1) { /* This will happen when the bot.VALUES_TO_USE.timeRange.initialDatetime is beyond the last candle available, meaning that the dataSet needs to be updated with more up-to-date data. */ - candlesIndex = candles.length - 1 + currentCandleIndex = candles.length - 1 } loop() @@ -495,9 +495,9 @@ function loop() { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Entering function."); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + candlesIndex); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + currentCandleIndex); } - let candle = candles[candlesIndex]; + let candle = candles[currentCandleIndex]; /* Not processing while out of user-defined time range */ @@ -569,8 +569,8 @@ let processingDate = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2); - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + candlesIndex + " @ " + processingDate) } - console.log("Jason -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + candlesIndex + " @ " + processingDate) + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + currentCandleIndex + " @ " + processingDate) } + console.log("Jason -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + currentCandleIndex + " @ " + processingDate) bot.sessionHeartBeat(processingDate) // tell the world we are alive and doing well } @@ -600,9 +600,9 @@ yesterday.periods = periods } - /* We skip the candle at the head of the market because candlesIndex has not closed yet. */ + /* We skip the candle at the head of the market because currentCandleIndex has not closed yet. */ let candlesPerDay = ONE_DAY_IN_MILISECONDS / timePeriod - if (candlesIndex === candles.length - 1) { + if (currentCandleIndex === candles.length - 1) { if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) { /*We are at the head of the market, thus we skip the last candle because it has not close yet. */ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } @@ -614,7 +614,7 @@ } } else { // We are processing Market Files - if (candlesIndex === candles.length - 1) { + if (currentCandleIndex === candles.length - 1) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); } controlLoop(); return @@ -1916,7 +1916,7 @@ } /* Check if we need to execute. */ - if (candlesIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ /* Check that we are in LIVE MODE */ if (bot.startMode === "Live") { /* We see if we need to put the actual order at the exchange. */ @@ -2180,7 +2180,7 @@ /* Position size and rate */ let strategy = tradingSystem.strategies[currentStrategyIndex]; - if (candlesIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ + if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/ /* Check that we are in LIVE MODE */ if (bot.startMode === "Live") { /* We see if we need to put the actual order at the exchange. */ @@ -2605,7 +2605,7 @@ if ( (currentStrategy.begin !== 0 && currentStrategy.end !== 0) || - (currentStrategy.begin !== 0 && candlesIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) + (currentStrategy.begin !== 0 && currentCandleIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) ) { strategiesArray.push(currentStrategy); @@ -2637,7 +2637,7 @@ if ( (currentTrade.begin !== 0 && currentTrade.end !== 0) || - (currentTrade.begin !== 0 && candlesIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) + (currentTrade.begin !== 0 && currentCandleIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay) ) { currentTrade.profit = lastTradeProfitLoss; @@ -2756,8 +2756,8 @@ return } - candlesIndex++ - if (candlesIndex < candles.length) { + currentCandleIndex++ + if (currentCandleIndex < candles.length) { setImmediate(loop) // This will execute the next loop in the next iteration of the NodeJs event loop allowing for other callbacks to be executed. } else { afterLoop() From ad89b5b22ee7c593086ed03d37cb4fb1c89a6810 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 21 Nov 2019 12:00:26 +0100 Subject: [PATCH 35/38] Removig requirement of definitions at Processs Instance --- Root.js | 47 +++++------------------------------- TradingBotProcessMainLoop.js | 6 ++++- server.js | 35 +++++++++++++++------------ 3 files changed, 30 insertions(+), 58 deletions(-) diff --git a/Root.js b/Root.js index bd73471d9..72781d834 100644 --- a/Root.js +++ b/Root.js @@ -140,47 +140,12 @@ let botConfig; let processInstance = global.TASK_NODE.bot.processes[processIndex] - /* Some very basic validations that we have all the information needed. */ - if (processInstance.referenceParent === undefined) { - console.log(logDisplace + "Root : [INFO] start -> Process Instance without Reference Parent. Process Instance = " + JSON.stringify(processInstance)); - global.EXIT_NODE_PROCESS() - return - } - - if (processInstance.referenceParent.code.codeName === undefined) { - console.log(logDisplace + "Root : [INFO] start -> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(processInstance.referenceParent)); - global.EXIT_NODE_PROCESS() - return - } - - if (processInstance.referenceParent.parentNode === undefined) { - console.log(logDisplace + "Root : [INFO] start -> Process Definition not attached to a Bot Definition. Process Definition = " + JSON.stringify(processInstance.referenceParent)); - global.EXIT_NODE_PROCESS() - return - } - - if (processInstance.referenceParent.parentNode.code.codeName === undefined) { - console.log(logDisplace + "Root : [INFO] start -> Bot Definition witn no codeName defined. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); - global.EXIT_NODE_PROCESS() - return - } - - if (processInstance.referenceParent.parentNode.parentNode === undefined) { - console.log(logDisplace + "Root : [INFO] start -> Bot Definition not attached to a Team. Bot Definition = " + JSON.stringify(processInstance.referenceParent.parentNode)); - global.EXIT_NODE_PROCESS() - return - } - - if (processInstance.referenceParent.parentNode.parentNode.code.codeName === undefined) { - console.log(logDisplace + "Root : [INFO] start -> Team witn no codeName defined. Team = " + JSON.stringify(processInstance.referenceParent.parentNode.parentNode)); - global.EXIT_NODE_PROCESS() - return - } - /* Here we will check if we need to load the configuration and code of the bot from a file or we will take that from the UI. */ if ( processInstance.referenceParent.code.framework !== undefined && - (processInstance.referenceParent.code.framework.name === 'Multi-Period-Market' || processInstance.referenceParent.code.framework.name === 'Multi-Period-Daily') + ( processInstance.referenceParent.code.framework.name === 'Multi-Period-Market' || + processInstance.referenceParent.code.framework.name === 'Multi-Period-Daily' || + processInstance.referenceParent.code.framework.name === 'Multi-Period') ) { botConfig = processInstance.referenceParent.parentNode.code botConfig.definedByUI = true @@ -199,16 +164,16 @@ const FILE_STORAGE = require('./FileStorage.js'); let fileStorage = FILE_STORAGE.newFileStorage(); - let filePath = global.TASK_NODE.bot.code.team + '/bots/' + global.TASK_NODE.bot.code.repo + '/this.bot.config.json'; + let filePath = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName + '/bots/' + global.TASK_NODE.bot.code.repo + '/this.bot.config.json'; - fileStorage.getTextFile(global.TASK_NODE.bot.code.team, filePath, onFileReceived); + fileStorage.getTextFile(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName, filePath, onFileReceived); function onFileReceived(err, text) { if (err.result !== global.DEFAULT_OK_RESPONSE.result) { console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err)); console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> filePath = " + filePath); - console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> team = " + global.TASK_NODE.bot.code.team); + console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> team = " + global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName); return; } diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index bbb5f013b..c4230dbde 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -43,7 +43,11 @@ if (bot.definedByUI === true) { /* The code of the bot is defined at the UI. No need to load a file with the code. */ - callBackFunction(global.DEFAULT_OK_RESPONSE); + session.initialize(onSessionInitialized) + + function onSessionInitialized(err) { + callBackFunction(err); + } return } diff --git a/server.js b/server.js index 5c371209c..22415c444 100644 --- a/server.js +++ b/server.js @@ -75,13 +75,6 @@ global.EXIT_NODE_PROCESS = function exitProcess() { for (let i = 0; i < global.TASK_NODE.bot.processes.length; i++) { let code = global.TASK_NODE.bot.processes[i].code - - /* Delete the event handler for each process. */ - - let key = global.TASK_NODE.bot.code.team + "-" + global.TASK_NODE.bot.code.bot + "-" + code.process - - global.SYSTEM_EVENT_HANDLER.deleteEventHandler(key) - let process = global.TASK_NODE.bot.processes[i] key = process.name + '-' + process.type + '-' + process.id @@ -183,25 +176,35 @@ function bootLoader() { /* Validate that the minimun amount of parameters required are defined. */ - if (global.TASK_NODE.bot.code.bot === undefined) { - console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'bot' at the Indicator | Sensor | Trading is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name); + if (global.TASK_NODE.bot.processes[processIndex].referenceParent === undefined) { + console.log("[INFO] Task Server -> server -> bootLoader -> Process Instance without a Reference Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex])); continue } - if (global.TASK_NODE.bot.code.team === undefined) { - console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'team' at the bot is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name); + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode === undefined) { + console.log("[INFO] Task Server -> server -> bootLoader -> Process Definition without parent Bot Definition. -> Process Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent)); continue } - if (global.TASK_NODE.bot.processes[processIndex].code.process === undefined) { - console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'process' at object Process is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name); + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode === undefined) { + console.log("[INFO] Task Server -> server -> bootLoader -> Bot Definition without parent Team. -> Bot Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode)); continue } - /* Create the event handler for each process. This event handlers are where the status reports updated events are raised. */ + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName === undefined) { + console.log("[INFO] Task Server -> server -> bootLoader -> Process Definition without a codeName defined. -> Process Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent)); + continue + } - let key = global.TASK_NODE.bot.code.team + "-" + global.TASK_NODE.bot.code.bot + "-" + code.process - global.SYSTEM_EVENT_HANDLER.createEventHandler(key) + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.code.codeName === undefined) { + console.log("[INFO] Task Server -> server -> bootLoader -> Bot Definition without a codeName defined. -> Bot Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode)); + continue + } + + if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName === undefined) { + console.log("[INFO] Task Server -> server -> bootLoader -> Team without a codeName defined. -> Team Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode)); + continue + } startRoot(processIndex); } From 302ee94e6ee79f2f51c42a1297211a914f202f82 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 21 Nov 2019 13:46:00 +0100 Subject: [PATCH 36/38] Bug Fixed --- TradingSimulation.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TradingSimulation.js b/TradingSimulation.js index bb0b0e0d5..5ca92c8c3 100644 --- a/TradingSimulation.js +++ b/TradingSimulation.js @@ -2697,17 +2697,17 @@ if (periods > lastPeriodAnnounced) { /* The Value Variation is what tells us how much the value already announced must change in order to annouce it again. */ - let valueVariantion + let valueVariation try { let code = JSON.parse(announcement.code) - valueVariantion = code.valueVariantion + valueVariation = code.valueVariation } catch (err) { announcement.error = err.message } - if (newAnnouncementRecord.value !== undefined && valueVariantion !== undefined) { - let upperLimit = newAnnouncementRecord.value + newAnnouncementRecord.value * valueVariantion / 100 - let lowerLimit = newAnnouncementRecord.value - newAnnouncementRecord.value * valueVariantion / 100 + if (newAnnouncementRecord.value !== undefined && valueVariation !== undefined) { + let upperLimit = newAnnouncementRecord.value + newAnnouncementRecord.value * valueVariation / 100 + let lowerLimit = newAnnouncementRecord.value - newAnnouncementRecord.value * valueVariation / 100 if (value > lowerLimit && value < upperLimit) { /* There is not enough variation to announce this again. */ return From f13e1361359e08b578c48da8193efe44d66a380e Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 21 Nov 2019 14:30:21 +0100 Subject: [PATCH 37/38] Bug fixed when Telegram API returns error --- Session.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Session.js b/Session.js index 6c791f314..c259426ed 100644 --- a/Session.js +++ b/Session.js @@ -491,9 +491,9 @@ if (socialBot.type === code.botType && socialBot.id === code.botId) { if (socialBot.type === "Telegram Bot") { if (announcement.formulaValue !== undefined) { - socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue) + socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err)) } else { - socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text) + socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err)) } } } @@ -523,7 +523,7 @@ const Telegram = require('telegraf/telegram') let telegramAPI telegramAPI = new Telegram(botToken) - telegramAPI.sendMessage(chatId, bot.SESSION.type + " '" + bot.SESSION.name + "' was Started.") + telegramAPI.sendMessage(chatId, bot.SESSION.type + " '" + bot.SESSION.name + "' was Started.").catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpTelegramBot -> Telegram API error -> err = " + err)) let botInstance = { telegramBot: telegramBot, From bb68148d2464e52e4dd49f24dc7dea51aa80f4e2 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 22 Nov 2019 19:36:16 +0100 Subject: [PATCH 38/38] Several Bug Fixes --- Commons.js | 3 ++- IndicatorBot.js | 1 + ProcessExecutionEvents.js | 14 +++++++++--- StatusDependencies.js | 46 ++++++++++++++++++++++++++++++--------- StatusReport.js | 2 +- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/Commons.js b/Commons.js index 7b5eda849..d9f61ce44 100644 --- a/Commons.js +++ b/Commons.js @@ -265,6 +265,7 @@ dataBuildingProcedure, variableName, timePeriod, + timePeriodLabel, resultsWithIrregularPeriods, interExecutionMemory, processingDailyFiles, @@ -384,7 +385,7 @@ To do that, the condition to be met is that this execution must include all candles of the current day. */ if (processingDailyFiles) { - if (lastRecord.current.end === lastInstantOfTheDay) { + if (lastRecord.current.end === lastInstantOfTheDay && yesterday.variable !== undefined) { interExecutionMemory.variable = JSON.parse(JSON.stringify(yesterday.variable)) } } diff --git a/IndicatorBot.js b/IndicatorBot.js index 46cb50b89..4c823887a 100644 --- a/IndicatorBot.js +++ b/IndicatorBot.js @@ -101,6 +101,7 @@ outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod, + timePeriodLabel, resultsWithIrregularPeriods, interExecutionMemory, processingDailyFiles, diff --git a/ProcessExecutionEvents.js b/ProcessExecutionEvents.js index 8ff8c2f54..fd04c2efc 100644 --- a/ProcessExecutionEvents.js +++ b/ProcessExecutionEvents.js @@ -78,7 +78,7 @@ /* This forces this process to wait until the process that this one depends on, emits its event signaling that the process execution has finished. */ - let extraCallerId = '' + let extraCallerId = '-' + Math.trunc(Math.random() * 10000) + '-' if (month) { extraCallerId = extraCallerId + '-' + month } if (year) { extraCallerId = extraCallerId + '-' + year } @@ -87,6 +87,10 @@ let subscriptionId + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> started listening to Process Execution Finished. "); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> key = " + key); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> callerId = " + callerId); } + global.SYSTEM_EVENT_HANDLER.listenToEvent(key, 'Process Execution Finished', undefined, callerId, responseCallBack, eventsCallBack) function responseCallBack(message) { @@ -95,13 +99,17 @@ callBackFunction(global.DEFAULT_FAIL_RESPONSE); } else { subscriptionId = message.eventSubscriptionId + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> subscriptionId = " + subscriptionId); } } } function eventsCallBack(message) { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> eventsCallBack -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> message = " + JSON.stringify(message)); } /* We continue the normal flow after we learn the dependent process has finished its execution. */ + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> stopListening to Process Execution Finished. "); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> subscriptionId = " + subscriptionId); } global.SYSTEM_EVENT_HANDLER.stopListening(key, 'Process Execution Finished', subscriptionId) if (message.event.err.result === global.DEFAULT_OK_RESPONSE.result) { @@ -126,7 +134,7 @@ global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started') global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event) - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> " + currentProcessKey + " Process Execution Started " + datetimeForLogs); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> " + currentProcessKey + " Process Execution Started " + datetimeForLogs); } callBackFunction(global.DEFAULT_OK_RESPONSE); } diff --git a/StatusDependencies.js b/StatusDependencies.js index 95092e152..542e25eec 100644 --- a/StatusDependencies.js +++ b/StatusDependencies.js @@ -33,7 +33,7 @@ } } else { logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not status dependencies."); - callBackFunction(global.DEFAULT_OK_RESPONSE) + callBackFunction(global.DEFAULT_FAIL_RESPONSE) return } @@ -41,13 +41,39 @@ let alreadyCalledBack = false; let loadCount = 0; - + let dependenciesToProcess = [] for (let i = 0; i < thisObject.nodeArray.length; i++) { + let statusDependency = { + dependency: thisObject.nodeArray[i], + month: pMonth, + year: pYear + } + + dependenciesToProcess.push(statusDependency) + + /* + For the special case that the statusDependency is a Self Reference AND this process is sliced in different + instances for each month, we will add the main self reference status report that is month/year independent. + */ + + if (statusDependency.dependency.code.mainUtility === "Self Reference" && pMonth !== undefined && pYear !== undefined) { + let mainDependency = { + dependency: thisObject.nodeArray[i], + month: undefined, + year: undefined + } + + dependenciesToProcess.push(mainDependency) + } + } + + for (let i = 0; i < dependenciesToProcess.length; i++) { let statusReportModule = STATUS_REPORT.newStatusReport(BOT, logger, UTILITIES); logger.write(MODULE_NAME, "[INFO] initialize -> onInitilized -> Initializing Status Report # " + (i + 1)); - statusReportModule.initialize(thisObject.nodeArray[i], pMonth, pYear, onInitilized); + let statusDependency = dependenciesToProcess[i] + statusReportModule.initialize(statusDependency.dependency, statusDependency.month, statusDependency.year, onInitilized); function onInitilized(err) { @@ -78,7 +104,7 @@ switch (err.message) { case global.DEFAULT_OK_RESPONSE.message: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> Status Dependency = " + JSON.stringify(thisObject.nodeArray[i])) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> Status Dependency = " + JSON.stringify(statusDependency.dependency)) } addReport(); return; @@ -86,7 +112,7 @@ case "Status Report was never created.": { logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack); - logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(thisObject.nodeArray[i])) + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(statusDependency.dependency)) addReport(); return; @@ -95,7 +121,7 @@ case "Status Report is corrupt.": { logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack); - logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(thisObject.nodeArray[i])) + logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(statusDependency.dependency)) addReport(); return; @@ -128,10 +154,10 @@ let key; - if (thisObject.nodeArray[i].processRunMonthly === true) { - key = thisObject.nodeArray[i].devTeam + "-" + thisObject.nodeArray[i].bot + "-" + thisObject.nodeArray[i].process + "-" + thisObject.nodeArray[i].dataSetVersion + "-" + pYear + "-" + pMonth; + if (statusDependency.dependency.processRunMonthly === true && statusDependency.month !== undefined && statusDependency.year !== undefined) { + key = statusDependency.dependency.devTeam + "-" + statusDependency.dependency.bot + "-" + statusDependency.dependency.process + "-" + statusDependency.dependency.dataSetVersion + "-" + statusDependency.year + "-" + statusDependency.month; } else { - key = thisObject.nodeArray[i].devTeam + "-" + thisObject.nodeArray[i].bot + "-" + thisObject.nodeArray[i].process + "-" + thisObject.nodeArray[i].dataSetVersion; + key = statusDependency.dependency.devTeam + "-" + statusDependency.dependency.bot + "-" + statusDependency.dependency.process + "-" + statusDependency.dependency.dataSetVersion; } thisObject.keys.push(key); @@ -143,7 +169,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Report added to Map. -> key = " + key); } - if (loadCount === thisObject.nodeArray.length) { + if (loadCount === dependenciesToProcess.length) { if (alreadyCalledBack === false) { alreadyCalledBack = true callBackFunction(global.DEFAULT_OK_RESPONSE); diff --git a/StatusReport.js b/StatusReport.js index b2e931a99..239f382b7 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -131,7 +131,7 @@ sessionPath = bot.SESSION.folderName + "/" } - if (statusDependencyNode.processRunMonthly === true) { + if (statusDependencyNode.processRunMonthly === true && month !== undefined && year !== undefined) { logger.fileName = MODULE_NAME + "." + statusDependencyNode.bot + "." + statusDependencyNode.process + "." + year + "." + month; timePath = "/" + year + "/" + month; }