diff --git a/FileStorage.js b/FileStorage.js index 2ec8a47ec..be6f08ccd 100644 --- a/FileStorage.js +++ b/FileStorage.js @@ -6,10 +6,7 @@ exports.newFileStorage = function newFileStorage(logger) { const MODULE_NAME = 'FileStorage' const MAX_RETRY = 30 const FAST_RETRY_TIME_IN_MILISECONDS = 500 - const SLOW_RETRY_TIME_IN_MILISECONDS = 5000 - let currentRetryGetTextFile = 0 - let currentRetryWriteTextFile = 0 - let currentRetryDeleteTextFile = 0 + const SLOW_RETRY_TIME_IN_MILISECONDS = 2000 if (logger === undefined) { // Dummy logger logger = {} @@ -29,94 +26,101 @@ exports.newFileStorage = function newFileStorage(logger) { function getTextFile(filePath, callBackFunction, noRetry) { - logger.write(MODULE_NAME, '[INFO] FileStorage -> getTextFile -> Entering Function.') + let currentRetryGetTextFile = 0 - let fileDoesNotExist = false + recursiveGetTextFile(filePath, callBackFunction, noRetry) - /* Choose path for either bots or data */ - let fileLocation - if (filePath.indexOf("/bots/") > 0) { - fileLocation = process.env.BOTS_PATH + '/' + filePath - } else { - fileLocation = process.env.STORAGE_PATH + '/' + filePath - } + function recursiveGetTextFile(filePath, callBackFunction, noRetry) { - try { + logger.write(MODULE_NAME, '[INFO] FileStorage -> getTextFile -> Entering Function.') - logger.write(MODULE_NAME, '[INFO] FileStorage -> getTextFile -> fileLocation: ' + fileLocation) + let fileDoesNotExist = false - /* Here we actually write the file. */ - const fs = require('fs') - fs.readFile(fileLocation, onFileRead) - - function onFileRead(err, text) { - let retryTimeToUse = FAST_RETRY_TIME_IN_MILISECONDS - if (currentRetryGetTextFile > MAX_RETRY - 2) { - retryTimeToUse = SLOW_RETRY_TIME_IN_MILISECONDS - } - if (err) { - - if (err.code === 'ENOENT') { // since files are deleted before being replaced, it can happen that it does not exist and after a retry it does. - if (noRetry === true) { - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Not Retrying. ') - let customResponse = { - result: global.CUSTOM_FAIL_RESPONSE.result, - message: 'File does not exist.' - } - callBackFunction(customResponse) - return - } else { - fileDoesNotExist = true - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Retrying. ') - setTimeout(retry, retryTimeToUse) - return - } - } - - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> err = ' + err.stack) - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> filePath = ' + filePath) - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> err = ' + err.stack) - setTimeout(retry, retryTimeToUse) - return - } - - if (text.toString() === "") { - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Read and Empty File -> filePath = ' + filePath) - setTimeout(retry, retryTimeToUse) - return - } - - callBackFunction(global.DEFAULT_OK_RESPONSE, text.toString()) + /* Choose path for either bots or data */ + let fileLocation + if (filePath.indexOf("/bots/") > 0) { + fileLocation = process.env.BOTS_PATH + '/' + filePath + } else { + fileLocation = process.env.STORAGE_PATH + '/' + filePath } - } catch (err) { - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> Error writing file -> file = ' + fileLocation) - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> Error writing file -> err = ' + err.stack) - retry() - } + try { - function retry() { - //console.log(new Date(), ' Retry #: ' + currentRetryGetTextFile, fileLocation ) - if (currentRetryGetTextFile < MAX_RETRY) { - currentRetryGetTextFile++ - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Will try to read the file again -> Retry #: ' + currentRetryGetTextFile) - getTextFile(filePath, callBackFunction) - } else { - currentRetryGetTextFile = 0 + logger.write(MODULE_NAME, '[INFO] FileStorage -> getTextFile -> fileLocation: ' + fileLocation) - if (fileDoesNotExist === true) { - logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Max retries reached reading a file. File Not Found.') - let customResponse = { - result: global.CUSTOM_FAIL_RESPONSE.result, - message: 'File does not exist.' + /* Here we actually write the file. */ + const fs = require('fs') + fs.readFile(fileLocation, onFileRead) + + function onFileRead(err, text) { + let retryTimeToUse = FAST_RETRY_TIME_IN_MILISECONDS + if (currentRetryGetTextFile > MAX_RETRY - 2) { + retryTimeToUse = SLOW_RETRY_TIME_IN_MILISECONDS } - callBackFunction(customResponse) - return + if (err) { + + if (err.code === 'ENOENT') { // since files are deleted before being replaced, it can happen that it does not exist and after a retry it does. + if (noRetry === true) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Not Retrying. ') + let customResponse = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: 'File does not exist.' + } + callBackFunction(customResponse) + return + } else { + fileDoesNotExist = true + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Retrying. ') + setTimeout(retry, retryTimeToUse) + return + } + } + + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> err = ' + err.stack) + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> filePath = ' + filePath) + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> err = ' + err.stack) + setTimeout(retry, retryTimeToUse) + return + } + + if (text.toString() === "") { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Read and Empty File -> filePath = ' + filePath) + setTimeout(retry, retryTimeToUse) + return + } + + callBackFunction(global.DEFAULT_OK_RESPONSE, text.toString()) + } + + } catch (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> Error writing file -> err = ' + err.stack) + retry() + } + + function retry() { + //console.log(new Date(), ' Retry #: ' + currentRetryGetTextFile, fileLocation ) + if (currentRetryGetTextFile < MAX_RETRY) { + currentRetryGetTextFile++ + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Will try to read the file again -> Retry #: ' + currentRetryGetTextFile) + recursiveGetTextFile(filePath, callBackFunction) } else { - logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> Max retries reached reading a file. Giving up.') - logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> file = ' + fileLocation) - callBackFunction(global.DEFAULT_FAIL_RESPONSE) - return + currentRetryGetTextFile = 0 + + if (fileDoesNotExist === true) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Max retries reached reading a file. File Not Found.') + let customResponse = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: 'File does not exist.' + } + callBackFunction(customResponse) + return + } else { + logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> Max retries reached reading a file. Giving up.') + logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> file = ' + fileLocation) + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + return + } } } } @@ -124,144 +128,161 @@ exports.newFileStorage = function newFileStorage(logger) { function createTextFile(filePath, fileContent, callBackFunction) { - logger.write(MODULE_NAME, '[INFO] FileStorage -> createTextFile -> Entering Function.') + let currentRetryWriteTextFile = 0 - /* Choose path for either logs or data */ - let fileLocation - if (filePath.indexOf("/Logs/") > 0) { - fileLocation = process.env.LOG_PATH + '/' + filePath - } else { - fileLocation = process.env.STORAGE_PATH + '/' + filePath - } + recursiveCreateTextFile(filePath, fileContent, callBackFunction) - try { + function recursiveCreateTextFile(filePath, fileContent, callBackFunction) { - logger.write(MODULE_NAME, '[INFO] FileStorage -> createTextFile -> fileLocation: ' + fileLocation) + logger.write(MODULE_NAME, '[INFO] FileStorage -> createTextFile -> Entering Function.') - /* If necesary a folder or folders are created before writing the file to disk. */ - let directoryPath = fileLocation.substring(0, fileLocation.lastIndexOf('/') + 1); - mkDirByPathSync(directoryPath) + /* Choose path for either logs or data */ + let fileLocation + if (filePath.indexOf("/Logs/") > 0) { + fileLocation = process.env.LOG_PATH + '/' + filePath + } else { + fileLocation = process.env.STORAGE_PATH + '/' + filePath + } - /* - Here we write the file with a temporary name so as to avoid dirty read from other processes. - Then we delete the original file, if exists, and finally we rename the temporary into the original name. - */ - const fs = require('fs') - fs.writeFile(fileLocation + '.tmp', fileContent, onFileWritenn) + try { - function onFileWritenn(err) { - let retryTimeToUse = FAST_RETRY_TIME_IN_MILISECONDS - if (currentRetryWriteTextFile > MAX_RETRY - 2) { - retryTimeToUse = SLOW_RETRY_TIME_IN_MILISECONDS - } - if (err) { - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> file = ' + fileLocation) - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> err = ' + err.stack) - setTimeout(retry, retryTimeToUse) - } else { + logger.write(MODULE_NAME, '[INFO] FileStorage -> createTextFile -> fileLocation: ' + fileLocation) - const fs = require('fs') - fs.unlink(fileLocation, onUnlinked) + /* If necesary a folder or folders are created before writing the file to disk. */ + let directoryPath = fileLocation.substring(0, fileLocation.lastIndexOf('/') + 1); + mkDirByPathSync(directoryPath) - function onUnlinked(err) { - let code = '' - if (err) { - code = err.code - } - if (code !== '' && code !== 'ENOENT') { - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> file = ' + fileLocation) - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> err = ' + err.stack) - setTimeout(retry, retryTimeToUse) - } else { + /* + Here we write the file with a temporary name so as to avoid dirty read from other processes. + Then we delete the original file, if exists, and finally we rename the temporary into the original name. + */ + const fs = require('fs') + fs.writeFile(fileLocation + '.tmp', fileContent, onFileWritenn) - const fs = require('fs') - fs.rename(fileLocation + '.tmp', fileLocation, onRenamed) + function onFileWritenn(err) { + let retryTimeToUse = FAST_RETRY_TIME_IN_MILISECONDS + if (currentRetryWriteTextFile > MAX_RETRY - 2) { + retryTimeToUse = SLOW_RETRY_TIME_IN_MILISECONDS + } + if (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> err = ' + err.stack) + setTimeout(retry, retryTimeToUse) + } else { - function onRenamed(err) { - if (err) { - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> file = ' + fileLocation + '.tmp') - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> err = ' + err.stack) - setTimeout(retry, retryTimeToUse) - } else { + const fs = require('fs') + fs.unlink(fileLocation, onUnlinked) - callBackFunction(global.DEFAULT_OK_RESPONSE) + function onUnlinked(err) { + let code = '' + if (err) { + code = err.code + } + if (code !== '' && code !== 'ENOENT') { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> err = ' + err.stack) + setTimeout(retry, retryTimeToUse) + } else { + const fs = require('fs') + fs.rename(fileLocation + '.tmp', fileLocation, onRenamed) + + function onRenamed(err) { + if (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> file = ' + fileLocation + '.tmp') + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> err = ' + err.stack) + setTimeout(retry, retryTimeToUse) + } else { + + callBackFunction(global.DEFAULT_OK_RESPONSE) + + } } } } } } + + } catch (err) { + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> Error writing file -> file = ' + fileLocation) + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> Error writing file -> err = ' + err.stack) + retry() } - } catch (err) { - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> Error writing file -> file = ' + fileLocation) - logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> Error writing file -> err = ' + err.stack) - retry() - } - - 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(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 retry() { + if (currentRetryWriteTextFile < MAX_RETRY) { + currentRetryWriteTextFile++ + logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> retry -> Will try to write the file again -> Retry #: ' + currentRetryWriteTextFile) + recursiveCreateTextFile(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 deleteTextFile(filePath, callBackFunction) { - logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> Entering Function.') + let currentRetryDeleteTextFile = 0 - /* Choose path for either logs or data */ - let fileLocation - if (filePath.indexOf("/Logs/") > 0) { - fileLocation = process.env.LOG_PATH + '/' + filePath - } else { - fileLocation = process.env.STORAGE_PATH + '/' + filePath - } + recursiveDeleteTextFile(filePath, callBackFunction) - try { + function recursiveDeleteTextFile(filePath, callBackFunction) { + logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> Entering Function.') - logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> fileLocation: ' + fileLocation) + /* Choose path for either logs or data */ + let fileLocation + if (filePath.indexOf("/Logs/") > 0) { + fileLocation = process.env.LOG_PATH + '/' + filePath + } else { + fileLocation = process.env.STORAGE_PATH + '/' + filePath + } - const fs = require('fs') - fs.unlink(fileLocation, onUnlinked) + try { - function onUnlinked(err) { - if (err) { - if (callBackFunction !== undefined) { - callBackFunction(global.DEFAULT_FAIL_RESPONSE) + logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> fileLocation: ' + fileLocation) + + const fs = require('fs') + fs.unlink(fileLocation, onUnlinked) + + function onUnlinked(err) { + let retryTimeToUse = FAST_RETRY_TIME_IN_MILISECONDS + if (currentRetryDeleteTextFile > MAX_RETRY - 2) { + retryTimeToUse = SLOW_RETRY_TIME_IN_MILISECONDS } + if (err) { + if (callBackFunction !== undefined) { + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } + } else { + setTimeout(retry, retryTimeToUse) + } + } + } 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 (currentRetryDeleteTextFile < MAX_RETRY) { + currentRetryDeleteTextFile++ + logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> retry -> Will try to delete the file again -> Retry #: ' + currentRetryDeleteTextFile) + recursiveDeleteTextFile(filePath, callBackFunction) } else { + currentRetryDeleteTextFile = 0 + logger.write(MODULE_NAME, '[ERROR] FileStorage -> deleteTextFile -> retry -> Max retries reached deleting a file. Giving up.') + logger.write(MODULE_NAME, '[ERROR] FileStorage -> deleteTextFile -> retry -> file = ' + fileLocation) 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 (currentRetryDeleteTextFile < MAX_RETRY) { - currentRetryDeleteTextFile++ - logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> retry -> Will try to delete the file again -> Retry #: ' + currentRetryDeleteTextFile) - createTextFile(filePath, fileContent, callBackFunction) - } else { - currentRetryDeleteTextFile = 0 - logger.write(MODULE_NAME, '[ERROR] FileStorage -> deleteTextFile -> retry -> Max retries reached deleting a file. Giving up.') - logger.write(MODULE_NAME, '[ERROR] FileStorage -> deleteTextFile -> retry -> file = ' + fileLocation) - callBackFunction(global.DEFAULT_FAIL_RESPONSE) - } } } diff --git a/MultiPeriod.js b/MultiPeriod.js index d9047fb3f..09d54057a 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -332,9 +332,13 @@ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> text = " + text); } if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - - logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = " + err.message); - callBackFunction(err); + if (err.message === 'File does not exist.') { + logger.write(MODULE_NAME, "[ERROR] The file " + filePath + '/' + fileName + ' does not exist and it is required to continue. This process will retry to read it in a while. In the meantime make yourself sure that the process that generates it has ran properly.'); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } else { + logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesMarketFiles -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = " + err.message); + callBackFunction(err); + } return; } @@ -660,7 +664,7 @@ if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { - logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] The file " + filePath + '/' + fileName + ' does not exist and it is required to continue. This process will retry to read it in a while. In the meantime make yourself sure that the process that generates it has ran properly.'); callBackFunction(global.DEFAULT_RETRY_RESPONSE); return; } @@ -721,12 +725,7 @@ if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { - logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Fail Because -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> filePath = " + filePath); - logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> fileName = " + fileName); - logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> You are trying to use data that does not exist. "); - logger.write(MODULE_NAME, "[ERROR] start -> processTimeFramesDailyFiles -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Check that your Datasets building processes are running well. "); - + logger.write(MODULE_NAME, "[ERROR] The file " + filePath + '/' + fileName + ' does not exist and it is required to continue. This process will retry to read it in a while. In the meantime make yourself sure that the process that generates it has ran properly.'); callBackFunction(global.DEFAULT_RETRY_RESPONSE); return; } diff --git a/Session.js b/Session.js index 872258aad..0d6685f89 100644 --- a/Session.js +++ b/Session.js @@ -187,13 +187,6 @@ /* Set all default values */ bot.VALUES_TO_USE = { - baseAsset: "BTC", - initialBalanceA: 0.001, - initialBalanceB: 0, - minimumBalanceA: 0.0005, - minimumBalanceB: 0, - maximumBalanceA: 0.002, - maximumBalanceB: 0, slippage: { positionRate: 0, stopLoss: 0, @@ -238,132 +231,6 @@ if (tradingSystem !== undefined) { - /* Applying Trading System Level Parameters */ - if (tradingSystem.parameters !== undefined) { - - /* Base Asset and Initial Balances. */ - { - if (tradingSystem.parameters.baseAsset !== undefined) { - if (tradingSystem.parameters.baseAsset.referenceParent !== undefined) { - if (tradingSystem.parameters.baseAsset.referenceParent.referenceParent !== undefined) { - - let code = tradingSystem.parameters.baseAsset.referenceParent.referenceParent.code - - if (code.codeName !== undefined) { - bot.VALUES_TO_USE.baseAsset = code.codeName; - } - - code = tradingSystem.parameters.baseAsset.code - - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { - if (code.initialBalance !== undefined) { - bot.VALUES_TO_USE.initialBalanceA = code.initialBalance; - bot.VALUES_TO_USE.initialBalanceB = 0 - } - if (code.minimumBalance !== undefined) { - bot.VALUES_TO_USE.minimumBalanceA = code.minimumBalance; - bot.VALUES_TO_USE.minimumBalanceB = 0 - } - if (code.maximumBalance !== undefined) { - bot.VALUES_TO_USE.maximumBalanceA = code.maximumBalance; - bot.VALUES_TO_USE.maximumBalanceB = 0 - } - } else { - if (code.initialBalance !== undefined) { - bot.VALUES_TO_USE.initialBalanceB = code.initialBalance; - bot.VALUES_TO_USE.initialBalanceA = 0 - } - if (code.minimumBalance !== undefined) { - bot.VALUES_TO_USE.minimumBalanceB = code.minimumBalance; - bot.VALUES_TO_USE.minimumBalanceA = 0 - } - if (code.maximumBalance !== undefined) { - bot.VALUES_TO_USE.maximumBalanceB = code.maximumBalance; - bot.VALUES_TO_USE.maximumBalanceA = 0 - } - } - } - } - } - } - - /* Quoted Asset */ - { - if (tradingSystem.parameters.quotedAsset !== undefined) { - if (tradingSystem.parameters.quotedAsset.referenceParent !== undefined) { - if (tradingSystem.parameters.quotedAsset.referenceParent.referenceParent !== undefined) { - - let code = tradingSystem.parameters.quotedAsset.referenceParent.referenceParent.code - - if (code.codeName !== undefined) { - bot.VALUES_TO_USE.quotedAsset = code.codeName; - } - } - } - } - } - - /* Time Frame */ - if (tradingSystem.parameters.timeFrame !== undefined) { - bot.VALUES_TO_USE.timeFrame = tradingSystem.parameters.timeFrame.code.value - } - - /* Slippage */ - if (tradingSystem.parameters.slippage !== undefined) { - if (tradingSystem.parameters.slippage.code !== undefined) { - - let code = tradingSystem.parameters.slippage.code - - if (code.positionRate !== undefined) { - bot.VALUES_TO_USE.slippage.positionRate = code.positionRate - } - if (code.stopLoss !== undefined) { - bot.VALUES_TO_USE.slippage.stopLoss = code.stopLoss - } - if (code.takeProfit !== undefined) { - bot.VALUES_TO_USE.slippage.takeProfit = code.takeProfit - } - } - } - - /* Fee Structure */ - if (tradingSystem.parameters.feeStructure !== undefined) { - if (tradingSystem.parameters.feeStructure.code !== undefined) { - - let code = tradingSystem.parameters.feeStructure.code - - if (code.maker !== undefined) { - bot.VALUES_TO_USE.feeStructure.maker = code.maker - } - if (code.taker !== undefined) { - bot.VALUES_TO_USE.feeStructure.taker = code.taker - } - } - } - - /* Time Range */ - if (tradingSystem.parameters.timeRange !== undefined) { - if (tradingSystem.parameters.timeRange.code !== undefined) { - - let code = tradingSystem.parameters.timeRange.code - if (code.initialDatetime !== undefined) { - if (isNaN(Date.parse(code.initialDatetime)) === true) { - parentLogger.write(MODULE_NAME, "[WARN] initialize -> runSession -> setValuesToUse -> Cannot use initialDatatime provided at Trading System Parameters because it is not a valid Date."); - } else { - bot.VALUES_TO_USE.timeRange.initialDatetime = new Date(code.initialDatetime) - } - } - if (code.finalDatetime !== undefined) { - if (isNaN(Date.parse(code.finalDatetime)) === true) { - parentLogger.write(MODULE_NAME, "[WARN] initialize -> runSession -> setValuesToUse -> Cannot use finalDatetime provided at Trading System Parameters because it is not a valid Date."); - } else { - bot.VALUES_TO_USE.timeRange.finalDatetime = new Date(code.finalDatetime) - } - } - } - } - } - /* Applying Session Level Parameters */ if (bot.SESSION !== undefined) { if (bot.SESSION.parameters !== undefined) { @@ -409,6 +276,23 @@ bot.VALUES_TO_USE.maximumBalanceA = 0 } } + + /* Solve when values are missing. */ + if (bot.VALUES_TO_USE.minimumBalanceA === undefined) { + bot.VALUES_TO_USE.minimumBalanceA = 0 + } + + if (bot.VALUES_TO_USE.minimumBalanceB === undefined) { + bot.VALUES_TO_USE.minimumBalanceB = 0 + } + + if (bot.VALUES_TO_USE.maximumBalanceA === undefined) { + bot.VALUES_TO_USE.maximumBalanceA = 10000000000000000 + } + + if (bot.VALUES_TO_USE.maximumBalanceB === undefined) { + bot.VALUES_TO_USE.maximumBalanceB = 10000000000000000 + } } } } @@ -512,16 +396,16 @@ for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) { let socialBot = bot.SESSION.socialBots.bots[i] try { - let code = JSON.parse(announcement.code) - if (socialBot.id === announcement.referenceParent.id) { - if (socialBot.type === "Telegram Bot") { - if (announcement.formulaValue !== undefined) { - 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).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err)) - } + let code = announcement.code + + if (socialBot.type === "Telegram Bot") { + if (announcement.formulaValue !== undefined) { + 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).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err)) } } + } catch (err) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> err = " + err.stack); } diff --git a/TradingBot.js b/TradingBot.js index 7dacf7836..833b16693 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -356,7 +356,8 @@ record.status + "," + record.number + "," + record.beginRate + "," + - record.endRate + "]"; + record.endRate + "," + + '"' + record.triggerOnSituation + '"' + "]"; if (separator === "") { separator = ","; } @@ -428,7 +429,8 @@ record.lastTradeROI + "," + record.beginRate + "," + record.endRate + "," + - record.exitType + "]"; + record.exitType + "," + + '"' + record.takePositionSituation + '"' + "]"; if (separator === "") { separator = ","; } @@ -816,7 +818,8 @@ record.status + "," + record.number + "," + record.beginRate + "," + - record.endRate + "]"; + record.endRate + "," + + '"' + record.triggerOnSituation + '"' + "]"; if (separator === "") { separator = ","; } @@ -894,7 +897,8 @@ record.lastTradeROI + "," + record.beginRate + "," + record.endRate + "," + - record.exitType + "]"; + record.exitType + "," + + '"' + record.takePositionSituation + '"' + "]"; if (separator === "") { separator = ","; } diff --git a/TradingSimulation.js b/TradingSimulation.js index 59413023f..471ae162d 100644 --- a/TradingSimulation.js +++ b/TradingSimulation.js @@ -14,21 +14,9 @@ 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 } @@ -141,7 +129,8 @@ status: 0, number: 0, beginRate: 0, - endRate: 0 + endRate: 0, + triggerOnSituation: '' } let currentTrade = { @@ -151,7 +140,8 @@ profit: 0, exitType: 0, beginRate: 0, - endRate: 0 + endRate: 0, + takePositionSituation: '' } let balanceBaseAsset = bot.VALUES_TO_USE.initialBalanceA; @@ -183,6 +173,10 @@ let orderId = 0; let messageId = 0; + /* Allowing these to be accesible at formulas */ + baseAsset = bot.VALUES_TO_USE.baseAsset + quotedAsset = bot.VALUES_TO_USE.quotedAsset + let yesterday = {}; /* Initialization */ @@ -211,7 +205,8 @@ status: 0, number: 0, beginRate: 0, - endRate: 0 + endRate: 0, + triggerOnSituation: '' } yesterday.currentTrade = { @@ -221,7 +216,8 @@ profit: 0, exitType: 0, beginRate: 0, - endRate: 0 + endRate: 0, + takePositionSituation: '' } yesterday.balanceBaseAsset = balanceBaseAsset; @@ -279,7 +275,8 @@ status: 0, number: 0, beginRate: 0, - endRate: 0 + endRate: 0, + triggerOnSituation: '' } interExecutionMemory.currentTrade = { @@ -289,7 +286,8 @@ profit: 0, exitType: 0, beginRate: 0, - endRate: 0 + endRate: 0, + takePositionSituation: '' } interExecutionMemory.balanceBaseAsset = balanceBaseAsset; @@ -349,7 +347,8 @@ status: interExecutionMemory.currentStrategy.status, number: interExecutionMemory.currentStrategy.number, beginRate: interExecutionMemory.currentStrategy.beginRate, - endRate: interExecutionMemory.currentStrategy.endRate + endRate: interExecutionMemory.currentStrategy.endRate, + triggerOnSituation: interExecutionMemory.currentStrategy.triggerOnSituation } currentTrade = { @@ -359,7 +358,8 @@ profit: interExecutionMemory.currentTrade.profit, exitType: interExecutionMemory.currentTrade.exitType, beginRate: interExecutionMemory.currentTrade.beginRate, - endRate: interExecutionMemory.currentTrade.endRate + endRate: interExecutionMemory.currentTrade.endRate, + takePositionSituation: interExecutionMemory.currentTrade.takePositionSituation } if (currentDay) { @@ -422,7 +422,8 @@ status: currentStrategy.status, number: currentStrategy.number, beginRate: currentStrategy.beginRate, - endRate: currentStrategy.endRate + endRate: currentStrategy.endRate, + triggerOnSituation: currentStrategy.triggerOnSituation } yesterday.currentTrade = { @@ -432,7 +433,8 @@ profit: currentTrade.profit, exitType: currentTrade.exitType, beginRate: currentTrade.beginRate, - endRate: currentTrade.endRate + endRate: currentTrade.endRate, + takePositionSituation: currentTrade.takePositionSituation } yesterday.lastTradeProfitLoss = lastTradeProfitLoss; @@ -570,9 +572,19 @@ 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 # " + currentCandleIndex + " @ " + processingDate) } - console.log("Trading-Engine -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + currentCandleIndex + " @ " + processingDate) - bot.sessionHeartBeat(processingDate) // tell the world we are alive and doing well + /* Telling the world we are alive and doing well */ + let fromDate = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) + let lastDate = new Date(bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) + + let currentDateString = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2); + let currentDate = new Date(loopingDay) + let percentage = global.getPercentage(fromDate, currentDate, lastDate) + bot.processHeartBeat(currentDateString, percentage) + + if (global.areEqualDates(currentDate, new Date()) === false) { + logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage); + } } previousLoopingDay = loopingDay.valueOf() @@ -706,7 +718,7 @@ if (openStage !== undefined) { /* Default Values*/ - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { positionSize = balanceBaseAsset; positionRate = candle.close; } else { @@ -729,13 +741,13 @@ initialDefinition.positionSize.formula.error = err.message } if (isNaN(positionSize)) { - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { positionSize = balanceBaseAsset; } else { positionSize = balanceQuotedAsset; } } else { - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { if (positionSize > balanceBaseAsset) { positionSize = balanceBaseAsset } } else { if (positionSize > balanceQuotedAsset) { positionSize = balanceQuotedAsset } @@ -756,7 +768,7 @@ initialDefinition.positionRate.formula.error = err.message } if (isNaN(positionRate)) { - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { positionRate = candle.close; } else { positionRate = candle.close; @@ -1093,7 +1105,7 @@ let maximumBalance let balance - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { balance = balanceBaseAsset minimumBalance = bot.VALUES_TO_USE.minimumBalanceA maximumBalance = bot.VALUES_TO_USE.maximumBalanceA @@ -1102,100 +1114,101 @@ minimumBalance = bot.VALUES_TO_USE.minimumBalanceB maximumBalance = bot.VALUES_TO_USE.maximumBalanceB } - - if (minimumBalance === undefined) { - minimumBalance = 0 + + let stopRunningDate = new Date(candle.begin) + if (balance <= minimumBalance) { + tradingSystem.error = "Min Balance @ " + stopRunningDate.toLocaleString() + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> " + tradingSystem.error ); } + afterLoop() + return } - if (maximumBalance === undefined) { - maximumBalance = 10000000000000000 + if (balance >= maximumBalance) { + tradingSystem.error = "Max Balance @ " + stopRunningDate.toLocaleString() + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> " + tradingSystem.error); } + afterLoop() + return } - 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. + /* + 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. + 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++) { + for (let j = 0; j < tradingSystem.strategies.length; j++) { - let strategy = tradingSystem.strategies[j]; + let strategy = tradingSystem.strategies[j]; - let triggerStage = strategy.triggerStage + let triggerStage = strategy.triggerStage - if (triggerStage !== undefined) { + if (triggerStage !== undefined) { - if (triggerStage.triggerOn !== undefined) { + if (triggerStage.triggerOn !== undefined) { - for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { + for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) { - let situation = triggerStage.triggerOn.situations[k]; - let passed = true; + let situation = triggerStage.triggerOn.situations[k]; + let passed = true; - for (let m = 0; m < situation.conditions.length; m++) { + for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m]; - let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + 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; } + let value = false + if (conditions.get(key) !== undefined) { + value = conditions.get(key).value; } - if (passed) { + if (value === false) { passed = false; } + } - 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 (passed) { - 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; - } + strategyStage = 'Trigger Stage'; + checkAnnouncements(triggerStage) + + currentStrategyIndex = j; + currentStrategy.begin = candle.begin; + currentStrategy.beginRate = candle.min; + currentStrategy.endRate = candle.min; // In case the strategy does not get exited + currentStrategy.triggerOnSituation = situation.name + + 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; + yesterday.currentStrategy.triggerOnSituation = currentStrategy.triggerOnSituation; } - - 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; } + + 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 */ @@ -1299,6 +1312,8 @@ type = '"Take Position"'; strategyStage = 'Open Stage'; + checkAnnouncements(strategy.openStage) + stopLossStage = 'Open Stage'; takeProfitStage = 'Open Stage'; stopLossPhase = 0; @@ -1315,7 +1330,8 @@ } takePositionNow = true - + currentTrade.takePositionSituation = situation.name + checkAnnouncements(triggerStage.takePosition) if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Conditions at the Take Position Event were met."); } @@ -1396,7 +1412,10 @@ stopLossPhase++; stopLossStage = 'Manage Stage' - if (takeProfitPhase > 0) { strategyStage = 'Manage Stage' } + if (takeProfitPhase > 0) { + strategyStage = 'Manage Stage' + checkAnnouncements(manageStage, 'Take Profit') + } if (processingDailyFiles) { if (positionedAtYesterday) { @@ -1501,6 +1520,7 @@ if (parentNode !== undefined) { let phase = parentNode.takeProfit.phases[p]; + if (phase === undefined) {return} // trying to jump to a phase that does not exists. let nextPhaseEvent = phase.nextPhaseEvent; if (nextPhaseEvent !== undefined) { @@ -1527,7 +1547,10 @@ takeProfitPhase++; takeProfitStage = 'Manage Stage' - if (stopLossPhase > 0) { strategyStage = 'Manage Stage' } + if (stopLossPhase > 0) { + strategyStage = 'Manage Stage' + checkAnnouncements(manageStage, 'Stop') + } if (processingDailyFiles) { if (positionedAtYesterday) { @@ -1683,7 +1706,7 @@ /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ - if ((bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset && candle.max >= stopLoss) || (bot.VALUES_TO_USE.baseAsset !== bot.market.baseAsset && candle.min <= stopLoss)) { + if ((baseAsset === bot.market.baseAsset && candle.max >= stopLoss) || (baseAsset !== bot.market.baseAsset && candle.min <= stopLoss)) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Stop Loss was hit."); } /* @@ -1693,7 +1716,7 @@ If we take the stop loss value at those situation would be a huge distortion of facts. */ - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { if (stopLoss < candle.min) { stopLoss = candle.min if (processingDailyFiles) { @@ -1718,7 +1741,7 @@ /* Apply the Slippage */ let slippageAmount = slippedStopLoss * bot.VALUES_TO_USE.slippage.stopLoss / 100 - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { slippedStopLoss = slippedStopLoss + slippageAmount } else { slippedStopLoss = slippedStopLoss - slippageAmount @@ -1729,6 +1752,8 @@ marketRate = closeRate; type = '"Close@StopLoss"'; strategyStage = 'Close Stage'; + checkAnnouncements(strategy.closeStage, 'Stop') + stopLossStage = 'No Stage'; takeProfitStage = 'No Stage'; currentTrade.end = candle.end; @@ -1753,7 +1778,7 @@ /* Take Profit condition: Here we verify if the Take Profit was hit or not. */ - if ((bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset && candle.min <= takeProfit) || (bot.VALUES_TO_USE.baseAsset !== bot.market.baseAsset && candle.max >= takeProfit)) { + if ((baseAsset === bot.market.baseAsset && candle.min <= takeProfit) || (baseAsset !== bot.market.baseAsset && candle.max >= takeProfit)) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Take Profit was hit."); } /* @@ -1763,7 +1788,7 @@ If we take the stop loss value at those situation would be a huge distortion of facts. */ - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { if (takeProfit > candle.max) { takeProfit = candle.max if (processingDailyFiles) { @@ -1787,7 +1812,7 @@ /* Apply the Slippage */ let slippageAmount = slippedTakeProfit * bot.VALUES_TO_USE.slippage.takeProfit / 100 - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { slippedTakeProfit = slippedTakeProfit + slippageAmount } else { slippedTakeProfit = slippedTakeProfit - slippageAmount @@ -1798,6 +1823,8 @@ marketRate = closeRate; type = '"Close@TakeProfit"'; strategyStage = 'Close Stage'; + checkAnnouncements(strategy.closeStage, 'Take Profit') + stopLossStage = 'No Stage'; takeProfitStage = 'No Stage'; @@ -1848,7 +1875,7 @@ /* 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 === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { tradePositionRate = tradePositionRate - slippageAmount } else { tradePositionRate = tradePositionRate + slippageAmount @@ -1976,7 +2003,7 @@ let orderSide - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { orderSide = "sell" orderPrice = tradePositionRate - 100 // This is going to be ingnored at the Exchange API for now since we only put market orders. @@ -2070,7 +2097,7 @@ let feePaid = 0 - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { feePaid = tradePositionSize * tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100 @@ -2182,7 +2209,7 @@ let amountB let orderSide - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { orderSide = "buy" orderPrice = ticker.last + 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. @@ -2264,7 +2291,7 @@ let feePaid = 0 - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { strategy.positionSize = balanceQuotedAsset / closeRate; strategy.positionRate = closeRate; @@ -2290,7 +2317,7 @@ } } - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { lastTradeProfitLoss = balanceBaseAsset - previousBalanceBaseAsset; lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; if (isNaN(lastTradeROI)) { lastTradeROI = 0; } @@ -2337,7 +2364,7 @@ } } - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { ROI = (bot.VALUES_TO_USE.initialBalanceA + profit) / bot.VALUES_TO_USE.initialBalanceA - 1; hitRatio = hits / roundtrips; anualizedRateOfReturn = ROI / days * 365; @@ -2495,8 +2522,8 @@ balanceQuotedAsset = Number.MAX_SAFE_INTEGER } - let quotedBaseAsset = '"' + bot.VALUES_TO_USE.baseAsset + '"' - let quotedQuotedAsset = '"' + bot.VALUES_TO_USE.quotedAsset + '"' + let quotedBaseAsset = '"' + baseAsset + '"' + let quotedQuotedAsset = '"' + quotedAsset + '"' simulationRecord = { begin: candle.begin, @@ -2578,7 +2605,8 @@ status: 0, number: 0, beginRate: 0, - endRate: 0 + endRate: 0, + triggerOnSituation: '' } if (processingDailyFiles) { @@ -2589,7 +2617,8 @@ status: 0, number: 0, beginRate: 0, - endRate: 0 + endRate: 0, + triggerOnSituation: '' } } } @@ -2605,7 +2634,7 @@ currentTrade.endRate = candle.close /* Here we will calculate the ongoing ROI */ - if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + if (baseAsset === bot.market.baseAsset) { currentTrade.lastTradeROI = (tradePositionRate - candle.close) / tradePositionRate * 100 } else { currentTrade.lastTradeROI = (candle.close - tradePositionRate) / tradePositionRate * 100 @@ -2626,7 +2655,8 @@ lastTradeROI: 0, exitType: 0, beginRate: 0, - endRate: 0 + endRate: 0, + takePositionSituation: '' } if (processingDailyFiles) { @@ -2638,7 +2668,8 @@ lastTradeROI: 0, exitType: 0, beginRate: 0, - endRate: 0 + endRate: 0, + takePositionSituation: '' } } } @@ -2649,7 +2680,7 @@ function checkAnnouncements(node, value) { /* - Value is an optional parameter that represents the value that the announcement is monitoring for change. + Value is an optional parameter that represents the value that the announcement is monitoring for change (for numeric values only). 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. */ @@ -2673,21 +2704,20 @@ 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 valueVariation - try { - let code = JSON.parse(announcement.code) - valueVariation = code.valueVariation - } catch (err) { - announcement.error = err.message - } + if (isNaN(value) === false) { + /* The Value Variation is what tells us how much the value already announced must change in order to annouce it again. */ + let valueVariation - 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 + let code = announcement.code + valueVariation = code.valueVariation + + 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 + } } } @@ -2695,6 +2725,7 @@ We store the announcement temporarily at an Array to differ its execution, becasue we need to evaulate its formula and at this point in time the potential variables used at the formula are still not set. */ + announcement.value = value announcementsToBeMade.push(announcement) /* Next, we will remmeber this announcement was already done, so that it is not announced again in further processing of the same day. */ @@ -2722,6 +2753,7 @@ let formulaValue if (announcement.formula !== undefined) { try { + let value = announcement.value formulaValue = eval(announcement.formula.code); } catch (err) { announcement.formula.error = err.message @@ -2729,7 +2761,9 @@ } announcement.formulaValue = formulaValue - bot.SESSION.socialBots.announce(announcement) + if (bot.SESSION.socialBots !== undefined) { + bot.SESSION.socialBots.announce(announcement) + } } } } @@ -2741,8 +2775,15 @@ /* 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.") + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing this session."); } + console.log("[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing this session.") + afterLoop() + return + } + + if (global.STOP_TASK_GRACEFULLY === 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 this task."); } + console.log("[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing this task.") afterLoop() return } @@ -2791,7 +2832,8 @@ status: yesterday.currentStrategy.status, number: yesterday.currentStrategy.number, beginRate: yesterday.currentStrategy.beginRate, - endRate: yesterday.currentStrategy.endRate + endRate: yesterday.currentStrategy.endRate, + triggerOnSituation: yesterday.currentStrategy.triggerOnSituation } interExecutionMemory.currentTrade = { @@ -2801,7 +2843,8 @@ profit: yesterday.currentTrade.profit, exitType: yesterday.currentTrade.exitType, beginRate: yesterday.currentTrade.beginRate, - endRate: yesterday.currentTrade.endRate + endRate: yesterday.currentTrade.endRate, + takePositionSituation: yesterday.currentTrade.takePositionSituation } interExecutionMemory.balanceBaseAsset = yesterday.balanceBaseAsset;