exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, BLOB_STORAGE, FILE_STORAGE) { const FULL_LOG = true; const LOG_FILE_CONTENT = false; const MODULE_NAME = "User Bot"; const TRADES_FOLDER_NAME = "Trades"; const CANDLES_FOLDER_NAME = "Candles"; const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands"; const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth"; const commons = COMMONS.newCommons(bot, logger, UTILITIES); thisObject = { initialize: initialize, start: start }; let oliviaStorage = BLOB_STORAGE.newBlobStorage(bot, logger); let chrisStorage = BLOB_STORAGE.newBlobStorage(bot, logger); let utilities = UTILITIES.newCloudUtilities(bot, logger); let statusDependencies; return thisObject; function initialize(pStatusDependencies, pMonth, pYear, callBackFunction) { try { logger.fileName = MODULE_NAME; logger.initialize(); if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } statusDependencies = pStatusDependencies; commons.initializeStorage(oliviaStorage, chrisStorage, onInizialized); function onInizialized(err) { if (err.result === global.DEFAULT_OK_RESPONSE.result) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onInizialized -> Initialization Succeed."); } callBackFunction(global.DEFAULT_OK_RESPONSE); } else { logger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> err = " + err.message); callBackFunction(err); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } /* This process is going to do the following: Read the candles from Olivia and produce the bollinger bands out of them. */ function start(callBackFunction) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } let market = global.MARKET; buildBands(); function buildBands() { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> Entering function."); } try { let n; periodsLoop(); function periodsLoop() { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> periodsLoop -> Entering function."); } /* We will iterate through all posible periods. */ n = 0 // loop Variable representing each possible period as defined at the periods array. loopBody(); } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } function loopBody() { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> Entering function."); } const outputPeriod = global.marketFilesPeriods[n][0]; const timePeriod = global.marketFilesPeriods[n][1]; nextCandleFile(); function nextCandleFile() { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> Entering function."); } let fileName = market.assetA + '_' + market.assetB + ".json"; let filePathRoot = bot.devTeam + "/" + "AAOlivia" + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriod; oliviaStorage.getTextFile(filePath, fileName, onFileReceived, true); function onFileReceived(err, text) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); } if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); } if (err.result !== global.DEFAULT_OK_RESPONSE.result) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.message); callBackFunction(err); return; } let marketFile = JSON.parse(text); let candles = []; buildCandles(); function buildCandles() { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> Entering function."); } try { for (let i = 0; i < marketFile.length; i++) { let candle = { open: undefined, close: undefined, min: 10000000000000, max: 0, begin: undefined, end: undefined, direction: undefined }; candle.min = marketFile[i][0]; candle.max = marketFile[i][1]; candle.open = marketFile[i][2]; candle.close = marketFile[i][3]; candle.begin = marketFile[i][4]; candle.end = marketFile[i][5]; candles.push(candle); } buildBands(); } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } function buildBands() { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> Entering function."); } let bandsArray = []; let pBArray = []; let numberOfPeriodsBB = 20; let numberOfStandardDeviations = 2; let numberOfPeriodsPB = 5; /* Building bands */ let band; for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them. let movingAverage = 0; for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the moving average. movingAverage = movingAverage + candles[j].close; } movingAverage = movingAverage / numberOfPeriodsBB; let standardDeviation = 0; for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the standard deviation. standardDeviation = standardDeviation + Math.pow (candles[j].close - movingAverage, 2); } standardDeviation = standardDeviation / numberOfPeriodsBB; standardDeviation = Math.sqrt(standardDeviation); if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later. band = { begin: candles[i].begin, end: candles[i].end, movingAverage: movingAverage, standardDeviation: standardDeviation, deviation: standardDeviation * numberOfStandardDeviations }; bandsArray.push(band); /* Calculating %B */ let lowerBB; let upperBB; lowerBB = band.movingAverage - band.deviation; upperBB = band.movingAverage + band.deviation; let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100; /* Moving Average Calculation */ let numberOfPreviousPeriods; let currentPosition = pBArray.length; if (currentPosition < numberOfPeriodsPB) { // Avoinding to get into negative array indexes numberOfPreviousPeriods = currentPosition; } else { numberOfPreviousPeriods = numberOfPeriodsPB; } movingAverage = 0; for (let j = currentPosition - numberOfPreviousPeriods; j < currentPosition; j++) { // go through the last numberOfPeriodsPBs to calculate the moving average. movingAverage = movingAverage + pBArray[j].value; } movingAverage = movingAverage + value; movingAverage = movingAverage / (numberOfPreviousPeriods + 1); let bandwidth = (upperBB- lowerBB) / band.movingAverage; let percentageBandwidth = { begin: candles[i].begin, end: candles[i].end, value: value, movingAverage: movingAverage, bandwidth: bandwidth }; pBArray.push(percentageBandwidth); } writeBandsFile(bandsArray, pBArray); } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } function writeBandsFile(pBands, pPB) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> Entering function."); } let separator = ""; let fileRecordCounter = 0; let fileContent = ""; for (i = 0; i < pBands.length; i++) { let band = pBands[i]; fileContent = fileContent + separator + '[' + band.begin + "," + band.end + "," + band.movingAverage + "," + band.standardDeviation + "," + band.deviation + "]"; if (separator === "") { separator = ","; } fileRecordCounter++; } fileContent = "[" + fileContent + "]"; let fileName = '' + market.assetA + '_' + market.assetB + '.json'; let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; let filePath = filePathRoot + "/Output/" + BOLLINGER_BANDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriod; chrisStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); function onFileCreated(err) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> Entering function."); } if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> fileContent = " + fileContent); } if (err.result !== global.DEFAULT_OK_RESPONSE.result) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.message); logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> filePath = " + filePath); logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); callBackFunction(err); return; } writePBFile(pPB); } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } function writePBFile(pPercentageBandwidths) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> Entering function."); } let separator = ""; let fileRecordCounter = 0; let fileContent = ""; for (i = 0; i < pPercentageBandwidths.length; i++) { let pB = pPercentageBandwidths[i]; fileContent = fileContent + separator + '[' + pB.begin + "," + pB.end + "," + pB.value + "," + pB.movingAverage + "," + pB.bandwidth + "]"; if (separator === "") { separator = ","; } fileRecordCounter++; } fileContent = "[" + fileContent + "]"; let fileName = '' + market.assetA + '_' + market.assetB + '.json'; let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; let filePath = filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriod; chrisStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); function onFileCreated(err) { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> Entering function."); } if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> fileContent = " + fileContent); } if (err.result !== global.DEFAULT_OK_RESPONSE.result) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.message); logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> filePath = " + filePath); logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); callBackFunction(err); return; } controlLoop(); } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } function controlLoop() { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> Entering function."); } n++; if (n < global.marketFilesPeriods.length) { loopBody(); } else { writeStatusReport(callBackFunction); } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> controlLoop -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } function writeStatusReport(callBack) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); } try { let reportKey = "AAMasters" + "-" + "AAChris" + "-" + "Multi-Period-Market" + "-" + "dataSet.V1"; let thisReport = statusDependencies.statusReports.get(reportKey); thisReport.file.lastExecution = bot.processDatetime; thisReport.save(callBack); } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } };