text: ﻿exports.newTradingBotProcessMainLoop = function newTradingBotProcessMainLoop(bot, parentLogger) {
  
      const ROOT_DIR = './';
  
      const MODULE_NAME = "Trading Bot Process Main Loop";
      const FULL_LOG = true;
  
      let USER_BOT_MODULE;
      let COMMONS_MODULE;
  
      const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog');
      const FILE_STORAGE = require('./Integrations/FileStorage.js');
      let fileStorage = FILE_STORAGE.newFileStorage(parentLogger);
      let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble.
  
      let nextLoopTimeoutHandle;
      let checkLoopHealthHandle;
  
      let thisObject = {
          initialize: initialize,
          run: run
      };
  
      bot["botCache"] = new Map();
      let processConfig;
      let UI_COMMANDS;
  
      return thisObject;
  
      function initialize(pUI_COMMANDS, pProcessConfig, callBackFunction) {
  
          /*  This function is exactly the same in the 3 modules representing the 2 different bot types loops. */
  
          try {
              if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
  
              UI_COMMANDS = pUI_COMMANDS;
              processConfig = pProcessConfig;
  
              let filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name
              filePath += "/User.Bot.js"
  
              fileStorage.getTextFile(global.DEV_TEAM, filePath, onBotDownloaded);
  
              function onBotDownloaded(err, text) {
  
                  if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
  
                      parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err.message = " + err.message);
                      callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                      return;
                  }
                  USER_BOT_MODULE = {}
                  USER_BOT_MODULE.newUserBot = eval(text); // TODO This needs to be changed function
  
                  filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo;
                  filePath += "/Commons.js"
  
                  fileStorage.getTextFile(global.DEV_TEAM, filePath, onCommonsDownloaded);
  
                  function onCommonsDownloaded(err, text) {
  
                      if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
                          parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.code || err.message);
                          callBackFunction(global.DEFAULT_OK_RESPONSE);
                          return;
                      }
                      COMMONS_MODULE = {}
                      COMMONS_MODULE.newCommons = eval(text); // TODO This needs to be changed function
  
                      callBackFunction(global.DEFAULT_OK_RESPONSE);
                  }
              }
          } catch (err) {
              parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack);
              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
          }
      }
  
      function run(pGenes, callBackFunction) {
  
          let context;
  
          try {
              if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] run -> Entering function."); }
  
              bot.enableCheckLoopHealth = true;
  
              let fixedTimeLoopIntervalHandle;
  
              if (bot.runAtFixedInterval === true) {
  
                  fixedTimeLoopIntervalHandle = setInterval(loop, bot.fixedInterval);
  
              } else {
  
                  loop();
  
              }
  
              function loop() {
  
                  try {
  
                      function pad(str, max) {
                          str = str.toString();
                          return str.length < max ? pad(" " + str, max) : str;
                      }
  
                      /* For each loop we want to create a new log file. */
  
                      logger = DEBUG_MODULE.newDebugLog();
                      logger.bot = bot;
                      logger.initialize();
  
                      bot.loopCounter++;
                      bot.loopStartTime = new Date().valueOf();
  
                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Entering function."); }
  
                      /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */
  
                      const UTILITIES = require(ROOT_DIR + 'CloudUtilities');
                      const EXCHANGE_API = require('@superalgos/exchange-gateway');
                      const CONTEXT = require(ROOT_DIR + 'Context');
                      const ASSISTANT = require(ROOT_DIR + 'Assistant');
                      const STATUS_REPORT = require(ROOT_DIR + 'StatusReport');
                      const DATA_SET = require(ROOT_DIR + 'DataSet');
                      const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies');
                      const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies');
  
                      /* Waitime Variable */
  
                      let nextWaitTime;
  
                      /* 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. */
  
                      switch (bot.startMode) {
                          case 'Live': {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Live Mode detected."); }
  
                              if(process.env.WAIT_TIME !== undefined)
                                  processConfig.normalWaitTime = process.env.WAIT_TIME
  
                              // This will be considered the process date and time, so as to have it consistenly all over the execution.
  
                              let localDate = new Date();
  
                              bot.processDatetime = new Date(Date.UTC(
                                  localDate.getUTCFullYear(),
                                  localDate.getUTCMonth(),
                                  localDate.getUTCDate(),
                                  localDate.getUTCHours(),
                                  localDate.getUTCMinutes(),
                                  localDate.getUTCSeconds(),
                                  localDate.getUTCMilliseconds()));
  
                              if (UI_COMMANDS.eventHandler !== undefined) {
  
                                  UI_COMMANDS.eventHandler.raiseEvent('Bot Execution Changed Datetime', bot.processDatetime);
  
                              }
  
                              break;
                          }
                          case 'Backtest': {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Backtesting Mode detected."); }
  
                              if(process.env.WAIT_TIME !== undefined) {
                                  processConfig.normalWaitTime = JSON.parse(process.env.WAIT_TIME)
                              }
  
                              if (bot.hasTheBotJustStarted === true) {
                                  if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Setting initial datetime."); }
                                  bot.processDatetime = new Date(bot.backtest.beginDatetime); // Set the starting time as the configured beginDatetime.
                              } else {
                                  bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance time here.
                                  let endDatetime = new Date(bot.backtest.endDatetime);
  
                                  if (bot.processDatetime.valueOf() > endDatetime.valueOf()) {
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> End of Backtesting Period reached. Exiting Bot Process Loop."); }
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(global.DEFAULT_OK_RESPONSE);
                                      return;
                                  }
                              }
                              break;
                          }
                          case 'Competition': {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Competition Mode detected."); }
  
                              if(process.env.WAIT_TIME !== undefined) {
                                  processConfig.normalWaitTime = process.env.WAIT_TIME
                              }
  
                              let localDate = new Date();
  
                              bot.processDatetime = new Date(Date.UTC(
                                  localDate.getUTCFullYear(),
                                  localDate.getUTCMonth(),
                                  localDate.getUTCDate(),
                                  localDate.getUTCHours(),
                                  localDate.getUTCMinutes(),
                                  localDate.getUTCSeconds(),
                                  localDate.getUTCMilliseconds()));
  
                              if (UI_COMMANDS.eventHandler !== undefined) {
  
                                  UI_COMMANDS.eventHandler.raiseEvent('Bot Execution Changed Datetime', bot.processDatetime);
  
                              }
  
                              let beginDatetime = new Date(bot.competition.beginDatetime);
  
                              if (bot.processDatetime.valueOf() < beginDatetime.valueOf()) {
  
                                  if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Competition not started yet. Wainting for the competition to start."); }
  
                                  nextWaitTime = 'Normal';
                                  loopControl(nextWaitTime);
                                  return;
                              }
  
                              let endDatetime = new Date(bot.competition.endDatetime);
  
                              if (bot.processDatetime.valueOf() > endDatetime.valueOf()) {
  
                                  if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> End of Competition Period reached. Exiting Bot Process Loop."); }
  
                                  logger.persist();
                                  clearInterval(fixedTimeLoopIntervalHandle);
                                  clearTimeout(nextLoopTimeoutHandle);
                                  clearTimeout(checkLoopHealthHandle);
                                  bot.enableCheckLoopHealth = false;
                                  callBackFunction(global.DEFAULT_OK_RESPONSE);
                                  return;
                              }
  
                              break;
                          }
                          default: {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> Unexpected bot.startMode.");
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> bot.startMode = " + bot.startMode);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                              return;
                          }
                      }
  
                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> bot.processDatetime = " + bot.processDatetime.toISOString()); }
  
                      if (global.AT_BREAKPOINT === true) {
  
                          if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Plot Breakpoint Hit."); }
  
                      }
  
                      /* High level log entry  */
  
                      console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.devTeam, 20) + " " + pad(bot.process, 30)
                          + " " + bot.startMode + " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8) + " bot.processDatetime = " + bot.processDatetime.toISOString());
  
                      /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */
  
                      let exchangeAPI;
                      let assistant;
                      let userBot;
                      let statusDependencies;
                      let dataDependencies;
  
                      initializeStatusDependencies();
  
                      function initializeStatusDependencies() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies ->  Entering function."); }
  
                              statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES);
  
                              statusDependencies.initialize(processConfig.statusDependencies, undefined, undefined, onInizialized);
  
                              function onInizialized(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies ->  onInizialized -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well."); }
                                              initializeDataDependencies();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Retry Later. Requesting Execution Retry.");
                                              nextWaitTime = 'Retry';
                                              loopControl(nextWaitTime);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies ->  onInizialized -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      function initializeDataDependencies() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies ->  Entering function."); }
  
                              dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET);
  
                              dataDependencies.initialize(processConfig.dataDependencies, onInizialized);
  
                              function onInizialized(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies ->  onInizialized -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); }
                                              initializeContext();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Retry Later. Requesting Execution Retry.");
                                              nextWaitTime = 'Retry';
                                              loopControl(nextWaitTime);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies ->  onInizialized -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      function initializeContext() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext ->  Entering function."); }
  
                              context = CONTEXT.newContext(bot, logger, UTILITIES);
                              context.initialize(statusDependencies, onInizialized);
  
                              function onInizialized(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext ->  onInizialized -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> Execution finished well."); }
                                              initializeExchangeAPI();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Retry Later. Requesting Execution Retry.");
                                              nextWaitTime = 'Retry';
                                              loopControl(nextWaitTime);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext ->  onInizialized -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      function initializeExchangeAPI() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI ->  Entering function."); }
  
                              exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, global.EXCHANGE_NAME);
  
                              exchangeAPI.initialize(onInizialized);
  
                              function onInizialized(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext ->  onInizialized -> onInizialized -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> onInizialized -> Execution finished well."); }
                                              initializeAssistant();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Retry Later. Requesting Execution Retry.");
                                              nextWaitTime = 'Retry';
                                              loopControl(nextWaitTime);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext ->  onInizialized -> onInizialized -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      function initializeAssistant() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant ->  Entering function."); }
  
                              assistant = ASSISTANT.newAssistant(bot, logger, UTILITIES);
                              assistant.initialize(context, exchangeAPI, dataDependencies, onInizialized);
  
                              function onInizialized(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> onInizialized -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> onInizialized -> Execution finished well."); }
                                              initializeUserBot();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Retry Later. Requesting Execution Retry.");
                                              nextWaitTime = 'Retry';
                                              loopControl(nextWaitTime);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      function initializeUserBot() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot ->  Entering function."); }
  
                              usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE);
  
                              usertBot.initialize(assistant, pGenes, onInizialized);
  
                              function onInizialized(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeUserBot -> onInizialized -> Execution finished well."); }
                                              startUserBot();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Retry Later. Requesting Execution Retry.");
                                              nextWaitTime = 'Retry';
                                              loopControl(nextWaitTime);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> onInizialized -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeUserBot -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      function startUserBot() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot ->  Entering function."); }
  
                              usertBot.start(onFinished);
  
                              function onFinished(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Execution finished well."); }
                                              saveContext();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Retry Later. Requesting Execution Retry.");
                                              nextWaitTime = 'Retry';
                                              loopControl(nextWaitTime);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> onFinished -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> startUserBot -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      function saveContext() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext ->  Entering function."); }
  
                              context.saveThemAll(onFinished);
  
                              function onFinished(err) {
  
                                  try {
  
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> onFinished -> Entering function."); }
  
                                      switch (err.result) {
                                          case global.DEFAULT_OK_RESPONSE.result: {
                                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> onFinished -> Execution finished well."); }
                                              nextWaitTime = 'Normal';
                                              updateClonesModule();
                                              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(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Can not retry at this point.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                          case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Operation Failed. Aborting the process.");
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(err);
                                              return;
                                          }
                                          default: {
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
                                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Unhandled err.result received. -> err.message = " + err.message);
  
                                              logger.persist();
                                              clearInterval(fixedTimeLoopIntervalHandle);
                                              clearTimeout(nextLoopTimeoutHandle);
                                              clearTimeout(checkLoopHealthHandle);
                                              bot.enableCheckLoopHealth = false;
                                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                                              return;
                                          }
                                      }
  
                                  } catch (err) {
                                      logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> err = "+ err.stack);
                                      logger.persist();
                                      clearInterval(fixedTimeLoopIntervalHandle);
                                      clearTimeout(nextLoopTimeoutHandle);
                                      clearTimeout(checkLoopHealthHandle);
                                      bot.enableCheckLoopHealth = false;
                                      callBackFunction(err);
                                  }
                              }
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> err = "+ err.stack);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(err);
                          }
                      }
  
                      async function updateClonesModule() {
  
                          try {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> updateClonesModule ->  Entering function."); }
  
                              nextWaitTime = 'Normal';
                              loopControl(nextWaitTime);
  
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> updateClonesModule -> err = "+ err.stack);
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> updateClonesModule -> Execution will continue anyways. ");
                              nextWaitTime = 'Normal';
                              loopControl(nextWaitTime);
                          }
                      }
  
                      function loopControl(nextWaitTime) {
  
                          if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); }
                          if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> nextWaitTime = " + nextWaitTime); }
  
                          /* Here we check if we must stop the loop gracefully. */
  
                          shallWeStop(onStop, onContinue);
  
                          function onStop() {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Entering function."); }
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Stopping the Loop Gracefully. See you next time!"); }
  
                              if (global.FULL_LOG === 'true' || bot.startMode === 'Competition') {
                                  logger.persist();
                              }
  
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(global.DEFAULT_OK_RESPONSE);
                              return;
  
                          }
  
                          function onContinue() {
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onContinue -> Entering function."); }
  
                              /* Trading bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */
  
                              switch (nextWaitTime) {
                                  case 'Normal': {
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Normal -> Restarting Loop in " + (processConfig.normalWaitTime / 1000) + " seconds."); }
                                      checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.normalWaitTime * 5, bot.loopCounter);
                                      nextLoopTimeoutHandle = setTimeout(loop, processConfig.normalWaitTime);
                                      if (global.FULL_LOG === 'true' || bot.startMode === 'Competition') {
                                          logger.persist();
                                      }
                                  }
                                      break;
                                  case 'Retry': {
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Retry -> Restarting Loop in " + (processConfig.retryWaitTime / 1000) + " seconds."); }
                                      checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.retryWaitTime * 5, bot.loopCounter);
                                      nextLoopTimeoutHandle = setTimeout(loop, processConfig.retryWaitTime);
                                      logger.persist();
                                  }
                                      break;
                                  case 'Sleep': {
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Sleep -> Restarting Loop in " + (processConfig.sleepWaitTime / 60000) + " minutes."); }
                                      nextLoopTimeoutHandle = setTimeout(loop, processConfig.sleepWaitTime);
                                      logger.persist();
                                  }
                                      break;
                                  case 'Coma': {
                                      if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Coma -> Restarting Loop in " + (processConfig.comaWaitTime / 3600000) + " hours."); }
                                      nextLoopTimeoutHandle = setTimeout(loop, processConfig.comaWaitTime);
                                      logger.persist();
                                  }
                                      break;
                              }
                          }
                      }
  
                      function checkLoopHealth(pLastLoop) {
  
                          if (bot.startMode === 'Backtest') { return; }
  
                          if (bot.enableCheckLoopHealth === false) {
  
                              logger.write(MODULE_NAME, "[WARN] run -> loop -> checkLoopHealth -> bot.enableCheckLoopHealth = " + bot.enableCheckLoopHealth);
  
                              return;
                          } // This gets disabled anytime the Main Loop is shut down by any condition.
  
                          if (bot.loopCounter <= pLastLoop + 1) {    // This means that the next loop started but also stopped executing abruptally.
  
                              let now = new Date().valueOf();
  
                              if (now - bot.loopStartTime > processConfig.normalWaitTime) {
  
                                  logger.write(MODULE_NAME, "[ERROR] run -> loop -> checkLoopHealth -> Dead loop found -> pLastLoop = " + pLastLoop);
                                  console.log((new Date().toISOString() + " [ERROR] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.instance, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found dead. Resurrecting it now."));
  
                                  logger.persist();                       // We persist the logs of the failed execution.
                                  clearTimeout(nextLoopTimeoutHandle);            // We cancel the timeout in case the original loop was still running and schedulled to reexecute.
                                  loop();                                 // We restart the loop so that the processing can continue.
  
                              } else {
  
                                  console.log((new Date().toISOString() + " [WARN] run -> loop -> checkLoopHealth -> " + pad(bot.codeName, 20) + " " + pad(bot.instance, 20) + " " + pad(bot.process, 30) + " Loop # " + pad(Number(bot.loopCounter), 5) + " found delayed but still alive. No action taken."));
  
                              }
                          }
                      }
  
                      function shallWeStop(stopCallBack, continueCallBack) {
  
                          try {
  
                              /* IMPORTANT: This function is exactly the same on the 3 modules. */
  
                              if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> shallWeStop -> Entering function. "); }
  
                              let stop = false
                              if (process.env.STOP_GRACEFULLY !== undefined)
                                  stop = JSON.parse(process.env.STOP_GRACEFULLY)
  
                              if (!stop && global.SHALL_BOT_STOP === false) {
                                  continueCallBack();
                              } else {
                                  stopCallBack();
                              }
                          } catch (err) {
                              logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> err.message = " + err.message);
                              logger.persist();
                              clearInterval(fixedTimeLoopIntervalHandle);
                              clearTimeout(nextLoopTimeoutHandle);
                              clearTimeout(checkLoopHealthHandle);
                              bot.enableCheckLoopHealth = false;
                              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
                              return;
                          }
                      }
  
                  } catch (err) {
                      parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = " + err.stack);
                      clearInterval(fixedTimeLoopIntervalHandle);
                      clearTimeout(nextLoopTimeoutHandle);
                      clearTimeout(checkLoopHealthHandle);
                      bot.enableCheckLoopHealth = false;
                      callBackFunction(err);
                  }
              }
  
          }
  
          catch (err) {
              parentLogger.write(MODULE_NAME, "[ERROR] run -> err = "+ err.stack);
              clearInterval(fixedTimeLoopIntervalHandle);
              clearTimeout(nextLoopTimeoutHandle);
              clearTimeout(checkLoopHealthHandle);
              bot.enableCheckLoopHealth = false;
              callBackFunction(global.DEFAULT_FAIL_RESPONSE);
          }
      }
  };
  