diff --git a/LowFrequencyTrading/TradingOutput.js b/LowFrequencyTrading/TradingOutput.js index 25312ea36..ac3a6ca72 100644 --- a/LowFrequencyTrading/TradingOutput.js +++ b/LowFrequencyTrading/TradingOutput.js @@ -27,7 +27,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL } - async function start(chart, timeFrame, timeFrameLabel, currentDay) { + async function start(chart, timeFrame, timeFrameLabel, tradingProcessDate) { try { if (timeFrame > global.dailyFilePeriods[0][0]) { @@ -41,7 +41,6 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL let tradingSimulation = TRADING_SIMULATION.newTradingSimulation(bot, logger, UTILITIES) let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets - let market = bot.market let outputDatasetsMap = new Map() if (bot.FIRST_EXECUTION === true && bot.RESUME === false) { @@ -127,7 +126,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL if (dataset.config.type === 'Daily Files') { - let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let dateForPath = tradingProcessDate.getUTCFullYear() + '/' + utilities.pad(tradingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(tradingProcessDate.getUTCDate(), 2); let fileName = 'Data.json' let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath @@ -184,7 +183,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL if (dataset.config.type === 'Daily Files') { - let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let dateForPath = tradingProcessDate.getUTCFullYear() + '/' + utilities.pad(tradingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(tradingProcessDate.getUTCDate(), 2); let fileName = 'Data.json' let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath diff --git a/LowFrequencyTrading/TradingProcess.js b/LowFrequencyTrading/TradingProcess.js index 971060a52..6932a8e56 100644 --- a/LowFrequencyTrading/TradingProcess.js +++ b/LowFrequencyTrading/TradingProcess.js @@ -66,19 +66,31 @@ try { let currentTimeFrame let currentTimeFrameLabel - let market = bot.market; /* Context Variables */ - let contextVariables = { lastFile: undefined, // Datetime of the last file files sucessfully produced by this process. dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history. dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process. }; - let previousDay; // Holds the date of the previous day relative to the processing date. - getContextVariables() + + /* Initializing the Trading Process Date */ + if (bot.FIRST_EXECUTION === true && bot.RESUME === false) { + /* + This funcion is going to be called many times by the Trading Bot Loop. + Only during the first execution and when the User is not resuming the execution + of a stopped session / task; we are going to initialize the Process Date Time. + This variable tell us which day we are standing at, specially while working + with Daily Files. From this Date is that we are going to load the Daily Files. + */ + bot.simulationState.tradingEngine.current.episode.processDate.value = bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf() + } + + /* This is the Date that is going to be used across the execution of this Trading Process */ + let tradingProcessDate = new Date(bot.simulationState.tradingEngine.current.episode.processDate.value) + await processSingleFiles() await processMarketFiles() @@ -102,19 +114,20 @@ await generateOutput(chart) await writeProcessFiles() - checkIfSessionMustStop() - } else { /* We are processing Daily Files */ - - /* - Go back one day to start well when we advance time at the begining of the loop. - */ - bot.tradingProcessDate = new Date(contextVariables.lastFile.valueOf() - global.ONE_DAY_IN_MILISECONDS); do { - if (advanceTime() === false) {break} + if (checkStopHeadOfTheMarket() === false) {break} if (checkStopTaskGracefully() === false) {break} if (checkStopProcessing() === false) {break} + + /* + We update the Trading Process Date with the date calculated at the simulation. + We will use this date to load indicator and output files. After that we will + use it to save Output Files and later the Data Ranges. This is the point where + the date calculated by the Simulation is applied at the Trading Process Level. + */ + tradingProcessDate = new Date(bot.simulationState.tradingEngine.current.episode.processDate.value) await processDailyFiles() @@ -126,19 +139,23 @@ while (true) } + checkIfSessionMustStop() + /* + Everything worked as expected. We return an OK code and wait for + the Bot Loop to call us again later. + */ callBackFunction(global.DEFAULT_OK_RESPONSE) function getContextVariables() { try { let thisReport; - let reportKey; let statusReport; /* We are going to use the start date as beging of market date. */ contextVariables.dateBeginOfMarket = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime) /* - Here we get the status report from the bot who knows which is the end of the market. + Here we get the status report from the bot who knows which is the end of the market. */ statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point") @@ -170,17 +187,6 @@ contextVariables.dateEndOfMarket = new Date(thisReport.lastFile.valueOf()); - /* Validation that the data is not up-to-date. */ - if (bot.tradingProcessDate.valueOf() - global.ONE_DAY_IN_MILISECONDS > contextVariables.dateEndOfMarket.valueOf() && bot.SESSION.type !== "Backtesting Session") { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> " + "Head of the market is @ " + contextVariables.dateEndOfMarket); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> You need to UPDATE your datasets in order to run a " + bot.SESSION.type) } - - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return - - } - /* Finally we get our own Status Report. */ statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") @@ -256,29 +262,13 @@ /* We cut the async calls via callBacks at this point, so as to have a clearer code upstream */ let response = await asyncGetDatasetFile(datasetModule, filePath, fileName) - onFileReceived(response.err, response.text) - function onFileReceived(err, text) { - try { - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - 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 -> processSingleFiles -> onFileReceived -> err = " + err.message); - callBackFunction(err); - } - return - } - - let dataFile = JSON.parse(text); - dataFiles.set(dependency.id, dataFile); - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> processSingleFiles -> onFileReceived -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw(response.err) } + + let dataFile = JSON.parse(response.text); + dataFiles.set(dependency.id, dataFile); } let mapKey = "Single Files" @@ -321,30 +311,14 @@ /* We cut the async calls via callBacks at this point, so as to have a clearer code upstream */ let response = await asyncGetDatasetFile(datasetModule, filePath, fileName) - onFileReceived(response.err, response.text) - function onFileReceived(err, text) { - try { - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - 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 -> processMarketFiles -> onFileReceived -> err = " + err.message); - callBackFunction(err); - } - return - } - - let dataFile = JSON.parse(text); - let trimmedDataFile = trimDataFile(dataFile, datasetModule.node.parentNode.record) - dataFiles.set(dependency.id, trimmedDataFile); - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> processMarketFiles -> onFileReceived -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw(response.err) } + + let dataFile = JSON.parse(response.text); + let trimmedDataFile = trimDataFile(dataFile, datasetModule.node.parentNode.record) + dataFiles.set(dependency.id, trimmedDataFile); function trimDataFile(dataFile, recordDefinition) { /* @@ -381,8 +355,8 @@ async function processDailyFiles() { /* Telling the world we are alive and doing well and which date we are processing right now. */ - let processingDate = bot.tradingProcessDate.getUTCFullYear() + '-' + utilities.pad(bot.tradingProcessDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(bot.tradingProcessDate.getUTCDate(), 2); - bot.processHeartBeat(processingDate) + let processingDateString = tradingProcessDate.getUTCFullYear() + '-' + utilities.pad(tradingProcessDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(tradingProcessDate.getUTCDate(), 2); + bot.processHeartBeat(processingDateString) /* We will iterate through all posible timeFrames. @@ -416,7 +390,6 @@ for (let dependencyIndex = 0; dependencyIndex < dataDependenciesModule.nodeArray.length; dependencyIndex++) { let dependency = dataDependenciesModule.nodeArray[dependencyIndex]; let datasetModule = dataDependenciesModule.dataSetsModulesArray[dependencyIndex]; - let file if (dependency.referenceParent.config.codeName !== "Multi-Period-Daily") { continue @@ -428,7 +401,7 @@ } } - let dateForPath = bot.tradingProcessDate.getUTCFullYear() + '/' + utilities.pad(bot.tradingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.tradingProcessDate.getUTCDate(), 2); + let dateForPath = tradingProcessDate.getUTCFullYear() + '/' + utilities.pad(tradingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(tradingProcessDate.getUTCDate(), 2); let filePath if (dependency.referenceParent.config.codeName === "Multi-Period-Daily") { filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + timeFrameLabel + "/" + dateForPath; @@ -439,30 +412,13 @@ /* We cut the async calls via callBacks at this point, so as to have a clearer code upstream */ let response = await asyncGetDatasetFile(datasetModule, filePath, fileName) - onFileReceived(response.err, response.text) - - function onFileReceived(err, text) { - try { - if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key 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); - return - } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] start -> processDailyFiles -> onFileReceived -> Not Ok -> err = " + err.code); - callBackFunction(err); - return - } - - file = JSON.parse(text); - dataFiles.set(dependency.id, file); - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> processDailyFiles -> onFileReceived -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } + + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw(response.err) } + + let dataFile = JSON.parse(response.text); + dataFiles.set(dependency.id, dataFile); } let mapKey = global.dailyFilePeriods[n][1]; @@ -470,26 +426,14 @@ } } - function advanceTime() { - bot.tradingProcessDate = new Date(bot.tradingProcessDate.valueOf() + global.ONE_DAY_IN_MILISECONDS); - previousDay = new Date(bot.tradingProcessDate.valueOf() - global.ONE_DAY_IN_MILISECONDS); - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> advanceTime -> bot.tradingProcessDate = " + bot.tradingProcessDate); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> advanceTime -> previousDay = " + previousDay); } - - /* Validation that we are not going past the user defined end date. */ - if (bot.tradingProcessDate.valueOf() >= bot.SESSION.parameters.timeRange.config.finalDatetime) { - const logText = "User Defined End Datetime reached @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> advanceTime -> " + logText); } - - bot.SESSION.stop(logText) - return false - } - - /* Validation that we are not going past the head of the market. */ - if (bot.tradingProcessDate.valueOf() > contextVariables.dateEndOfMarket.valueOf() + global.ONE_DAY_IN_MILISECONDS - 1) { - const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + "."; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> advanceTime -> " + logText); } + function checkStopHeadOfTheMarket() { + /* + We need to check if we have reached the head of the market in order to know + when to break the Daily Files Process loop and give time for a new candles / + indicators to be built and continue the processing once this process is called + again. + */ + if (bot.simulationState.tradingEngine.current.episode.headOfTheMarket.value === true) { return false } } @@ -565,7 +509,7 @@ chart, currentTimeFrame, currentTimeFrameLabel, - bot.tradingProcessDate + tradingProcessDate ) bot.FIRST_EXECUTION = false // From here on, all other loops executions wont be the first execution. @@ -574,24 +518,28 @@ async function writeProcessFiles() { await writeTimeFramesFiles(currentTimeFrame, currentTimeFrameLabel) - await writeDataRanges(currentTimeFrameLabel) + await writeDataRanges() if (currentTimeFrame > global.dailyFilePeriods[0][0]) { await writeMarketStatusReport(currentTimeFrameLabel) } else { - await writeDailyStatusReport(bot.tradingProcessDate, currentTimeFrameLabel) + await writeDailyStatusReport(currentTimeFrameLabel) } - async function writeDataRanges(currentTimeFrameLabel) { - for (let outputDatasetIndex = 0; outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length; outputDatasetIndex++) { + async function writeDataRanges() { + for ( + let outputDatasetIndex = 0; + outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length; + outputDatasetIndex++ + ) { let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.config.codeName; - await writeDataRange(contextVariables.dateBeginOfMarket, bot.tradingProcessDate, productCodeName, currentTimeFrameLabel); + await writeDataRange(productCodeName); } - async function writeDataRange(pBegin, pEnd, productCodeName, currentTimeFrameLabel) { + async function writeDataRange(productCodeName) { let dataRange = { - begin: pBegin.valueOf(), - end: pEnd.valueOf() + global.ONE_DAY_IN_MILISECONDS + begin: contextVariables.dateBeginOfMarket.valueOf(), + end: tradingProcessDate + global.ONE_DAY_IN_MILISECONDS } let fileContent = JSON.stringify(dataRange); @@ -618,12 +566,16 @@ async function writeTimeFramesFiles(currentTimeFrame, currentTimeFrameLabel) { - for (let outputDatasetIndex = 0; outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length; outputDatasetIndex++) { + for ( + let outputDatasetIndex = 0; + outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length; + outputDatasetIndex++ + ) { let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.config.codeName; - await writeTimeFramesFile(currentTimeFrame, currentTimeFrameLabel, productCodeName, 'Multi-Period-Daily') - await writeTimeFramesFile(currentTimeFrame, currentTimeFrameLabel, productCodeName, 'Multi-Period-Market') + await writeTimeFramesFile( currentTimeFrameLabel, productCodeName, 'Multi-Period-Daily') + await writeTimeFramesFile( currentTimeFrameLabel, productCodeName, 'Multi-Period-Market') - async function writeTimeFramesFile(currentTimeFrame, currentTimeFrameLabel, productCodeName, processType) { + async function writeTimeFramesFile(currentTimeFrameLabel, productCodeName, processType) { let timeFramesArray = [] timeFramesArray.push(currentTimeFrameLabel) @@ -641,12 +593,12 @@ } } - async function writeDailyStatusReport(lastFileDate, currentTimeFrameLabel) { + async function writeDailyStatusReport( currentTimeFrameLabel) { let reportKey = bot.dataMine + "-" + bot.codeName + "-" + bot.processNode.referenceParent.config.codeName let thisReport = statusDependencies.statusReports.get(reportKey) thisReport.file.lastExecution = bot.currentDaytime - thisReport.file.lastFile = lastFileDate + thisReport.file.lastFile = tradingProcessDate thisReport.file.simulationState = bot.simulationState thisReport.file.timeFrames = currentTimeFrameLabel await thisReport.asyncSave() @@ -660,33 +612,54 @@ thisReport.file.simulationState = bot.simulationState thisReport.file.timeFrames = currentTimeFrameLabel - logger.newInternalLoop(bot.codeName, bot.process, bot.tradingProcessDate) + logger.newInternalLoop(bot.codeName, bot.process, tradingProcessDate) await thisReport.asyncSave() } } function checkIfSessionMustStop() { - /* - The natural way for the session to stop is when we reached the - user defined end datetime. - */ - const now = new Date() - if (now.valueOf() >= bot.SESSION.parameters.timeRange.config.finalDatetime) { - const logText = "User Defined End Datetime reached @ " + now.getUTCFullYear() + "/" + (now.getUTCMonth() + 1) + "/" + now.getUTCDate() + "."; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeProcessFiles -> onMarketStatusReport -> " + logText); } - bot.SESSION.stop('because it just finished.') - return - } if (bot.SESSION.type === 'Backtesting Session') { - bot.SESSION.stop('Backtesting Session over Market Files Finished.') - return + /* + Backtests needs only one execution of this process to complete. + */ + bot.SESSION.stop('Backtesting Session Finished.') + } else { + /* + The natural way for the session to stop is when we reached the + user defined end datetime. + */ + const now = new Date() + if (now.valueOf() >= bot.SESSION.parameters.timeRange.config.finalDatetime) { + const logText = "User Defined End Datetime reached @ " + now.getUTCFullYear() + "/" + (now.getUTCMonth() + 1) + "/" + now.getUTCDate() + "."; + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeProcessFiles -> onMarketStatusReport -> " + logText); } + + bot.SESSION.stop('Final Datetime Reached.') + } } } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); + /* An unhandled exception occured. in this case we return Fail and log the stack. */ + if (err.stack) { + logger.write(MODULE_NAME, "[ERROR] start -> Unhandled Exception. Will Abort this process. err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* Some expected file was not found. We will return a RETRY code and move on. */ + if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { + logger.write(MODULE_NAME, "[ERROR] File not Found. Will Retry the Process Loop.") + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return + } + + /* Some other handled exception occured. We return Fail and move on. */ + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> Handled Exception. Will Abort this process. err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } } } diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index 72cb3d97e..023008b31 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -368,8 +368,33 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT For the special case of Daily Files, this means that the last candle of each day will never be processed. */ if (tradingEngine.current.episode.candle.index.value + 1 === candles.length - 1) { + /* + When processing daily files, we need a mechanism to turn from one day to the next one. + That mechanism is the one implemented here. If we detect that we are at the last candle of + the day, we will advance current process day. By doing so, during the next execution, the + simulation will receive the candles and indicators files of the next day. + */ + if (bot.processingDailyFiles) { + let candlesPerDay = global.ONE_DAY_IN_MILISECONDS / sessionParameters.timeFrame.config.value + if (tradingEngine.current.episode.candle.index.value + 1 === candlesPerDay - 1) { + /* + Here we found that the next candle of the Simulation is the last candle of the day. + It is time to move to the next day so as to receive next, the indicator files from + the next day. At the same time we will reset the index to be ready to real next time + the first candle of the new set we receive. + */ + tradingEngine.current.episode.candle.index.value = 0 + tradingEngine.current.episode.processDate.value = + tradingEngine.current.episode.processDate.value + global.ONE_DAY_IN_MILISECONDS + } + } + + /* We reached the head of the market */ + tradingEngine.current.episode.headOfTheMarket.value = true return false } else { + /* Wd did not reach the head of the market */ + tradingEngine.current.episode.headOfTheMarket.value = false return true } } diff --git a/Session.js b/Session.js index daed3a114..ccc5d4e3d 100644 --- a/Session.js +++ b/Session.js @@ -67,6 +67,7 @@ setUpAppSchema() setUpSessionFolderName() + /* We validate all parameters received and complete some that might be missing if needed. */ if (checkParemeters() === false) { return } socialBotsModule.initialize() @@ -310,7 +311,6 @@ } function startBackTesting(message) { - bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf()) // Here we initialize the date of the process. return true } @@ -319,8 +319,6 @@ if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> Key 'codeName' or 'secret' not provided. Plese check that and try again."); } return false } - - bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf()) pProcessConfig.liveWaitTime = bot.SESSION.parameters.timeFrame.config.value return true } @@ -332,8 +330,6 @@ return false } - bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf()) - /* Reduce the balance */ let balancePercentage = 1 // This is the default value @@ -356,7 +352,6 @@ } function startPaperTrading(message) { - bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf()) pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value return true }