From 136be168a52bda0396c473f3f748265167191a46 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 25 Oct 2019 12:51:54 +0200 Subject: [PATCH] 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) {