From afb781e44295f2db8241e67fa9a4b685c6c629d2 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 28 Apr 2018 10:37:40 +0200 Subject: [PATCH] Allows Paralell Run on Live and Backtest from different IDEs --- .vs/AACloud/v15/.suo | Bin 101376 -> 100864 bytes Assistant.js | 345 +++++++++++++------- Context.js | 145 ++++++--- Datasource.js | 526 ------------------------------- Project.njsproj | 3 - Run.js | 79 ++++- Trading Bot Process Main Loop.js | 98 ++++-- 7 files changed, 466 insertions(+), 730 deletions(-) delete mode 100644 Datasource.js diff --git a/.vs/AACloud/v15/.suo b/.vs/AACloud/v15/.suo index 84d954d6aba2025620bc35bad41341d29f5815e0..a77841872cc078a7ad16723bd6806ca80c2f5e37 100644 GIT binary patch delta 4337 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 getMarketRate -> onFileReceived -> Backtest Mode detected."); + case "Live": { - /* We need to find the candle at which the process is currently running. */ + logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Live Mode detected."); - for (let i = 0; i < candleArray.length; i++) { + let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate. let candle = { - open: candleArray[i][2], - close: candleArray[i][3], - min: candleArray[i][0], - max: candleArray[i][1], - begin: candleArray[i][4], - end: candleArray[i][5] + open: lastCandleRecord[2], + close: lastCandleRecord[3], + min: lastCandleRecord[0], + max: lastCandleRecord[1], + begin: lastCandleRecord[4], + end: lastCandleRecord[5] }; - if (bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) { + marketRate = candle.close; + marketRate = Number(marketRate.toFixed(8)); - marketRate = (candle.open + candle.close) / 2; - marketRate = Number(marketRate.toFixed(8)); + /* + Now we verify that this candle is not too old. Lets say no more than 10 minutes old. This could happen if the datasets for + any reason stops being updated. + */ - /* The Backtest Mode simulates that every trade posted is executed. - In order to do this, we will take all open orders from the context and create a trades array similar to the one returned by the Exchange. */ + let delay = (bot.processDatetime.valueOf() - candle.begin) / 1000 / 60; - validateExchangeSyncronicity(); + logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Market Rate " + delay + " minutes old."); + + if (delay > 30) { + + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Market Rate more than 10 minutes old. Retrying later."); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); return; } - } - logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> No candle found for the current Process Datetime."); - logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> bot.processDatetime = " + bot.processDatetime); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - - } else { // We are running LIVE Mode. - - logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Live Mode detected."); - - let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate. - - let candle = { - open: lastCandleRecord[2], - close: lastCandleRecord[3], - min: lastCandleRecord[0], - max: lastCandleRecord[1], - begin: lastCandleRecord[4], - end: lastCandleRecord[5] - }; - - marketRate = candle.close; - marketRate = Number(marketRate.toFixed(8)); - - /* - Now we verify that this candle is not too old. Lets say no more than 2 minutes old. This could happen if the datasets for - any reason stops being updated. - */ - - let delay = (bot.processDatetime.valueOf() - candle.begin) / 1000 / 60; - - logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Market Rate " + delay + " minutes old." ); - - if (delay > 30) { - - logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Market Rate more than 10 minutes old. Retrying later."); - - callBackFunction(global.DEFAULT_RETRY_RESPONSE); + validateExchangeSyncronicity(); return; } - validateExchangeSyncronicity(); - return; + case "Backtest": { + + logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Backtest Mode detected."); + + /* We need to find the candle at which the process is currently running. */ + + for (let i = 0; i < candleArray.length; i++) { + + let candle = { + open: candleArray[i][2], + close: candleArray[i][3], + min: candleArray[i][0], + max: candleArray[i][1], + begin: candleArray[i][4], + end: candleArray[i][5] + }; + + if (bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) { + + marketRate = (candle.open + candle.close) / 2; + marketRate = Number(marketRate.toFixed(8)); + + /* The Backtest Mode simulates that every trade posted is executed. + In order to do this, we will take all open orders from the context and create a trades array similar to the one returned by the Exchange. */ + + validateExchangeSyncronicity(); + return; + } + } + + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> No candle found for the current Process Datetime."); + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> bot.processDatetime = " + bot.processDatetime); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + case "Competition": { + + logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Competition Mode detected."); + + let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate. + + let candle = { + open: lastCandleRecord[2], + close: lastCandleRecord[3], + min: lastCandleRecord[0], + max: lastCandleRecord[1], + begin: lastCandleRecord[4], + end: lastCandleRecord[5] + }; + + marketRate = candle.close; + marketRate = Number(marketRate.toFixed(8)); + + /* + Now we verify that this candle is not too old. Lets say no more than 10 minutes old. This could happen if the datasets for + any reason stops being updated. + */ + + let delay = (bot.processDatetime.valueOf() - candle.begin) / 1000 / 60; + + logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Market Rate " + delay + " minutes old."); + + if (delay > 30) { + + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Market Rate more than 10 minutes old. Retrying later."); + + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + + validateExchangeSyncronicity(); + return; + } + + default: { + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Unexpected bot.runMode."); + logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> bot.runMode = " + bot.runMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } } } catch (err) { @@ -250,18 +300,36 @@ */ - if (bot.backTestingMode === true) { + switch (bot.runMode) { - if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Backtest Mode Detected."); } + case "Live": { - let exchangePositions = []; // We simulate all positions were executed. + if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Live Mode Detected."); } + exchangeAPI.getOpenPositions(global.MARKET, onResponse); + break; + } - onResponse(global.DEFAULT_OK_RESPONSE, exchangePositions); - } else { + case "Backtest": { - if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Live Mode Detected."); } - exchangeAPI.getOpenPositions(global.MARKET, onResponse); + if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Backtest Mode Detected."); } + let exchangePositions = []; // We simulate all positions were executed. + onResponse(global.DEFAULT_OK_RESPONSE, exchangePositions); + break; + } + case "Competition": { + + if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Competition Mode Detected."); } + exchangeAPI.getOpenPositions(global.MARKET, onResponse); + break; + } + + default: { + logger.write("[ERROR] getPositionsAtExchange -> Unexpected bot.runMode."); + logger.write("[ERROR] getPositionsAtExchange -> bot.runMode = " + bot.runMode); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } } function onResponse(err, pExchangePositions) { @@ -481,47 +549,66 @@ */ - if (bot.backTestingMode === true) { + switch (bot.runMode) { - if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Backtest Mode Detected."); } + case "Live": { - let trades = []; + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Live Mode Detected."); } + exchangeAPI.getExecutedTrades(pPositionId, onResponse); + return; - /* We look for the position at the executionContext */ - - for (let i = 0; i < context.executionContext.positions.length; i++) { - - let thisPosition = context.executionContext.positions[i]; - - if (thisPosition.id === pPositionId) { - - let trade = { - id: Math.trunc(Math.random(1) * 1000000), - type: thisPosition.type, - rate: thisPosition.rate, - amountA: thisPosition.amountA, - amountB: thisPosition.amountB, - fee: 0, - date: (new Date()).valueOf() - } - - trades.push(trade); - - onResponse(global.DEFAULT_OK_RESPONSE, trades); - - return; - } } - logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Position not found at Executioin Context."); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; + case "Backtest": { - } else { + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Backtest Mode Detected."); } - if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Live Mode Detected."); } - exchangeAPI.getExecutedTrades(pPositionId, onResponse); + let trades = []; + /* We look for the position at the executionContext */ + + for (let i = 0; i < context.executionContext.positions.length; i++) { + + let thisPosition = context.executionContext.positions[i]; + + if (thisPosition.id === pPositionId) { + + let trade = { + id: Math.trunc(Math.random(1) * 1000000), + type: thisPosition.type, + rate: thisPosition.rate, + amountA: thisPosition.amountA, + amountB: thisPosition.amountB, + fee: 0, + date: (new Date()).valueOf() + } + + trades.push(trade); + + onResponse(global.DEFAULT_OK_RESPONSE, trades); + + return; + } + } + + logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Position not found at Executioin Context."); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + + case "Competition": { + + if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Competition Mode Detected."); } + exchangeAPI.getExecutedTrades(pPositionId, onResponse); + return; + } + + default: { + logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Unexpected bot.runMode."); + logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> bot.runMode = " + bot.runMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } } function onResponse(err, pTrades) { @@ -839,22 +926,40 @@ /* All validations passed, we proceed. */ - if (bot.backTestingMode === true) { + switch (bot.runMode) { - if (pRate !== marketRate) { + case "Live": { - logger.write("[ERROR] putPosition -> Input Validations -> putPosition Rate can not be different to marketRate while in Backtesting Mode. "); - onResponse(global.DEFAULT_FAIL_RESPONSE, positionId); + exchangeAPI.putPosition(global.MARKET, pType, pRate, pAmountA, pAmountB, onResponse); + return; } - let positionId = Math.trunc(Math.random(1) * 1000000); - if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } - onResponse(global.DEFAULT_OK_RESPONSE, positionId); + case "Backtest": { - } else { + if (pRate !== marketRate) { - exchangeAPI.putPosition(global.MARKET, pType, pRate, pAmountA, pAmountB, onResponse); + logger.write("[ERROR] putPosition -> Input Validations -> putPosition Rate can not be different to marketRate while in Backtesting Mode. "); + onResponse(global.DEFAULT_FAIL_RESPONSE, positionId); + } + let positionId = Math.trunc(Math.random(1) * 1000000); + if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } + onResponse(global.DEFAULT_OK_RESPONSE, positionId); + return; + } + + case "Competition": { + + exchangeAPI.putPosition(global.MARKET, pType, pRate, pAmountA, pAmountB, onResponse); + return; + } + + default: { + logger.write("[ERROR] putPosition -> Unexpected bot.runMode."); + logger.write("[ERROR] putPosition -> bot.runMode = " + bot.runMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } } function onResponse(err, pPositionId) { @@ -938,16 +1043,34 @@ if (FULL_LOG === true) { logger.write("[INFO] movePosition -> pPosition = " + JSON.stringify(pPosition)); } if (FULL_LOG === true) { logger.write("[INFO] movePosition -> pNewRate = " + pNewRate); } - if (bot.backTestingMode === true) { + switch (bot.runMode) { - let positionId = Math.trunc(Math.random(1) * 1000000); - if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } - onResponse(global.DEFAULT_OK_RESPONSE, positionId); + case "Live": { - } else { + exchangeAPI.movePosition(pPosition, pNewRate, onResponse); + return; + } - exchangeAPI.movePosition(pPosition, pNewRate, onResponse); + case "Backtest": { + let positionId = Math.trunc(Math.random(1) * 1000000); + if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } + onResponse(global.DEFAULT_OK_RESPONSE, positionId); + return; + } + + case "Competition": { + + exchangeAPI.movePosition(pPosition, pNewRate, onResponse); + return; + } + + default: { + logger.write("[ERROR] movePosition -> Unexpected bot.runMode."); + logger.write("[ERROR] movePosition -> bot.runMode = " + bot.runMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } } function onResponse(err, pPositionId) { diff --git a/Context.js b/Context.js index c462c30f9..967cb7e23 100644 --- a/Context.js +++ b/Context.js @@ -78,6 +78,8 @@ let statusDependencies; + let runIndex; // This is the index for this run and depends on the runMode. + return thisObject; function initialize(pStatusDependencies, callBackFunction) { @@ -166,6 +168,34 @@ } else { + switch (bot.runMode) { + + case "Live": { + + runIndex = thisObject.statusReport.liveRuns.length - 1; + break; + } + + case "Backtest": { + + runIndex = thisObject.statusReport.backtestRuns.length - 1; + break; + } + + case "Competition": { + + runIndex = thisObject.statusReport.competitionRuns.length - 1; + break; + } + + default: { + logger.write("[ERROR] initialize -> createConext -> Unexpected bot.runMode."); + logger.write("[ERROR] initialize -> createConext -> bot.runMode = " + bot.runMode); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } + } + getExecutionHistory(callBack); } @@ -182,7 +212,7 @@ if (FULL_LOG === true) { logger.write("[INFO] initialize -> getExecutionHistory -> Entering function."); } - let fileName = "Execution.History." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex + ".json"; + let fileName = "Execution.History." + bot.runMode + "." + runIndex + ".json"; let filePath = bot.filePathRoot + "/Output/" + bot.process; if (FULL_LOG === true) { logger.write("[INFO] initialize -> getExecutionHistory -> fileName = " + fileName); } @@ -208,9 +238,12 @@ /* Move some values to the new record, in case there are no transactions that re-calculate them. */ - thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[thisObject.executionHistory.length - 1][1]; + thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[thisObject.executionHistory.length-1][1]; thisObject.newHistoryRecord.sellAvgRate = thisObject.executionHistory[thisObject.executionHistory.length - 1][2]; + thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[1]; + thisObject.newHistoryRecord.sellAvgRate = thisObject.executionHistory[2]; + getExecutionContext(callBack); } catch (err) { @@ -241,25 +274,37 @@ if (FULL_LOG === true) { logger.write("[INFO] initialize -> getExecutionContext -> Entering function."); } let date; - let runType = thisObject.statusReport.runType; - switch (runType) { + switch (bot.runMode) { - case "Backtest": { - date = new Date(thisObject.statusReport.backtestRuns[thisObject.statusReport.runIndex].lastExecution); - thisObject.statusReport.backtestRuns[thisObject.statusReport.runIndex].lastExecution = bot.processDatetime; + case "Live": { + date = new Date(thisObject.statusReport.liveRuns[runIndex].lastExecution); + thisObject.statusReport.liveRuns[runIndex].lastExecution = bot.processDatetime; + thisObject.statusReport.liveRuns[runIndex].endDatetime = bot.processDatetime; break; } - case "Live": { - date = new Date(thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].lastExecution); - thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].lastExecution = bot.processDatetime; - thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].endDatetime = bot.processDatetime; + case "Backtest": { + date = new Date(thisObject.statusReport.backtestRuns[runIndex].lastExecution); + thisObject.statusReport.backtestRuns[runIndex].lastExecution = bot.processDatetime; break; } + + case "Competition": { + date = new Date(thisObject.statusReport.competitionRuns[runIndex].lastExecution); + thisObject.statusReport.competitionRuns[runIndex].lastExecution = bot.processDatetime; + break; + } + + default: { + logger.write("[ERROR] initialize -> getExecutionContext -> Unexpected bot.runMode."); + logger.write("[ERROR] initialize -> getExecutionContext -> bot.runMode = " + bot.runMode); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } } - let fileName = "Execution.Context." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex + ".json"; + let fileName = "Execution.Context." + bot.runMode + "." + runIndex + ".json"; let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2); let filePath = bot.filePathRoot + "/Output/" + bot.process + '/' + dateForPath; @@ -327,43 +372,18 @@ */ - let runType; - - if (bot.backTestingMode === true) { - runType = "Backtest"; - } else { - runType = "Live"; - } - /* Here, we dont know if the Status Report was ever created or not. To test that we do this. */ - if (thisObject.statusReport.runType === undefined) { // This means that the Status Report does not exist. + if (thisObject.statusReport.liveRuns === undefined) { // This means that the Status Report does not exist. thisObject.statusReport = { - runType: runType, - runIndex: 0, liveRuns: [], - backtestRuns: [] + backtestRuns: [], + competitionRuns: [] }; - } - thisObject.statusReport.runType = runType; - - switch (runType) { - - case "Backtest": { - - let runContent = { - beginDatetime: (new Date(bot.timePeriod.beginDatetime)).valueOf(), - endDatetime: (new Date(bot.timePeriod.endDatetime)).valueOf(), - lastExecution: bot.processDatetime - }; - - thisObject.statusReport.backtestRuns.push(runContent); - thisObject.statusReport.runIndex = thisObject.statusReport.backtestRuns.length - 1; - break; - } + switch (bot.runMode) { case "Live": { @@ -374,9 +394,42 @@ }; thisObject.statusReport.liveRuns.push(runContent); - thisObject.statusReport.runIndex = thisObject.statusReport.liveRuns.length - 1; + runIndex = thisObject.statusReport.liveRuns.length - 1; break; } + + case "Backtest": { + + let runContent = { + beginDatetime: (new Date(bot.backtest.beginDatetime)).valueOf(), + endDatetime: (new Date(bot.backtest.endDatetime)).valueOf(), + lastExecution: bot.processDatetime + }; + + thisObject.statusReport.backtestRuns.push(runContent); + runIndex = thisObject.statusReport.backtestRuns.length - 1; + break; + } + + case "Competition": { + + let runContent = { + beginDatetime: (new Date(bot.competition.beginDatetime)).valueOf(), + endDatetime: (new Date(bot.competition.endDatetime)).valueOf(), + lastExecution: bot.processDatetime + }; + + thisObject.statusReport.competitionRuns.push(runContent); + runIndex = thisObject.statusReport.competitionRuns.length - 1; + break; + } + + default: { + logger.write("[ERROR] initialize -> createConext -> Unexpected bot.runMode."); + logger.write("[ERROR] initialize -> createConext -> bot.runMode = " + bot.runMode); + callBack(global.DEFAULT_FAIL_RESPONSE); + return; + } } thisObject.executionHistory = []; @@ -469,7 +522,7 @@ if (FULL_LOG === true) { logger.write("[INFO] saveThemAll -> writeExecutionContext -> Entering function."); } - let fileName = "Execution.Context." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex +".json"; + let fileName = "Execution.Context." + bot.runMode + "." + runIndex +".json"; let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCHours(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCMinutes(), 2); let filePath = bot.filePathRoot + "/Output/" + bot.process + '/' + dateForPath; @@ -530,7 +583,7 @@ if (FULL_LOG === true) { logger.write("[INFO] saveThemAll -> writeExucutionHistory -> Entering function."); } - let fileName = "Execution.History." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex + ".json"; + let fileName = "Execution.History." + bot.runMode + "." + runIndex + ".json"; let filePath = bot.filePathRoot + "/Output/" + bot.process; if (FULL_LOG === true) { logger.write("[INFO] saveThemAll -> writeExucutionHistory -> fileName = " + fileName); } @@ -586,8 +639,8 @@ /* Here we will write the file containing the max sequence number. */ - fileContent = thisObject.statusReport.runIndex; - fileName = "Execution.History." + thisObject.statusReport.runType + "." + "Sequence" + ".json"; + fileContent = runIndex; + fileName = "Execution.History." + bot.runMode + "." + "Sequence" + ".json"; filePath = bot.filePathRoot + "/Output/" + bot.process; cloudStorage.createTextFile(filePath, fileName, fileContent + '\n', onSequenceFileCreated); diff --git a/Datasource.js b/Datasource.js deleted file mode 100644 index 8fdd9dbc0..000000000 --- a/Datasource.js +++ /dev/null @@ -1,526 +0,0 @@ -exports.newDatasource = function newDatasource(BOT, DEBUG_MODULE, BLOB_STORAGE, UTILITIES) { - - const FULL_LOG = true; - - /* - - This module allows trading bots to access information stored at the cloud storage. - - */ - - const MODULE_NAME = "Datasource"; - - let thisObject = { - initialize: initialize, - candlesFiles: new Map, // Complete sets of candles for different Time Periods. For Time Periods < 1hs sets are of current day only, otherwise whole market. - candlesMap: new Map, // The last 10 candles for each Time Period will be stored here. - stairsFiles: new Map, // Complete sets of patterns for different Time Periods. For Time Periods < 1hs sets are of current day only, otherwise whole market. - stairsMap: new Map // The patterns we are currently in will be stored here. - }; - - let bot = BOT; - - const logger = DEBUG_MODULE.newDebugLog(); - logger.fileName = MODULE_NAME; - logger.bot = bot; - - let oliviaStorage = BLOB_STORAGE.newBlobStorage(bot); - let tomStorage = BLOB_STORAGE.newBlobStorage(bot); - - /* Utilities needed. */ - - let utilities = UTILITIES.newUtilities(bot); - - return thisObject; - - function initialize(callBackFunction) { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - initializeOliviaStorage(); - - function initializeOliviaStorage() { - - if (FULL_LOG === true) { logger.write("[INFO] initializeOliviaStorage -> Entering function."); } - - oliviaStorage.initialize({ bot: "AAOlivia", devTeam: "AAMasters" }, onInizialized); - - function onInizialized(err) { - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - initializeTomStorage(); - } else { - logger.write("[ERROR] initialize -> initializeOliviaStorage -> err = " + err.message); - callBackFunction(err); - } - } - } - - function initializeTomStorage() { - - if (FULL_LOG === true) { logger.write("[INFO] initializeTomStorage -> Entering function."); } - - tomStorage.initialize({ bot: "AATom", devTeam: "AAMasters" }, onInizialized); - - function onInizialized(err) { - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - getCandles(onDone); - } else { - logger.write("[ERROR] initialize -> initializeTomStorage -> err = " + err.message); - callBackFunction(err); - } - } - } - - function onDone(err) { - try { - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { - logger.write("[INFO] initialize -> onDone -> Execution finished well. :-)"); - callBackFunction(global.DEFAULT_OK_RESPONSE); - return; - } - case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write("[ERROR] initialize -> onDone -> Retry Later. Requesting Execution Retry."); - callBackFunction(err); - return; - } - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write("[ERROR] initialize -> onDone -> Operation Failed. Aborting the process."); - callBackFunction(err); - return; - } - } - - } catch (err) { - logger.write("[ERROR] initialize -> onDone -> err = " + err.message); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getCandles(callBack) { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getCandles -> Entering function."); } - - /* - - We will read several files with candles for the current day. We will use these files as an input - to make trading decitions later. - - */ - - getMarketFiles(); - - function getMarketFiles() { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getCandles -> getMarketFiles -> Entering function."); } - - let filesOk = 0; - let filesNotOk = 0; - - for (i = 0; i < global.marketFilesPeriods.length; i++) { - - let periodTime = global.marketFilesPeriods[i][0]; - let periodName = global.marketFilesPeriods[i][1]; - - getFile(oliviaStorage, "@AssetA_@AssetB.json", "@Exchange/Output/Candles/Multi-Period-Market/@Period", periodName, undefined, onFileReceived, callBack); - - function onFileReceived(err, file) { - - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - filesOk++; - thisObject.candlesFiles.set(periodName, file); - } else { - filesNotOk++; - } - - if (filesOk + filesNotOk === global.marketFilesPeriods.length) { - - if (filesOk === global.marketFilesPeriods.length) { - - getDailyFiles(); - - } else { - logger.write("[ERROR] getCandles -> getMarketFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk); - logger.write("[ERROR] getCandles -> getMarketFiles -> onFileReceived -> Will abort the process and request a retry."); - callBack(global.DEFAULT_RETRY_RESPONSE); - return; - } - } - } - } - } catch (err) { - logger.write("[ERROR] getCandles -> getMarketFiles -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getDailyFiles() { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getCandles -> getDailyFiles -> Entering function."); } - - let filesOk = 0; - let filesNotOk = 0; - - for (i = 0; i < global.dailyFilePeriods.length; i++) { - - let periodTime = global.dailyFilePeriods[i][0]; - let periodName = global.dailyFilePeriods[i][1]; - - getFile(oliviaStorage, "@AssetA_@AssetB.json", "@Exchange/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day", periodName, bot.processDatetime, onFileReceived, callBack); - - function onFileReceived(err, file) { - - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - filesOk++; - thisObject.candlesFiles.set(periodName, file); - } else { - filesNotOk++; - } - - if (filesOk + filesNotOk === global.dailyFilePeriods.length) { - - if (filesOk === global.dailyFilePeriods.length) { - - getCandlesWeAreIn(); - - } else { - logger.write("[ERROR] getCandles -> getDailyFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk); - logger.write("[ERROR] getCandles -> getDailyFiles -> onFileReceived -> Will abort the process and request a retry."); - callBack(global.DEFAULT_RETRY_RESPONSE); - return; - } - } - } - } - } catch (err) { - logger.write("[ERROR] getCandles -> getDailyFiles -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getCandlesWeAreIn() { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getCandles -> getCandlesWeAreIn -> Entering function."); } - - let counter = 0; - - thisObject.candlesFiles.forEach(getCurrentCandles); - - function getCurrentCandles(pCandlesFile, pPeriodName, map) { - - let candlesArray = []; - - for (i = 0; i < pCandlesFile.length; i++) { - - let candle = { - open: undefined, - close: undefined, - min: 10000000000000, - max: 0, - begin: undefined, - end: undefined, - direction: undefined - }; - - candle.min = pCandlesFile[i][0]; - candle.max = pCandlesFile[i][1]; - - candle.open = pCandlesFile[i][2]; - candle.close = pCandlesFile[i][3]; - - candle.begin = pCandlesFile[i][4]; - candle.end = pCandlesFile[i][5]; - - if (candle.open > candle.close) { candle.direction = 'down'; } - if (candle.open < candle.close) { candle.direction = 'up'; } - if (candle.open === candle.close) { candle.direction = 'side'; } - - /* We are going to store the last 10 candles per period, which will give the bot a good sense of where it is. */ - - let timePeriod = candle.end - candle.begin + 1; // In miliseconds. (remember each candle spans a period minus one milisecond) - - if (candle.begin >= bot.processDatetime.valueOf() - timePeriod * 10 && candle.begin <= bot.processDatetime.valueOf()) { - - candlesArray.push(candle); - - } - } - - thisObject.candlesMap.set(pPeriodName, candlesArray); - - counter++; - - if (counter === thisObject.candlesFiles.size) { - - getPatterns(callBack); - - } - } - } catch (err) { - logger.write("[ERROR] getCandles -> getCandlesWeAreIn -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write("[ERROR] getCandles -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getPatterns(callBack) { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> Entering function."); } - - /* - - We will read several files with pattern calculations for the current day. We will use these files as an input - to make trading decitions later. - - */ - - getMarketFiles(); - - function getMarketFiles() { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> getMarketFiles -> Entering function."); } - - let filesOk = 0; - let filesNotOk = 0; - - for (i = 0; i < global.marketFilesPeriods.length; i++) { - - let periodTime = global.marketFilesPeriods[i][0]; - let periodName = global.marketFilesPeriods[i][1]; - - getFile(tomStorage, "@AssetA_@AssetB.json", "@Exchange/Tom/dataSet.V1/Output/Candle-Stairs/Multi-Period-Market/@Period", periodName, undefined, onFileReceived, callBack); - - function onFileReceived(err, file) { - - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - filesOk++; - thisObject.stairsFiles.set(periodName, file); - } else { - filesNotOk++; - } - - if (filesOk + filesNotOk === global.marketFilesPeriods.length) { - - if (filesOk === global.marketFilesPeriods.length) { - - getDailyFiles(); - - } else { - logger.write("[ERROR] getPatterns -> getMarketFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk); - logger.write("[ERROR] getPatterns -> getMarketFiles -> onFileReceived -> Will abort the process and request a retry."); - callBack(global.DEFAULT_RETRY_RESPONSE); - return; - } - } - } - } - } catch (err) { - logger.write("[ERROR] getPatterns -> getMarketFiles -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getDailyFiles() { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> getDailyFiles -> Entering function."); } - - let filesOk = 0; - let filesNotOk = 0; - - for (i = 0; i < global.dailyFilePeriods.length; i++) { - - let periodTime = global.dailyFilePeriods[i][0]; - let periodName = global.dailyFilePeriods[i][1]; - - getFile(tomStorage, "@AssetA_@AssetB.json", "@Exchange/Tom/dataSet.V1/Output/Candle-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day", periodName, bot.processDatetime, onFileReceived, callBack); - - function onFileReceived(err, file) { - - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - filesOk++; - thisObject.stairsFiles.set(periodName, file); - } else { - filesNotOk++; - } - - if (filesOk + filesNotOk === global.dailyFilePeriods.length) { - - if (filesOk === global.dailyFilePeriods.length) { - - getStairsWeAreIn(); - - } else { - logger.write("[ERROR] getPatterns -> getDailyFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk); - logger.write("[ERROR] getPatterns -> getDailyFiles -> onFileReceived -> Will abort the process and request a retry."); - callBack(global.DEFAULT_RETRY_RESPONSE); - return; - } - } - } - } - } catch (err) { - logger.write("[ERROR] getPatterns -> getDailyFiles -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getStairsWeAreIn() { - - try { - - if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> getStairsWeAreIn -> Entering function."); } - - let counter = 0; - - thisObject.stairsFiles.forEach(getCurrentStairs); - - function getCurrentStairs(pStairsFile, pPeriodName, map) { - - for (i = 0; i < pStairsFile.length; i++) { - - let stairs = { - open: undefined, - close: undefined, - min: 10000000000000, - max: 0, - begin: undefined, - end: undefined, - direction: undefined, - candleCount: 0, - firstMin: 0, - firstMax: 0, - lastMin: 0, - lastMax: 0 - }; - - stairs.open = pStairsFile[i][0]; - stairs.close = pStairsFile[i][1]; - - stairs.min = pStairsFile[i][2]; - stairs.max = pStairsFile[i][3]; - - stairs.begin = pStairsFile[i][4]; - stairs.end = pStairsFile[i][5]; - - stairs.direction = pStairsFile[i][6]; - stairs.candleCount = pStairsFile[i][7]; - - stairs.firstMin = pStairsFile[i][8]; - stairs.firstMax = pStairsFile[i][9]; - - stairs.lastMin = pStairsFile[i][10]; - stairs.lastMax = pStairsFile[i][11]; - - if (bot.processDatetime.valueOf() >= stairs.begin && bot.processDatetime.valueOf() <= stairs.end) { - - thisObject.stairsMap.set(pPeriodName, stairs); - - } - } - - counter++; - - if (counter === thisObject.stairsFiles.size) { - - callBack(global.DEFAULT_OK_RESPONSE); - - } - } - } catch (err) { - logger.write("[ERROR] getPatterns -> getStairsWeAreIn -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write("[ERROR] getPatterns -> err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getFile(pFileStorage, pFileName, pFilePath, pPeriodName, pDatetime, innerCallBack, outerCallBack) { - - try { - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> Entering function."); } - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pFileName = " + pFileName); } - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pFilePath = " + pFilePath); } - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pPeriodName = " + pPeriodName); } - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pDatetime = " + pDatetime); } - - pFileName = pFileName.replace("@AssetA", MARKET.assetA); - pFileName = pFileName.replace("@AssetB", MARKET.assetB); - - pFilePath = pFilePath.replace("@Exchange", global.EXCHANGE_NAME); - pFilePath = pFilePath.replace("@Period", pPeriodName); - - if (pDatetime !== undefined) { - - pFilePath = pFilePath.replace("@Year", pDatetime.getUTCFullYear()); - pFilePath = pFilePath.replace("@Month", utilities.pad(pDatetime.getUTCMonth() + 1, 2)); - pFilePath = pFilePath.replace("@Day", utilities.pad(pDatetime.getUTCDate(), 2)); - - } - - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> final pFilePath = " + pFilePath); } - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> final pFileName = " + pFileName); } - - pFileStorage.getTextFile(pFilePath, pFileName, onFileReceived); - - function onFileReceived(err, text) { - - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> onFileReceived -> pFilePath = " + pFilePath); } - if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> onFileReceived -> pFileName = " + pFileName); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write("[ERROR] initialize -> getFile -> onFileReceived -> err = " + err.message); - innerCallBack(err); - return; - } - - try { - - let data = JSON.parse(text); - innerCallBack(global.DEFAULT_OK_RESPONSE, data); - - } catch (err) { - logger.write("[ERROR] initialize -> getFile -> onFileReceived -> Parsing JSON -> err = " + err.message); - innerCallBack(err); - return; - } - } - - } catch (err) { - logger.write("[ERROR] initialize -> getFile -> err = " + err.message); - outerCallBack("Operation Failed"); - } - } - - } catch (err) { - - logger.write("[ERROR] initialize -> err = " + err.message); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } -}; \ No newline at end of file diff --git a/Project.njsproj b/Project.njsproj index a798d6c02..3313fa7bd 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -42,9 +42,6 @@ Code - - Code - Code diff --git a/Run.js b/Run.js index 838dd60f1..a4779b6ea 100644 --- a/Run.js +++ b/Run.js @@ -298,9 +298,8 @@ for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) { runIndicatorBot(botConfig, processConfig, month, year); break; } - case 'Trading': { - runTradingBot(botConfig, processConfig); - break; + default: { + logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type); } } } @@ -321,23 +320,23 @@ for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) { runExtractionBot(botConfig, processConfig, month, year); break; } + default: { + logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } } } } - if (processConfig.startMode.timePeriod !== undefined) { + if (processConfig.startMode.live !== undefined) { - if (processConfig.startMode.timePeriod.run === "true") { + if (processConfig.startMode.live.run === "true") { - botConfig.backTestingMode = true; - botConfig.timePeriod = processConfig.startMode.timePeriod; + botConfig.runMode = "Live"; - /* We override these waitTimes to the one specified at the timePeriod configuration. */ + let month = pad((new Date()).getUTCMonth() + 1, 2); + let year = (new Date()).getUTCFullYear(); - processConfig.normalWaitTime = processConfig.startMode.timePeriod.waitTime; - processConfig.retryWaitTime = processConfig.startMode.timePeriod.waitTime; - - if (processConfig.startMode.timePeriod.resumeExecution === "true") { + if (processConfig.startMode.live.resumeExecution === "true") { botConfig.hasTheBotJustStarted = false; } else { botConfig.hasTheBotJustStarted = true; @@ -348,6 +347,62 @@ for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) { runTradingBot(botConfig, processConfig); break; } + default: { + logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + if (processConfig.startMode.backtest !== undefined) { + + if (processConfig.startMode.backtest.run === "true") { + + botConfig.runMode = "Backtest"; + botConfig.backtest = processConfig.startMode.backtest; + + /* We override these waitTimes to the one specified at the backtest configuration. */ + + processConfig.normalWaitTime = processConfig.startMode.backtest.waitTime; + processConfig.retryWaitTime = processConfig.startMode.backtest.waitTime; + + /* Backtest Mode does not support Resume Execution, so this is the only way. */ + + botConfig.hasTheBotJustStarted = true; + + switch (botConfig.type) { + case 'Trading': { + runTradingBot(botConfig, processConfig); + break; + } + default: { + logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } + } + } + } + + if (processConfig.startMode.competition !== undefined) { + + if (processConfig.startMode.competition.run === "true") { + + botConfig.runMode = "Competition"; + botConfig.competition = processConfig.startMode.competition; + + if (processConfig.startMode.competition.resumeExecution === "true") { + botConfig.hasTheBotJustStarted = false; + } else { + botConfig.hasTheBotJustStarted = true; + } + + switch (botConfig.type) { + case 'Trading': { + runTradingBot(botConfig, processConfig); + break; + } + default: { + logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type); + } } } } diff --git a/Trading Bot Process Main Loop.js b/Trading Bot Process Main Loop.js index 4ed118198..ba40a44b7 100644 --- a/Trading Bot Process Main Loop.js +++ b/Trading Bot Process Main Loop.js @@ -68,7 +68,6 @@ const POLONIEX_CLIENT_MODULE = require(ROOT_DIR + 'Poloniex API Client'); const EXCHANGE_API = require(ROOT_DIR + 'ExchangeAPI'); const CONTEXT = require(ROOT_DIR + 'Context'); - const DATASOURCE = require(ROOT_DIR + 'Datasource'); const ASSISTANT = require(ROOT_DIR + 'Assistant'); const STATUS_REPORT = require(ROOT_DIR + 'Status Report'); const DATA_SET = require(ROOT_DIR + 'Data Set'); @@ -77,48 +76,83 @@ /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ - if (bot.backTestingMode === true) { + switch (bot.runMode) { + case 'Live': { - if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Backtesting Mode detected."); } + if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Live Mode detected."); } - if (bot.hasTheBotJustStarted === true) { + // This will be considered the process date and time, so as to have it consistenly all over the execution. - if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Bot just starting, setting initial datetime."); } + let localDate = new Date(); - bot.processDatetime = new Date(bot.timePeriod.beginDatetime); // Set the starting time as the configured beginDatetime. - - } else { - - bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time. - - let endDatetime = new Date(bot.timePeriod.endDatetime); - - if (bot.processDatetime.valueOf() > endDatetime.valueOf()) { - - if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> End of Backtesting Period reached. Exiting Bot Process Loop."); } - - callBackFunction(global.DEFAULT_OK_RESPONSE); - return; - } + bot.processDatetime = new Date(Date.UTC( + localDate.getUTCFullYear(), + localDate.getUTCMonth(), + localDate.getUTCDate(), + localDate.getUTCHours(), + localDate.getUTCMinutes(), + localDate.getUTCSeconds(), + localDate.getUTCMilliseconds())); + break; } + case 'Backtest': { - } else { // We are running Live! + if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Backtesting Mode detected."); } - if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Live Mode detected."); } + if (bot.hasTheBotJustStarted === true) { - // This will be considered the process date and time, so as to have it consistenly all over the execution. + if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Bot just starting, setting initial datetime."); } - let localDate = new Date(); + bot.processDatetime = new Date(bot.backtest.beginDatetime); // Set the starting time as the configured beginDatetime. - bot.processDatetime = new Date(Date.UTC( - localDate.getUTCFullYear(), - localDate.getUTCMonth(), - localDate.getUTCDate(), - localDate.getUTCHours(), - localDate.getUTCMinutes(), - localDate.getUTCSeconds(), - localDate.getUTCMilliseconds())); + } else { + bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time. + + let endDatetime = new Date(bot.backtest.endDatetime); + + if (bot.processDatetime.valueOf() > endDatetime.valueOf()) { + + if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> End of Backtesting Period reached. Exiting Bot Process Loop."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + } + break; + } + case 'Competition': { + + if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Competition Mode detected."); } + + if (bot.hasTheBotJustStarted === true) { + + if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Bot just starting, setting initial datetime."); } + + bot.processDatetime = new Date(bot.competition.beginDatetime); // Set the starting time as the configured beginDatetime. + + } else { + + bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time. + + let endDatetime = new Date(bot.competition.endDatetime); + + if (bot.processDatetime.valueOf() > endDatetime.valueOf()) { + + if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> End of Competition Period reached. Exiting Bot Process Loop."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + } + break; + } + default: { + logger.write("[ERROR] run -> loop -> Unexpected bot.runMode."); + logger.write("[ERROR] run -> loop -> bot.runMode = " + bot.runMode); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } } if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> bot.processDatetime = " + bot.processDatetime); }