diff --git a/Commons.js b/Commons.js
new file mode 100644
index 000000000..d9f61ce44
--- /dev/null
+++ b/Commons.js
@@ -0,0 +1,515 @@
+exports.newCommons = function newCommons(bot, logger, UTILITIES, FILE_STORAGE) {
+
+ const FULL_LOG = true;
+ const LOG_FILE_CONTENT = false;
+ const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
+
+ const MODULE_NAME = "Commons";
+
+ let thisObject = {
+ validateDataDependencies: validateDataDependencies,
+ validateOutputDatasets: validateOutputDatasets,
+ inflateDatafiles: inflateDatafiles,
+ dataBuildingProcedure: dataBuildingProcedure,
+ calculationsProcedure: calculationsProcedure,
+ generateFileContent: generateFileContent,
+ writeFile: writeFile
+ };
+
+ let utilities = UTILITIES.newCloudUtilities(logger);
+ let fileStorage = FILE_STORAGE.newFileStorage(logger);
+
+ return thisObject;
+
+ function validateDataDependencies(dataDependencies, callBackFunction) {
+ for (let i = 0; i < dataDependencies.length; i++) {
+
+ let dataDependencyNode = dataDependencies[i]
+
+ /* Basic validations to see if we have everything we need. */
+ if (dataDependencyNode.referenceParent.parentNode.code.singularVariableName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (dataDependencyNode.referenceParent.parentNode.code.pluralVariableName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (dataDependencyNode.referenceParent.parentNode.record === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ }
+ return true
+ }
+
+ function validateOutputDatasets(outputDatasets, callBackFunction) {
+ for (let i = 0; i < outputDatasets.length; i++) {
+ let outputDatasetNode = outputDatasets[i]
+
+ if (outputDatasetNode.referenceParent === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Output Dataset without Reference Parent. Output Dataset = " + JSON.stringify(outputDatasetNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (outputDatasetNode.referenceParent.parentNode.code.singularVariableName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (outputDatasetNode.referenceParent.parentNode.code.pluralVariableName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (outputDatasetNode.referenceParent.parentNode.record === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (outputDatasetNode.referenceParent.parentNode.dataBuilding === undefined) {
+ logger.write(MODULE_NAME, "[WARN] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
+ }
+ if (outputDatasetNode.referenceParent.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(outputDatasetNode.referenceParent));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (outputDatasetNode.referenceParent.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(outputDatasetNode.referenceParent));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (outputDatasetNode.referenceParent.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (outputDatasetNode.referenceParent.parentNode.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Bot witn no codeName defined. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Team. Bot = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] start -> Team witn no codeName defined. Team = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ }
+ return true
+ }
+
+ function inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod) {
+ /*
+ For each dataDependencyNode in our data dependencies, we should have a dataFile containing the records needed as an imput for this process.
+ What we need to do first is transform those records into JSON objects that can be used by user-defined formulas.
+ The first step does that but with the not calculated properties, the second step adds the calculated properties.
+ */
+ for (let i = 0; i < dataDependencies.length; i++) {
+
+ let dataFile
+ let jsonData // Datafile converted into Json objects
+ let inputData // Includes calculated properties
+ let singularVariableName // name of the variable for this product
+ let recordDefinition
+
+ let dataDependencyNode = dataDependencies[i]
+ dataFile = dataFiles.get(dataDependencyNode.id)
+
+ if (dataFile === undefined) { continue } // When a datafile is not found it might be because we are processing market or daily and at the dependency array there are both types mixed up.
+
+ recordDefinition = dataDependencyNode.referenceParent.parentNode.record
+ singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName
+ /* Transform the raw data into JSON objects */
+ jsonData = jsonifyDataFile(dataFile, recordDefinition)
+ /* Add the calculated properties */
+ if (dataDependencyNode.referenceParent.parentNode.calculations !== undefined) {
+ inputData = calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod)
+ } else {
+ inputData = jsonData
+ }
+ products[dataDependencyNode.referenceParent.parentNode.code.pluralVariableName] = inputData
+
+ /* The main dependency is defined as the first dependency processed. */
+ if (mainDependency.records === undefined) {
+ mainDependency.records = inputData
+ }
+ }
+ }
+
+ function jsonifyDataFile(dataFile, recordDefinition) {
+
+ /*
+ This function has as an input the raw data on files and creates with it an array of JSON objects
+ with not calculated properties for later being consumed by Formulas
+ */
+
+ let jsonifiedArray = []
+ let previous;
+
+ for (let i = 0; i < dataFile.length; i++) {
+
+ let record = {}
+ for (let j = 0; j < recordDefinition.properties.length; j++) {
+ let property = recordDefinition.properties[j]
+ if (property.code.isCalculated !== true) {
+ record[property.code.codeName] = dataFile[i][j]
+ }
+ }
+
+ record.previous = previous;
+ jsonifiedArray.push(record);
+ previous = record;
+ }
+
+ return jsonifiedArray
+ }
+
+ function calculationsProcedure(jsonArray, recordDefinition, calculationsProcedure, variableName, timePeriod) {
+
+ /*
+ This function has as an input an array of JSON objects, and it adds calculated properties to
+ complete the set of properties that will be available for Formulas.
+ */
+
+ let system = { // These are the available system variables to be used in User Code and Formulas
+ timePeriod: timePeriod,
+ ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS
+ }
+ let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas.
+ let results = []
+
+ /* This is Initialization Code */
+ if (calculationsProcedure.initialization !== undefined) {
+ if (calculationsProcedure.initialization.code !== undefined) {
+ try {
+ eval(calculationsProcedure.initialization.code.code)
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = " + err.stack)
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = " + calculationsProcedure.initialization.code.code);
+ throw ("Error Executing User Code.")
+ }
+ }
+ }
+
+ /* This is Initialization Code */
+ if (calculationsProcedure.loop !== undefined) {
+ if (calculationsProcedure.loop.code !== undefined) {
+ for (let index = 0; index < jsonArray.length; index++) {
+
+ let product = {}
+ product[variableName] = jsonArray[index]
+
+ /* This is Loop Code */
+ try {
+ eval(calculationsProcedure.loop.code.code)
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = " + err.stack)
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product))
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = " + calculationsProcedure.loop.code.code);
+ throw ("Error Executing User Code.")
+ }
+
+ /* For each calculated property we apply its formula*/
+ for (let j = 0; j < recordDefinition.properties.length; j++) {
+ let property = recordDefinition.properties[j]
+ if (property.code.isCalculated === true) {
+ if (property.formula !== undefined) {
+ if (property.formula.code !== undefined) {
+ try {
+ let newValue = eval(property.formula.code)
+ let currentRecord = product[variableName]
+ currentRecord[property.code.codeName] = newValue
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack)
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product))
+ logger.write(MODULE_NAME, "[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code);
+ throw ("Error Executing User Code.")
+ }
+ }
+ }
+ }
+ }
+
+ /* Adding the new element to the resulting array */
+ results.push(product[variableName]);
+ }
+ }
+ }
+ return results
+ }
+
+ function dataBuildingProcedure(
+ products,
+ mainDependency,
+ recordDefinition,
+ dataBuildingProcedure,
+ variableName,
+ timePeriod,
+ timePeriodLabel,
+ resultsWithIrregularPeriods,
+ interExecutionMemory,
+ processingDailyFiles,
+ currentDay
+ ) {
+
+ /*
+ This function has as an input the products object, with all the information
+ of all products calculated so far by the process. Based on that information
+ the function will evaluate user supplied code and formulas in order to build
+ a new set of information.
+ */
+
+ let lastInstantOfTheDay
+ let yesterday = {}
+ let system = { // These are the available system variables to be used in User Code and Formulas
+ timePeriod: timePeriod,
+ ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS
+ }
+ let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas.
+ let results = []
+
+ /* This is Initialization Code */
+ if (dataBuildingProcedure.initialization !== undefined) {
+ if (dataBuildingProcedure.initialization.code !== undefined) {
+ try {
+ eval(dataBuildingProcedure.initialization.code.code)
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Error = " + err.stack)
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> initialization -> Error executing User Code. Code = " + dataBuildingProcedure.initialization.code.code);
+ throw ("Error Executing User Code.")
+ }
+ }
+ }
+
+ if (processingDailyFiles) {
+ /* Initialization of Last Instance */
+ lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1;
+
+ if (interExecutionMemory.variable === undefined) {
+ /* The first time the intialization variables goes to the Inter Execution Memory. */
+ interExecutionMemory.variable = JSON.parse(JSON.stringify(variable))
+ }
+ else {
+ /* We override the initialization, since the valid stuff is already at the Inter Execution Memory */
+ variable = JSON.parse(JSON.stringify(interExecutionMemory.variable))
+ }
+ }
+
+ /* This is Initialization Code */
+ if (dataBuildingProcedure.loop !== undefined) {
+ if (dataBuildingProcedure.loop.code !== undefined) {
+ let lastRecord
+ for (let index = 0; index < mainDependency.records.length; index++) {
+
+ let record = {
+ previous: mainDependency.records[index - 1],
+ current: mainDependency.records[index],
+ next: mainDependency.records[index + 1]
+ }
+ lastRecord = record
+ let product = {}
+ product[variableName] = {}
+
+ /* Here is how we know if we are processing Yesterday. */
+ let positionedAtYesterday = false
+ if (processingDailyFiles) {
+ positionedAtYesterday = (record.current.end < currentDay.valueOf())
+ }
+
+ /* This is Loop Code */
+ try {
+ eval(dataBuildingProcedure.loop.code.code)
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Error = " + err.stack)
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. product = " + JSON.stringify(product))
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> Error executing User Code. Code = " + dataBuildingProcedure.loop.code.code);
+ throw ("Error Executing User Code.")
+ }
+
+ /* For each non-calculated property we apply its formula*/
+ for (let j = 0; j < recordDefinition.properties.length; j++) {
+ let property = recordDefinition.properties[j]
+ if (property.code.isCalculated !== true) {
+ if (property.formula !== undefined) {
+ if (property.formula.code !== undefined) {
+ try {
+ let newValue = eval(property.formula.code)
+ let currentRecord = product[variableName]
+ currentRecord[property.code.codeName] = newValue
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Error = " + err.stack)
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. product = " + JSON.stringify(product))
+ logger.write(MODULE_NAME, "[ERROR] dataBuildingProcedure -> loop -> formula -> Error executing User Code. Code = " + property.formula.code);
+ throw ("Error Executing User Code.")
+ }
+ }
+ }
+ }
+ }
+
+ /* If within the code the new data structure was not yet pushed to the results array, we do it */
+ if (resultsWithIrregularPeriods !== true) {
+ /* Adding the new element to the resulting array */
+ results.push(product[variableName]);
+ }
+
+ /* While we are positioned at Yesterday, we keey updating this data structure. */
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.variable = JSON.parse(JSON.stringify(variable))
+ }
+ }
+ }
+ /*
+ Before returning we need to see if we have to record some of our counters at the interExecutionMemory.
+ To do that, the condition to be met is that this execution must include all candles of the current day.
+ */
+ if (processingDailyFiles) {
+ if (lastRecord.current.end === lastInstantOfTheDay && yesterday.variable !== undefined) {
+ interExecutionMemory.variable = JSON.parse(JSON.stringify(yesterday.variable))
+ }
+ }
+ }
+ }
+ return results
+ }
+
+ function generateFileContent(records, recordDefinition, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> generateFileContent -> Entering function."); }
+
+ let fileContent = "";
+ let recordSeparator = "";
+
+ for (let i = 0; i < records.length; i++) {
+ let record = records[i];
+
+ if (processingDailyFiles === true) {
+
+ if (resultsWithIrregularPeriods === true) {
+ /*
+ Here we have an special problem that occurs when an object spans several time peridos. If not taken care of
+ it can happen that the object gets splitted between 2 days, which we dont want since it would loose some of
+ its properties.
+
+ To solve this issue, we wont save objects which ends at the last candle of the day, because we will save it
+ at the next day, in whole, even if it starts in the previous day.
+ */
+
+ let lastInstantOdDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1;
+
+ if (record.end < currentDay.valueOf() - 1) { continue; }
+ if (record.end === lastInstantOdDay) { continue; }
+ } else {
+ /*
+ The normal situation is that objects are respecting the time size of its main dependency, in which case we only
+ care of not adding objects of the previous day we are currently processing (while on daily files)
+ */
+ if (record.end < currentDay.valueOf()) { continue; }
+ }
+ }
+
+ fileContent = fileContent + recordSeparator + '['
+ let propertySeparator = ""
+ for (let j = 0; j < recordDefinition.properties.length; j++) {
+ let property = recordDefinition.properties[j]
+ if (property.code.isCalculated !== true) {
+ fileContent = fileContent + propertySeparator
+ if (property.code.isString === true) {
+ fileContent = fileContent + '"'
+ }
+ fileContent = fileContent + record[property.code.codeName]
+ if (property.code.isString === true) {
+ fileContent = fileContent + '"'
+ }
+ if (propertySeparator === "") { propertySeparator = ","; }
+ }
+ }
+ fileContent = fileContent + ']'
+ if (recordSeparator === "") { recordSeparator = ","; }
+ }
+ fileContent = "[" + fileContent + "]";
+ return fileContent
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> generateFileContent -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, currentDay, callBackFunction) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> Entering function."); }
+
+ let market = global.MARKET;
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+ let dateForPath = ''
+
+ if (processingDailyFiles === true) {
+ dateForPath = "/" + currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
+ }
+
+ let filePathRoot = contextSummary.devTeam + "/" + contextSummary.bot + "." + contextSummary.botVersion.major + "." + contextSummary.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + contextSummary.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + contextSummary.product + "/" + contextSummary.dataset + "/" + timePeriodLabel + dateForPath;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(contextSummary.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ anotherFileWritten();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+}
+
diff --git a/DataDependencies.js b/DataDependencies.js
index 0be8aa16e..ec7bbbb95 100644
--- a/DataDependencies.js
+++ b/DataDependencies.js
@@ -2,11 +2,12 @@
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
-
const MODULE_NAME = "Data Dependencies";
+ let bot = BOT
+
let thisObject = {
- config: undefined,
+ nodeArray: undefined,
dataSets: new Map(),
initialize: initialize,
keys: []
@@ -15,15 +16,28 @@
return thisObject;
- function initialize(pDataDependenciesConfig, callBackFunction) {
+ function initialize(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
- thisObject.config = pDataDependenciesConfig;
+ /* Basic Valdidations */
+ if (bot.processNode.referenceParent.processDependencies !== undefined) {
+ if (bot.processNode.referenceParent.processDependencies.dataDependencies !== undefined) {
+ thisObject.nodeArray = bot.processNode.referenceParent.processDependencies.dataDependencies
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have data dependencies at all.");
+ callBackFunction(global.DEFAULT_OK_RESPONSE)
+ return
+ }
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not data dependencies.");
+ callBackFunction(global.DEFAULT_OK_RESPONSE)
+ return
+ }
- if (thisObject.config === undefined) {
+ if (thisObject.nodeArray.length === 0) {
// We allow old indicators not to declare their data dependencies.
@@ -32,17 +46,17 @@
}
/*
- For each dependency declared at the bot config, we will initialize a DataSet as part of this initialization process.
+ For each dependency declared at the nodeArray, we will initialize a DataSet as part of this initialization process.
*/
let alreadyCalledBack = false;
let addCount = 0;
- for (let i = 0; i < thisObject.config.length; i++) {
+ for (let i = 0; i < thisObject.nodeArray.length; i++) {
let dataSetModule = DATA_SET.newDataSet(BOT, logger);
- dataSetModule.initialize(thisObject.config[i], onInitilized);
+ dataSetModule.initialize(thisObject.nodeArray[i], onInitilized);
function onInitilized(err) {
@@ -65,14 +79,14 @@
let key;
- key = thisObject.config[i].devTeam + "-" + thisObject.config[i].bot + "-" + thisObject.config[i].product + "-" + thisObject.config[i].dataSet + "-" + thisObject.config[i].dataSetVersion;
+ key = thisObject.nodeArray[i].devTeam + "-" + thisObject.nodeArray[i].bot + "-" + thisObject.nodeArray[i].product + "-" + thisObject.nodeArray[i].dataSet + "-" + thisObject.nodeArray[i].dataSetVersion;
thisObject.keys.push(key);
thisObject.dataSets.set(key, dataSetModule);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addDataSet -> DataSet added to Map. -> key = " + key); }
- if (addCount === thisObject.config.length) {
+ if (addCount === thisObject.nodeArray.length) {
if (alreadyCalledBack === false) {
alreadyCalledBack = true
callBackFunction(global.DEFAULT_OK_RESPONSE);
diff --git a/DataSet.js b/DataSet.js
index 17b5d8ee4..4d6a72bc4 100644
--- a/DataSet.js
+++ b/DataSet.js
@@ -1,6 +1,6 @@
exports.newDataSet = function newDataSet(BOT, logger) {
- const MODULE_NAME = "Data Set";
+ const MODULE_NAME = "Dataset";
let bot = BOT;
@@ -10,7 +10,7 @@ exports.newDataSet = function newDataSet(BOT, logger) {
createTextFile: createTextFile
};
- let dependencyConfig;
+ let dataDependencyNode;
/* Storage account to be used here. */
@@ -19,16 +19,76 @@ exports.newDataSet = function newDataSet(BOT, logger) {
return thisObject;
- function initialize(pDependencyConfig, callBackFunction) {
+ function initialize(pDataDependencyNode, callBackFunction) {
try {
-
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
- logger.fileName = MODULE_NAME + "." + pDependencyConfig.bot + "." + pDependencyConfig.product + "." + pDependencyConfig.dataSet;
+ dataDependencyNode = pDataDependencyNode;
+ logger.fileName = MODULE_NAME + "." + dataDependencyNode.type + "." + dataDependencyNode.name + "." + dataDependencyNode.id;
- dependencyConfig = pDependencyConfig;
+ /* Some very basic validations that we have all the information needed. */
+ if (dataDependencyNode.referenceParent === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Data Dependency without Reference Parent. Data Dependency = " + JSON.stringify(dataDependencyNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (dataDependencyNode.referenceParent.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(dataDependencyNode.referenceParent));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (dataDependencyNode.referenceParent.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Dataset not attached to a Product Definition. Dataset = " + JSON.stringify(dataDependencyNode.referenceParent));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (dataDependencyNode.referenceParent.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Product Definition witn no codeName defined. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (dataDependencyNode.referenceParent.parentNode.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Product Definition not attached to a Bot. Product Definition = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (dataDependencyNode.referenceParent.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (dataDependencyNode.referenceParent.parentNode.parentNode.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Team. Bot = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (dataDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Team witn no codeName defined. Team = " + JSON.stringify(dataDependencyNode.referenceParent.parentNode.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ /* Simplifying the access to basic info */
+ dataDependencyNode.dataSet = dataDependencyNode.referenceParent.code.codeName
+ dataDependencyNode.product = dataDependencyNode.referenceParent.parentNode.code.codeName
+ dataDependencyNode.bot = dataDependencyNode.referenceParent.parentNode.parentNode.code.codeName
+ dataDependencyNode.devTeam = dataDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName
+
+ /* This stuff is still hardcoded and unresolved. */
+ dataDependencyNode.botVersion = {
+ "major": 1,
+ "minor": 0
+ }
+ dataDependencyNode.dataSetVersion = "dataSet.V1"
callBackFunction(global.DEFAULT_OK_RESPONSE);
@@ -44,11 +104,11 @@ exports.newDataSet = function newDataSet(BOT, logger) {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTextFile -> Entering function."); }
- let filePathRoot = dependencyConfig.devTeam + "/" + dependencyConfig.bot + "." + dependencyConfig.botVersion.major + "." + dependencyConfig.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dependencyConfig.dataSetVersion;
+ let filePathRoot = dataDependencyNode.devTeam + "/" + dataDependencyNode.bot + "." + dataDependencyNode.botVersion.major + "." + dataDependencyNode.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dataDependencyNode.dataSetVersion;
let filePath = filePathRoot + "/Output/" + pFolderPath;
filePath += '/' + pFileName
- fileStorage.getTextFile(dependencyConfig.devTeam, filePath, onFileReceived);
+ fileStorage.getTextFile(dataDependencyNode.devTeam, filePath, onFileReceived);
function onFileReceived(err, text) {
@@ -72,7 +132,7 @@ exports.newDataSet = function newDataSet(BOT, logger) {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> pFolderPath = " + pFolderPath); }
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createTextFile -> pFileName = " + pFileName); }
- let ownerId = dependencyConfig.devTeam + "-" + dependencyConfig.bot + "-" + dependencyConfig.botVersion.major + "-" + dependencyConfig.botVersion.minor + "-" + dependencyConfig.dataSetVersion;
+ let ownerId = dataDependencyNode.devTeam + "-" + dataDependencyNode.bot + "-" + dataDependencyNode.botVersion.major + "-" + dataDependencyNode.botVersion.minor + "-" + dataDependencyNode.dataSetVersion;
let botId = bot.devTeam + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.dataSetVersion;
if (ownerId !== botId) {
@@ -86,10 +146,10 @@ exports.newDataSet = function newDataSet(BOT, logger) {
return;
}
- let filePathRoot = dependencyConfig.devTeam + "/" + dependencyConfig.bot + "." + dependencyConfig.botVersion.major + "." + dependencyConfig.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dependencyConfig.dataSetVersion;
+ let filePathRoot = dataDependencyNode.devTeam + "/" + dataDependencyNode.bot + "." + dataDependencyNode.botVersion.major + "." + dataDependencyNode.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + dataDependencyNode.dataSetVersion;
let filePath = filePathRoot + "/Output/" + pFolderPath + '/' + pFileName;
- fileStorage.createTextFile(dependencyConfig.devTeam, filePath, pFileContent, onFileCreated);
+ fileStorage.createTextFile(dataDependencyNode.devTeam, filePath, pFileContent, onFileCreated);
function onFileCreated(err) {
diff --git a/DebugLog.js b/DebugLog.js
index 06b40a94e..a51b02b0b 100644
--- a/DebugLog.js
+++ b/DebugLog.js
@@ -21,9 +21,7 @@ exports.newDebugLog = function newDebugLog() {
};
let accumulatedLog = "[";
-
let disableLogging;
-
let internalLoopCounter = -1;
return thisObject;
@@ -80,35 +78,32 @@ exports.newDebugLog = function newDebugLog() {
}
let fileName;
-
if (internalLoopCounter >= 0) {
-
fileName = "Loop." + pad(thisObject.bot.loopCounter, 8) + "." + pad(internalLoopCounter, 4) + ".json";
-
} else {
-
fileName = "Loop." + pad(thisObject.bot.loopCounter, 8) + ".json";
-
}
writeLog();
+ /* This is the implementation of the mechanism to auto-mantain logs. */
+ thisObject.bot.LOGS_TO_DELETE_QUEUE.push(filePath + '/' + fileName)
+ if (thisObject.bot.LOGS_TO_DELETE_QUEUE.length > thisObject.bot.DELETE_QUEUE_SIZE) {
+ let fileToDelete = thisObject.bot.LOGS_TO_DELETE_QUEUE[0]
+ thisObject.bot.LOGS_TO_DELETE_QUEUE.splice(0,1)
+ fileStorage.deleteTextFile(thisObject.bot.devTeam, fileToDelete);
+ }
+
function writeLog() {
fileStorage.createTextFile(thisObject.bot.devTeam, filePath + '/' + fileName, contentToPersist + '\r\n' + "]", onFileCreated);
-
function onFileCreated(err) {
-
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
-
console.log("[ERROR] DebugLog -> persist -> onInizialized -> onFileCreated -> err = "+ err.message);
-
setTimeout(writeLog, 10000); // Lets retry until we make it.
return;
}
-
contentToPersist = "";
- //thisObject = {};
}
}
} catch (err) {
@@ -126,6 +121,10 @@ exports.newDebugLog = function newDebugLog() {
if (thisObject.bot === undefined) { return; }
+ if (pModule === "") { // For debugging purposes
+ console.log(pMessage)
+ }
+
let newDate = new Date();
newDate = newDate.toISOString();
diff --git a/FileStorage.js b/FileStorage.js
index 5f5534c3d..ee784f5fa 100644
--- a/FileStorage.js
+++ b/FileStorage.js
@@ -19,7 +19,8 @@ exports.newFileStorage = function newFileStorage(logger) {
let thisObject = {
getTextFile: getTextFile,
- createTextFile: createTextFile
+ createTextFile: createTextFile,
+ deleteTextFile: deleteTextFile
}
return thisObject
@@ -207,6 +208,60 @@ exports.newFileStorage = function newFileStorage(logger) {
}
}
+ function deleteTextFile(container, filePath, callBackFunction) {
+
+ logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> Entering Function.')
+
+ container = container.toLowerCase()
+
+ /* Choose path for either logs or data */
+ let fileLocation
+ if (filePath.indexOf("/Logs/") > 0) {
+ fileLocation = process.env.LOG_PATH + '/' + container + '/' + filePath
+ } else {
+ fileLocation = process.env.STORAGE_PATH + '/' + container + '/' + filePath
+ }
+
+ try {
+
+ logger.write(MODULE_NAME, '[INFO] FileStorage -> deleteTextFile -> fileLocation: ' + fileLocation)
+
+ const fs = require('fs')
+ fs.unlink(fileLocation, onUnlinked)
+
+ function onUnlinked(err) {
+ if (err) {
+ if (callBackFunction !== undefined) {
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE)
+ }
+ } else {
+ if (callBackFunction !== undefined) {
+ callBackFunction(global.DEFAULT_OK_RESPONSE)
+ }
+ }
+ }
+ } catch (err) {
+ logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> Error writing file -> file = ' + fileLocation)
+ logger.write(MODULE_NAME, '[WARN] FileStorage -> deleteTextFile -> Error writing file -> err = ' + err.stack)
+ if (callBackFunction !== undefined) {
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE)
+ }
+ }
+
+ function retry() {
+ if (currentRetryWriteTextFile < MAX_RETRY) {
+ currentRetryWriteTextFile++
+ logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> retry -> Will try to write the file again -> Retry #: ' + currentRetryWriteTextFile)
+ createTextFile(container, filePath, fileContent, callBackFunction)
+ } else {
+ currentRetryWriteTextFile = 0
+ logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> Max retries reached writting a file. Giving up.')
+ logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> file = ' + fileLocation)
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE)
+ }
+ }
+ }
+
/* Function to create folders of missing folders at any path. */
function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
diff --git a/IndicatorBot.js b/IndicatorBot.js
new file mode 100644
index 000000000..4c823887a
--- /dev/null
+++ b/IndicatorBot.js
@@ -0,0 +1,164 @@
+exports.newIndicatorBot = function newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE) {
+
+ const FULL_LOG = true;
+ const LOG_FILE_CONTENT = false;
+ const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
+
+ const MODULE_NAME = "Indicator Bot";
+
+ let thisObject = {
+ initialize: initialize,
+ finalize: finalize,
+ start: start
+ };
+
+ let utilities = UTILITIES.newCloudUtilities(logger);
+ let fileStorage = FILE_STORAGE.newFileStorage(logger);
+
+ const COMMONS = require('./Commons.js');
+ let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE);
+
+ return thisObject;
+
+ function finalize() {
+ thisObject = undefined
+ utilities = undefined
+ fileStorage = undefined
+ commons = undefined
+ }
+
+ function initialize(callBackFunction) {
+
+ try {
+
+ logger.fileName = MODULE_NAME;
+ logger.initialize();
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
+
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function start(dataFiles, timePeriod, timePeriodLabel, currentDay, interExecutionMemory, callBackFunction) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
+
+ let products = {}
+ let mainDependency = {}
+ let processingDailyFiles
+
+ if (currentDay !== undefined) {
+ processingDailyFiles = true
+ } else {
+ processingDailyFiles = false
+ }
+
+ /* The first phase here is about checking that we have everything we need at the definition level. */
+ let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies
+ if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) { return }
+
+ let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets
+ if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return }
+
+ /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */
+ commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod)
+
+ /* During the third phase, we need to generate the data of the different products this process produces an output */
+ for (let i = 0; i < outputDatasets.length; i++) {
+
+ let outputDatasetNode = outputDatasets[i]
+ let jsonData // Just build data as Json objects
+ let outputData // Data built as a result of applying user defined code and formulas at the Data Building Procedure
+ let singularVariableName // Name of the variable for this product
+ let recordDefinition // Record as defined by the user at the UI
+ let fileContent // Here we store the contents of the new data built just before writing it to a file.
+ let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution.
+
+ /*
+ For each outputDatasetNode in our process output, we will build the information based on our input products.
+ */
+
+ recordDefinition = outputDatasetNode.referenceParent.parentNode.record
+ singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName
+
+ /* Check Irregular Periods */
+ if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) {
+ resultsWithIrregularPeriods = true
+ }
+
+ /* Build the data */
+ jsonData = commons.dataBuildingProcedure(
+ products,
+ mainDependency,
+ recordDefinition,
+ outputDatasetNode.referenceParent.parentNode.dataBuilding,
+ singularVariableName,
+ timePeriod,
+ timePeriodLabel,
+ resultsWithIrregularPeriods,
+ interExecutionMemory,
+ processingDailyFiles,
+ currentDay
+ )
+
+ /* Add the calculated properties */
+ if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) {
+ outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timePeriod)
+ } else {
+ outputData = jsonData
+ }
+ products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName] = outputData
+ }
+
+ /*At the fourth and last phase, we will save the new information generated into files corresponding to each output outputDatasetNode.*/
+ let totalFilesWritten = 0
+ for (let i = 0; i < outputDatasets.length; i++) {
+ let outputDatasetNode = outputDatasets[i]
+ let outputData = products[outputDatasetNode.referenceParent.parentNode.code.pluralVariableName]
+ let resultsWithIrregularPeriods // A product will have irregular periods when the User Code inserts new result records at will, in contrast with normal procedure where the platform insert one record per loop execution.
+ let contextSummary = {}
+
+ /* Check Irregular Periods */
+ if (outputDatasetNode.referenceParent.parentNode.dataBuilding.loop.code.code.indexOf('results.push') >= 0) {
+ resultsWithIrregularPeriods = true
+ }
+
+ /* Simplifying the access to basic info */
+
+ contextSummary.dataset = outputDatasetNode.referenceParent.code.codeName
+ contextSummary.product = outputDatasetNode.referenceParent.parentNode.code.codeName
+ contextSummary.bot = outputDatasetNode.referenceParent.parentNode.parentNode.code.codeName
+ contextSummary.devTeam = outputDatasetNode.referenceParent.parentNode.parentNode.parentNode.code.codeName
+
+ /* This stuff is still hardcoded and unresolved. */
+ contextSummary.botVersion = {
+ "major": 1,
+ "minor": 0
+ }
+ contextSummary.dataSetVersion = "dataSet.V1"
+
+ let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction)
+ commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timePeriodLabel, currentDay, callBackFunction)
+ }
+
+
+ function anotherFileWritten() {
+ totalFilesWritten++
+ if (totalFilesWritten === outputDatasets.length) {
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+};
diff --git a/IndicatorBotProcessMainLoop.js b/IndicatorBotProcessMainLoop.js
index dd451faec..e29c7ac60 100644
--- a/IndicatorBotProcessMainLoop.js
+++ b/IndicatorBotProcessMainLoop.js
@@ -29,15 +29,21 @@
return thisObject;
function initialize(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."); }
processConfig = pProcessConfig;
- let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name
+ if (bot.definedByUI === true) {
+ /* The code of the bot is defined at the UI. No need to load a file with the code. */
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+ return
+ }
+
+ /* This bot is not ready for taking its code from the UI, then we need to load it from its repo. */
+
+ let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName
filePath += "/User.Bot.js"
fileStorage.getTextFile(bot.devTeam, filePath, onBotDownloaded);
@@ -47,6 +53,7 @@
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message);
+ parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
@@ -129,6 +136,8 @@
const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies');
const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies');
const DATA_SET = require(ROOT_DIR + 'DataSet');
+ const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents');
+ const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput');
/* 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. */
@@ -141,6 +150,8 @@
/* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */
+ let processOutput
+ let processExecutionEvents
let userBot;
let processFramework;
let statusDependencies;
@@ -148,7 +159,155 @@
let nextWaitTime;
- initializeStatusDependencies();
+ initializeProcessExecutionEvents();
+
+ function initializeProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger)
+
+ processExecutionEvents.initialize(pMonth, pYear, onInizialized);
+
+ function onInizialized(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well.");
+ startProcessExecutionEvents()
+ 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, "[WARN] run -> loop -> initializeProcessExecutionEvents -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> 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 -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + 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 -> initializeProcessExecutionEvents -> 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 -> initializeProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
+ function startProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents.start(onStarted);
+
+ function onStarted(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well.");
+ initializeStatusDependencies();
+ 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, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> 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 -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + 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 -> startProcessExecutionEvents -> onStarted -> 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 -> startProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
function initializeStatusDependencies() {
@@ -158,7 +317,7 @@
statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES);
- statusDependencies.initialize(processConfig.statusDependencies, pMonth, pYear, onInizialized);
+ statusDependencies.initialize(pMonth, pYear, onInizialized);
function onInizialized(err) {
@@ -233,7 +392,7 @@
dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET);
- dataDependencies.initialize(processConfig.dataDependencies, onInizialized);
+ dataDependencies.initialize(onInizialized);
function onInizialized(err) {
@@ -253,12 +412,12 @@
switch (processConfig.framework.name) {
case 'Multi-Period-Market': {
- processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE);
+ processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, UTILITIES, FILE_STORAGE);
intitializeProcessFramework();
break;
}
case 'Multi-Period-Daily': {
- processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE);
+ processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, UTILITIES, FILE_STORAGE);
intitializeProcessFramework();
break;
}
@@ -448,8 +607,7 @@
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished -> Execution finished well.");
- nextWaitTime = 'Normal';
- loopControl(nextWaitTime);
+ raiseEventsProcessOutput()
return;
}
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
@@ -568,7 +726,7 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); }
- processFramework.initialize(processConfig, statusDependencies, dataDependencies, undefined, onInizialized);
+ processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized);
function onInizialized(err) {
@@ -681,8 +839,7 @@
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well.");
- nextWaitTime = 'Normal';
- loopControl(nextWaitTime);
+ raiseEventsProcessOutput()
return;
}
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
@@ -795,6 +952,155 @@
}
}
+ function raiseEventsProcessOutput() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); }
+
+ processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger)
+
+ processOutput.raiseEvents(statusDependencies, onFinished);
+
+ function onFinished(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well.");
+ finishProcessExecutionEvents()
+ 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, "[WARN] run -> loop -> raiseEventsProcessOutput -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> 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 -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + 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 -> raiseEventsProcessOutput -> 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 -> raiseEventsProcessOutput -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
+ function finishProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents.finish(onFinished);
+
+ function onFinished(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well.");
+ nextWaitTime = 'Normal';
+ loopControl(nextWaitTime);
+ 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, "[WARN] run -> loop -> finishProcessExecutionEvents -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> 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 -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + 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 -> finishProcessExecutionEvents -> 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 -> finishProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
function loopControl(nextWaitTime) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); }
diff --git a/MultiPeriod.js b/MultiPeriod.js
index df6dc1195..00961e350 100644
--- a/MultiPeriod.js
+++ b/MultiPeriod.js
@@ -1,4 +1,4 @@
-exports.newMultiPeriod = function newMultiPeriod(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE) {
+exports.newMultiPeriod = function newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
@@ -16,11 +16,11 @@
let statusDependencies;
let dataDependencies;
- let storages = [];
- let dataFiles = [];
+ let datasets = [];
+ let dataFiles = new Map();
let multiPeriodDataFiles = new Map();
- let usertBot;
+ let botInstance;
const FILE_STORAGE = require('./FileStorage.js');
let fileStorage = FILE_STORAGE.newFileStorage(logger);
@@ -42,11 +42,11 @@
dataDependencies = pDataDependencies;
processConfig = pProcessConfig;
- for (let i = 0; i < dataDependencies.config.length; i++) {
+ for (let i = 0; i < dataDependencies.nodeArray.length; i++) {
let key;
- let storage;
- let dependency = dataDependencies.config[i];
+ let dataset;
+ let dependency = dataDependencies.nodeArray[i];
key = dependency.devTeam + "-" +
dependency.bot + "-" +
@@ -54,14 +54,16 @@
dependency.dataSet + "-" +
dependency.dataSetVersion
- storage = dataDependencies.dataSets.get(key);
+ dataset = dataDependencies.dataSets.get(key);
- storages.push(storage);
+ datasets.push(dataset);
}
- usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage);
- usertBot.initialize(callBackFunction, pAssistant);
+ let USER_BOT_MODULE = require("./TradingBot")
+
+ botInstance = USER_BOT_MODULE.newTradingBot(bot, logger, UTILITIES, FILE_STORAGE);
+ botInstance.initialize(pAssistant, callBackFunction);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
@@ -70,13 +72,13 @@
}
function finalize() {
- storages = undefined
+ datasets = undefined
dataFiles = undefined
multiPeriodDataFiles = undefined
statusDependencies = undefined
dataDependencies = undefined
- usertBot.finalize()
- usertBot = undefined
+ botInstance.finalize()
+ botInstance = undefined
fileStorage = undefined
processConfig = undefined
thisObject = undefined
@@ -125,12 +127,7 @@
Here we get the status report from the bot who knows which is the end of the market.
*/
- let botWhoKnowsTheEndOfTheMarket = statusDependencies.config[processConfig.framework.endDate.takeItFromStatusDependency];
-
- reportKey = botWhoKnowsTheEndOfTheMarket.devTeam + "-" + botWhoKnowsTheEndOfTheMarket.bot + "-" + botWhoKnowsTheEndOfTheMarket.process + "-" + "dataSet.V1";
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
-
- statusReport = statusDependencies.statusReports.get(reportKey);
+ statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
@@ -144,7 +141,7 @@
return;
}
- thisReport = statusDependencies.statusReports.get(reportKey).file;
+ thisReport = statusReport.file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
@@ -174,10 +171,7 @@
/* Finally we get our own Status Report. */
- reportKey = bot.devTeam + "-" + bot.codeName + "-" + "Multi-Period" + "-" + "dataSet.V1";
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
-
- statusReport = statusDependencies.statusReports.get(reportKey);
+ statusReport = statusDependencies.reportsByMainUtility.get("Self Reference")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
@@ -191,7 +185,7 @@
return;
}
- thisReport = statusDependencies.statusReports.get(reportKey).file;
+ thisReport = statusReport.file;
if (thisReport.lastFile !== undefined) {
@@ -290,12 +284,12 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsMarketFiles -> periodsLoopBody -> Entering function."); }
const timePeriod = global.marketFilesPeriods[n][0];
- const outputPeriodLabel = global.marketFilesPeriods[n][1];
+ const timePeriodLabel = global.marketFilesPeriods[n][1];
let dependencyIndex = 0;
- dataFiles = [];
+ dataFiles = new Map();
- if (bot.VALUES_TO_USE.timePeriod === outputPeriodLabel) {
+ if (bot.VALUES_TO_USE.timePeriod === timePeriodLabel) {
currentTimePeriod = global.marketFilesPeriods[n][0];
currentOutputPeriodName = global.marketFilesPeriods[n][1];
}
@@ -307,8 +301,8 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsMarketFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); }
- let dependency = dataDependencies.config[dependencyIndex];
- let storage = storages[dependencyIndex];
+ let dependency = dataDependencies.nodeArray[dependencyIndex];
+ let dataset = datasets[dependencyIndex];
getFile();
@@ -326,9 +320,9 @@
return
}
- let filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel;
+ let filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel;
- storage.getTextFile(filePath, fileName, onFileReceived);
+ dataset.getTextFile(filePath, fileName, onFileReceived);
function onFileReceived(err, text) {
@@ -345,7 +339,7 @@
}
let dataFile = JSON.parse(text);
- dataFiles.push(dataFile);
+ dataFiles.set(dependency.id, dataFile);
dependencyControlLoop();
@@ -377,7 +371,7 @@
dependencyIndex++;
- if (dependencyIndex < dataDependencies.config.length) {
+ if (dependencyIndex < dataDependencies.nodeArray.length) {
dependencyLoopBody();
@@ -581,28 +575,28 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> Entering function."); }
const timePeriod = global.dailyFilePeriods[n][0];
- const outputPeriodLabel = global.dailyFilePeriods[n][1];
+ const timePeriodLabel = global.dailyFilePeriods[n][1];
if (processConfig.framework.validPeriods !== undefined) {
let validPeriod = false;
for (let i = 0; i < processConfig.framework.validPeriods.length; i++) {
let period = processConfig.framework.validPeriods[i];
- if (period === outputPeriodLabel) { validPeriod = true }
+ if (period === timePeriodLabel) { validPeriod = true }
}
if (validPeriod === false) {
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> outputPeriodLabel = " + outputPeriodLabel); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> timePeriodLabel = " + timePeriodLabel); }
periodsControlLoop();
return;
}
}
- if (bot.VALUES_TO_USE.timePeriod === outputPeriodLabel) {
+ if (bot.VALUES_TO_USE.timePeriod === timePeriodLabel) {
currentTimePeriod = global.dailyFilePeriods[n][0];
currentOutputPeriodName = global.dailyFilePeriods[n][1];
}
let dependencyIndex = 0;
- dataFiles = [];
+ dataFiles = new Map();
dependencyLoopBody();
function dependencyLoopBody() {
@@ -611,8 +605,8 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> periodsLoopBody -> dependencyLoopBody -> Entering function."); }
- let dependency = dataDependencies.config[dependencyIndex];
- let storage = storages[dependencyIndex];
+ let dependency = dataDependencies.nodeArray[dependencyIndex];
+ let dataset = datasets[dependencyIndex];
let previousFile;
let currentFile;
@@ -640,13 +634,13 @@
let filePath
if (dependency.dataSet === "Multi-Period-Daily") {
- filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath;
+ filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath;
} else {
filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath;
}
let fileName = market.assetA + '_' + market.assetB + ".json";
- storage.getTextFile(filePath, fileName, onFileReceived);
+ dataset.getTextFile(filePath, fileName, onFileReceived);
function onFileReceived(err, text) {
@@ -716,13 +710,13 @@
let dateForPath = bot.multiPeriodProcessDatetime.getUTCFullYear() + '/' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.multiPeriodProcessDatetime.getUTCDate(), 2);
let filePath
if (dependency.dataSet === "Multi-Period-Daily") {
- filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath;
+ filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath;
} else {
filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath;
}
let fileName = market.assetA + '_' + market.assetB + ".json";
- storage.getTextFile(filePath, fileName, onFileReceived);
+ dataset.getTextFile(filePath, fileName, onFileReceived);
function onFileReceived(err, text) {
@@ -754,7 +748,7 @@
let dataFile = previousFile.concat(currentFile);
- dataFiles.push(dataFile);
+ dataFiles.set(dependency.id, dataFile);
dependencyControlLoop();
@@ -785,7 +779,7 @@
dependencyIndex++;
- if (dependencyIndex < dataDependencies.config.length) {
+ if (dependencyIndex < dataDependencies.nodeArray.length) {
dependencyLoopBody();
@@ -847,7 +841,7 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriodsDailyFiles -> callTheBot -> Entering function."); }
- usertBot.start(
+ botInstance.start(
multiPeriodDataFiles,
currentTimePeriod,
currentOutputPeriodName,
@@ -959,21 +953,19 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
- let productIndex = 0;
- productLoopBody();
+ let outputDatasetIndex = -1;
+ controlLoop();
function productLoopBody() {
-
- let product = bot.products[productIndex];
-
- writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodProcessDatetime, product, currentOutputPeriodName, controlLoop);
+ let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName;
+ writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodProcessDatetime, productCodeName, currentOutputPeriodName, controlLoop);
}
function controlLoop() {
- productIndex++;
+ outputDatasetIndex++;
- if (productIndex < bot.products.length) {
+ if (outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length) {
productLoopBody();
} else {
callBack(global.DEFAULT_OK_RESPONSE);
@@ -987,7 +979,7 @@
}
- function writeDataRange(pBegin, pEnd, product, currentOutputPeriodName, callBack) {
+ function writeDataRange(pBegin, pEnd, productCodeName, currentOutputPeriodName, callBack) {
try {
@@ -1001,7 +993,7 @@
let fileContent = JSON.stringify(dataRange);
let fileName = '/Data.Range.' + market.assetA + '_' + market.assetB + '.json';
- let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + product.codeName + "/" + 'Multi-Period-Daily' + fileName;
+ let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName;
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
@@ -1019,7 +1011,7 @@
logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent);
}
- let key = bot.devTeam + "-" + bot.codeName + "-" + product.codeName + "-" + currentOutputPeriodName
+ let key = bot.devTeam + "-" + bot.codeName + "-" + productCodeName + "-" + currentOutputPeriodName
let event = {
dateRange: dataRange
}
diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js
index 4585fcea9..a19d0c52c 100644
--- a/MultiPeriodDaily.js
+++ b/MultiPeriodDaily.js
@@ -1,4 +1,4 @@
-exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE) {
+exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
@@ -16,12 +16,11 @@
let statusDependencies;
let dataDependencies;
- let storages = [];
- let dataFiles = [];
+ let datasets = [];
+ let dataFiles = new Map;
- let usertBot;
+ let botInstance;
- const FILE_STORAGE = require('./FileStorage.js');
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let bootstrappingTheProcess = false
@@ -30,7 +29,7 @@
return thisObject;
- function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, pAssistant, callBackFunction) {
+ function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) {
try {
@@ -43,11 +42,11 @@
dataDependencies = pDataDependencies;
processConfig = pProcessConfig;
- for (let i = 0; i < dataDependencies.config.length; i++) {
+ for (let i = 0; i < dataDependencies.nodeArray.length; i++) {
let key;
- let storage;
- let dependency = dataDependencies.config[i];
+ let dataset;
+ let dependency = dataDependencies.nodeArray[i];
key = dependency.devTeam + "-" +
dependency.bot + "-" +
@@ -55,14 +54,16 @@
dependency.dataSet + "-" +
dependency.dataSetVersion
- storage = dataDependencies.dataSets.get(key);
+ dataset = dataDependencies.dataSets.get(key);
- storages.push(storage);
+ datasets.push(dataset);
}
- usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage);
- usertBot.initialize(dataDependencies, callBackFunction, pAssistant);
+ let USER_BOT_MODULE = require("./IndicatorBot")
+
+ botInstance = USER_BOT_MODULE.newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE);
+ botInstance.initialize(callBackFunction);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack);
@@ -71,11 +72,11 @@
}
function finalize() {
- storages = undefined
+ datasets = undefined
dataFiles = undefined
statusDependencies = undefined
dataDependencies = undefined
- usertBot = undefined
+ botInstance = undefined
fileStorage = undefined
processConfig = undefined
thisObject = undefined
@@ -126,12 +127,7 @@
We look first for the bot who knows the begining of the marke in order to get when the market starts.
*/
- let botWhoKnowsTheBeginingOfTheMarket = statusDependencies.config[processConfig.framework.startDate.takeItFromStatusDependency];
-
- reportKey = botWhoKnowsTheBeginingOfTheMarket.devTeam + "-" + botWhoKnowsTheBeginingOfTheMarket.bot + "-" + botWhoKnowsTheBeginingOfTheMarket.process + "-" + "dataSet.V1";
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
-
- statusReport = statusDependencies.statusReports.get(reportKey);
+ statusReport = statusDependencies.reportsByMainUtility.get("Market Starting Point")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
@@ -145,7 +141,7 @@
return;
}
- thisReport = statusDependencies.statusReports.get(reportKey).file;
+ thisReport = statusReport.file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
@@ -168,7 +164,7 @@
/* The ending date is fixed, we will end there. */
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> We have got a user defined endDate. -> endDate = " + processConfig.framework.endDate.fixedDate); }
- contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate);
+ contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate);
} else {
@@ -176,12 +172,7 @@
Here we get the status report from the bot who knows which is the end of the market.
*/
- let botWhoKnowsTheEndOfTheMarket = statusDependencies.config[processConfig.framework.endDate.takeItFromStatusDependency];
-
- reportKey = botWhoKnowsTheEndOfTheMarket.devTeam + "-" + botWhoKnowsTheEndOfTheMarket.bot + "-" + botWhoKnowsTheEndOfTheMarket.process + "-" + "dataSet.V1";
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
-
- statusReport = statusDependencies.statusReports.get(reportKey);
+ statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
@@ -195,7 +186,7 @@
return;
}
- thisReport = statusDependencies.statusReports.get(reportKey).file;
+ thisReport = statusReport.file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
@@ -214,10 +205,7 @@
/* Finally we get our own Status Report. */
- reportKey = bot.devTeam + "-" + bot.codeName + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1";
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
-
- statusReport = statusDependencies.statusReports.get(reportKey);
+ statusReport = statusDependencies.reportsByMainUtility.get("Self Reference")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
@@ -231,7 +219,7 @@
return;
}
- thisReport = statusDependencies.statusReports.get(reportKey).file;
+ thisReport = statusReport.file;
if (thisReport.lastFile !== undefined) {
@@ -382,23 +370,23 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Entering function."); }
const timePeriod = global.dailyFilePeriods[n][0];
- const outputPeriodLabel = global.dailyFilePeriods[n][1];
+ const timePeriodLabel = global.dailyFilePeriods[n][1];
if (processConfig.framework.validPeriods !== undefined) {
let validPeriod = false;
for (let i = 0; i < processConfig.framework.validPeriods.length; i++) {
let period = processConfig.framework.validPeriods[i];
- if (period === outputPeriodLabel) { validPeriod = true }
+ if (period === timePeriodLabel) { validPeriod = true }
}
if (validPeriod === false) {
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> outputPeriodLabel = " + outputPeriodLabel); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Discarding period for not being listed as a valid period. -> timePeriodLabel = " + timePeriodLabel); }
periodsControlLoop();
return;
}
}
let dependencyIndex = 0;
- dataFiles = [];
+ dataFiles = new Map();
dependencyLoopBody();
@@ -408,8 +396,8 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> Entering function."); }
- let dependency = dataDependencies.config[dependencyIndex];
- let storage = storages[dependencyIndex];
+ let dependency = dataDependencies.nodeArray[dependencyIndex];
+ let dataset = datasets[dependencyIndex];
let previousFile;
let currentFile;
@@ -430,13 +418,13 @@
let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2);
let filePath
if (dependency.dataSet === "Multi-Period-Daily") {
- filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath;
+ filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath;
} else {
filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath;
}
let fileName = market.assetA + '_' + market.assetB + ".json";
- storage.getTextFile(filePath, fileName, onFileReceived);
+ dataset.getTextFile(filePath, fileName, onFileReceived);
function onFileReceived(err, text) {
@@ -500,13 +488,13 @@
let dateForPath = bot.multiPeriodDailyProcessDatetime.getUTCFullYear() + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.multiPeriodDailyProcessDatetime.getUTCDate(), 2);
let filePath
if (dependency.dataSet === "Multi-Period-Daily") {
- filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel + "/" + dateForPath;
+ filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel + "/" + dateForPath;
} else {
filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath;
}
let fileName = market.assetA + '_' + market.assetB + ".json";
- storage.getTextFile(filePath, fileName, onFileReceived);
+ dataset.getTextFile(filePath, fileName, onFileReceived);
function onFileReceived(err, text) {
@@ -545,7 +533,7 @@
let dataFile = previousFile.concat(currentFile);
- dataFiles.push(dataFile);
+ dataFiles.set(dependency.id, dataFile);
dependencyControlLoop();
}
@@ -575,7 +563,7 @@
dependencyIndex++;
- if (dependencyIndex < dataDependencies.config.length) {
+ if (dependencyIndex < dataDependencies.nodeArray.length) {
dependencyLoopBody();
@@ -598,15 +586,13 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> Entering function."); }
const timePeriod = global.dailyFilePeriods[n][0];
- const outputPeriodLabel = global.dailyFilePeriods[n][1];
+ const timePeriodLabel = global.dailyFilePeriods[n][1];
- usertBot.start(
+ botInstance.start(
dataFiles,
timePeriod,
- outputPeriodLabel,
+ timePeriodLabel,
bot.multiPeriodDailyProcessDatetime,
- contextVariables.dateBeginOfMarket,
- contextVariables.dateEndOfMarket,
interExecutionMemoryArray[n],
onBotFinished);
@@ -701,21 +687,19 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
- let productIndex = 0;
- productLoopBody();
+ let outputDatasetIndex = -1;
+ controlLoop();
function productLoopBody() {
-
- let folderName = bot.products[productIndex].codeName;
-
- writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, folderName, controlLoop);
+ let productCodeName = bot.processNode.referenceParent.processOutput.outputDatasets[outputDatasetIndex].referenceParent.parentNode.code.codeName;
+ writeDataRange(contextVariables.dateBeginOfMarket, bot.multiPeriodDailyProcessDatetime, productCodeName, controlLoop);
}
function controlLoop() {
- productIndex++;
+ outputDatasetIndex++;
- if (productIndex < bot.products.length) {
+ if (outputDatasetIndex < bot.processNode.referenceParent.processOutput.outputDatasets.length) {
productLoopBody();
} else {
callBack(global.DEFAULT_OK_RESPONSE);
@@ -729,7 +713,7 @@
}
- function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
+ function writeDataRange(pBegin, pEnd, productCodeName, callBack) {
try {
@@ -743,7 +727,7 @@
let fileContent = JSON.stringify(dataRange);
let fileName = '/Data.Range.' + market.assetA + '_' + market.assetB + '.json';
- let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process + fileName;
+ let filePath = bot.filePathRoot + "/Output/" + productCodeName + "/" + bot.process + fileName;
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
diff --git a/MultiPeriodMarket.js b/MultiPeriodMarket.js
index 99870774e..4ee15f3e8 100644
--- a/MultiPeriodMarket.js
+++ b/MultiPeriodMarket.js
@@ -1,4 +1,4 @@
-exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, COMMONS, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE) {
+exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
@@ -14,19 +14,16 @@
let statusDependencies;
let dataDependencies;
- let storages = [];
- let dataFiles = [];
+ let datasets = [];
+ let dataFiles = new Map();
- let usertBot;
-
- const FILE_STORAGE = require('./FileStorage.js');
- let fileStorage = FILE_STORAGE.newFileStorage(logger);
+ let botInstance;
let processConfig;
return thisObject;
- function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, pAssistant, callBackFunction) {
+ function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) {
try {
@@ -39,11 +36,11 @@
dataDependencies = pDataDependencies;
processConfig = pProcessConfig;
- for (let i = 0; i < dataDependencies.config.length; i++) {
+ for (let i = 0; i < dataDependencies.nodeArray.length; i++) {
let key;
- let storage;
- let dependency = dataDependencies.config[i];
+ let dataset;
+ let dependency = dataDependencies.nodeArray[i];
key = dependency.devTeam + "-" +
dependency.bot + "-" +
@@ -51,14 +48,16 @@
dependency.dataSet + "-" +
dependency.dataSetVersion
- storage = dataDependencies.dataSets.get(key);
+ dataset = dataDependencies.dataSets.get(key);
- storages.push(storage);
+ datasets.push(dataset);
}
- usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, fileStorage);
- usertBot.initialize(dataDependencies, callBackFunction, pAssistant);
+ let USER_BOT_MODULE = require("./IndicatorBot")
+
+ botInstance = USER_BOT_MODULE.newIndicatorBot(bot, logger, UTILITIES, FILE_STORAGE);
+ botInstance.initialize(callBackFunction);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack);
@@ -67,12 +66,11 @@
}
function finalize() {
- storages = undefined
+ datasets = undefined
dataFiles = undefined
statusDependencies = undefined
dataDependencies = undefined
- usertBot = undefined
- fileStorage = undefined
+ botInstance = undefined
processConfig = undefined
thisObject = undefined
}
@@ -150,10 +148,10 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> Entering function."); }
const timePeriod = global.marketFilesPeriods[n][0];
- const outputPeriodLabel = global.marketFilesPeriods[n][1];
+ const timePeriodLabel = global.marketFilesPeriods[n][1];
let dependencyIndex = 0;
- dataFiles = [];
+ dataFiles = new Map;
dependencyLoopBody();
@@ -163,8 +161,8 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> Entering function."); }
- let dependency = dataDependencies.config[dependencyIndex];
- let storage = storages[dependencyIndex];
+ let dependency = dataDependencies.nodeArray[dependencyIndex];
+ let dataset = datasets[dependencyIndex];
getFile();
@@ -179,11 +177,11 @@
let filePath
if (dependency.dataSet === "Multi-Period-Market") {
- filePath = dependency.product + '/' + dependency.dataSet + "/" + outputPeriodLabel;
+ filePath = dependency.product + '/' + dependency.dataSet + "/" + timePeriodLabel;
} else {
filePath = dependency.product + '/' + dependency.dataSet + "/" + dateForPath;
}
- storage.getTextFile(filePath, fileName, onFileReceived);
+ dataset.getTextFile(filePath, fileName, onFileReceived);
function onFileReceived(err, text) {
@@ -200,7 +198,7 @@
}
let dataFile = JSON.parse(text);
- dataFiles.push(dataFile);
+ dataFiles.set(dependency.id, dataFile);
dependencyControlLoop();
@@ -232,7 +230,7 @@
dependencyIndex++;
- if (dependencyIndex < dataDependencies.config.length) {
+ if (dependencyIndex < dataDependencies.nodeArray.length) {
dependencyLoopBody();
@@ -255,14 +253,14 @@
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> Entering function."); }
const timePeriod = global.marketFilesPeriods[n][0];
- const outputPeriodLabel = global.marketFilesPeriods[n][1];
+ const timePeriodLabel = global.marketFilesPeriods[n][1];
- usertBot.start(
+ botInstance.start(
dataFiles,
timePeriod,
- outputPeriodLabel,
- contextVariables.dateBeginOfMarket,
- contextVariables.dateEndOfMarket,
+ timePeriodLabel,
+ undefined,
+ undefined,
onBotFinished);
function onBotFinished(err) {
diff --git a/ProcessExecutionEvents.js b/ProcessExecutionEvents.js
new file mode 100644
index 000000000..fd04c2efc
--- /dev/null
+++ b/ProcessExecutionEvents.js
@@ -0,0 +1,170 @@
+exports.newProcessExecutionEvents = function newProcessExecutionEvents(BOT, logger) {
+
+ /*
+
+ Here we manage the process execution events. This allow us to emit events when the process starts and finishes. Also to wait for a dependent event
+ of another process.
+
+ */
+
+ const MODULE_NAME = "Process Execution Events";
+
+ let bot = BOT;
+
+ let thisObject = {
+ initialize, initialize,
+ start: start,
+ finish: finish
+ };
+
+ let processThisDependsOn; // This is the process this process needs to wait for it to finishes in order to start.
+
+ let month;
+ let year;
+ let currentProcessKey
+ let datetimeForLogs = ''
+
+ return thisObject;
+
+ function initialize(pMonth, pYear, callBackFunction) {
+
+ try {
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
+
+ month = pMonth;
+ year = pYear;
+
+ if (month && year) {
+ datetimeForLogs = "Month: " + month + " Year: " + year
+ }
+
+ let name = 'Not Depends on any Process'
+
+ if (bot.processNode.referenceParent !== undefined) {
+
+ currentProcessKey = bot.processNode.referenceParent.name + "-" + bot.processNode.referenceParent.type + "-" + bot.processNode.referenceParent.id
+
+ if (bot.processNode.referenceParent.executionStartedEvent !== undefined) {
+ if (bot.processNode.referenceParent.executionStartedEvent.referenceParent !== undefined) {
+ if (bot.processNode.referenceParent.executionStartedEvent.referenceParent.parentNode !== undefined) {
+ processThisDependsOn = bot.processNode.referenceParent.executionStartedEvent.referenceParent.parentNode
+
+ name = processThisDependsOn.name
+ if (processThisDependsOn.code.monthlyInstances === true) {
+ logger.fileName = MODULE_NAME + "." + name + "." + year + "." + month;
+ }
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> " + currentProcessKey + datetimeForLogs + " depends on " + name); }
+ }
+ }
+ }
+ }
+
+ logger.fileName = MODULE_NAME + "." + name;
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function start(callBackFunction) {
+
+ try {
+
+ if (processThisDependsOn !== undefined) {
+
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> Entering Code Block."); }
+
+ /* This forces this process to wait until the process that this one depends on, emits its event signaling that the process execution has finished. */
+
+ let extraCallerId = '-' + Math.trunc(Math.random() * 10000) + '-'
+ if (month) { extraCallerId = extraCallerId + '-' + month }
+ if (year) { extraCallerId = extraCallerId + '-' + year }
+
+ let key = processThisDependsOn.name + "-" + processThisDependsOn.type + "-" + processThisDependsOn.id
+ let callerId = bot.devTeam + "-" + bot.codeName + "-" + bot.process + extraCallerId
+
+ let subscriptionId
+
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> started listening to Process Execution Finished. "); }
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> key = " + key); }
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> callerId = " + callerId); }
+
+ global.SYSTEM_EVENT_HANDLER.listenToEvent(key, 'Process Execution Finished', undefined, callerId, responseCallBack, eventsCallBack)
+
+ function responseCallBack(message) {
+ if (message.result !== global.DEFAULT_OK_RESPONSE.result) {
+ logger.write(MODULE_NAME, "[ERROR] start -> responseCallBack -> message = " + JSON.stringify(message))
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ } else {
+ subscriptionId = message.eventSubscriptionId
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> waitForDependantProcess -> subscriptionId = " + subscriptionId); }
+ }
+ }
+
+ function eventsCallBack(message) {
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> Entering function."); }
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> message = " + JSON.stringify(message)); }
+
+ /* We continue the normal flow after we learn the dependent process has finished its execution. */
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> stopListening to Process Execution Finished. "); }
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> subscriptionId = " + subscriptionId); }
+ global.SYSTEM_EVENT_HANDLER.stopListening(key, 'Process Execution Finished', subscriptionId)
+
+ if (message.event.err.result === global.DEFAULT_OK_RESPONSE.result) {
+ /* We emit our own event that the process started. */
+ let event = {
+ err: global.DEFAULT_OK_RESPONSE
+ }
+ global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started')
+ global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event)
+
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> eventsCallBack -> " + currentProcessKey + " Process Execution Started because " + key + datetimeForLogs + " Finished."); }
+ }
+
+ callBackFunction(message.event.err);
+ }
+ } else {
+ /* In this case, the Process does not depends on a process which needs to wait for. */
+ /* We emit our own event that the process started. */
+ let event = {
+ err: global.DEFAULT_OK_RESPONSE
+ }
+ global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Started')
+ global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Started', event)
+
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] start -> " + currentProcessKey + " Process Execution Started " + datetimeForLogs); }
+
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+ }
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function finish(callBackFunction) {
+
+ try {
+
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> Entering function."); }
+
+ let event = {
+ err: global.DEFAULT_OK_RESPONSE
+ }
+
+ global.SYSTEM_EVENT_HANDLER.createEventHandler(currentProcessKey, 'Process Execution Finished')
+ global.SYSTEM_EVENT_HANDLER.raiseEvent(currentProcessKey, 'Process Execution Finished', event)
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] finish -> " + currentProcessKey + " Process Execution Finished " + datetimeForLogs); }
+
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] finish -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+};
diff --git a/ProcessOutput.js b/ProcessOutput.js
new file mode 100644
index 000000000..ef74e7305
--- /dev/null
+++ b/ProcessOutput.js
@@ -0,0 +1,104 @@
+exports.newProcessOutput = function newProcessOutput(BOT, logger) {
+
+ /*
+ Here we emit the events that signals that a Dataset was updated.
+ */
+
+ const MODULE_NAME = "Process Output";
+
+ let bot = BOT;
+
+ let thisObject = {
+ raiseEvents, raiseEvents
+ };
+
+ return thisObject;
+
+ function raiseEvents(statusDependencies, callBackFunction) {
+
+ try {
+ if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] raiseEvents -> Entering function."); }
+
+ /* We will reaise the events for the datasets impacted by the process that just finished. */
+
+ if (bot.processNode.referenceParent.processOutput !== undefined) {
+ if (bot.processNode.referenceParent.processOutput.outputDatasets !== undefined) {
+ let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets
+
+ for (let j = 0; j < outputDatasets.length; j++) {
+ let outputDataset = outputDatasets[j]
+
+ /* Some validations to verify that everything is in place. */
+ if (outputDataset.referenceParent !== undefined) {
+ if (outputDataset.referenceParent.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Dataset Definition not defined. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (outputDataset.referenceParent.parentNode !== undefined) {
+ if (outputDataset.referenceParent.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Product not defined. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (outputDataset.referenceParent.parentNode.parentNode !== undefined) {
+ if (outputDataset.referenceParent.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Bot not defined. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ if (outputDataset.referenceParent.parentNode.parentNode.parentNode !== undefined) {
+ if (outputDataset.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Team not defined. Team = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Team. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Product not attached to a Bot. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Dataset Definition not attached to a Product. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Output Dataset with no reference to a Dataset Definition. Output Dataset = " + JSON.stringify(outputDataset));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ /* All good, lets emit the event. */
+
+ let devTeam = outputDataset.referenceParent.parentNode.parentNode.parentNode.code.codeName
+ let botName = outputDataset.referenceParent.parentNode.parentNode.code.codeName
+ let product = outputDataset.referenceParent.parentNode.code.codeName
+ let dataset = outputDataset.referenceParent.code.codeName
+
+ let statusReport = statusDependencies.reportsByMainUtility.get("Self Reference")
+
+ key = devTeam + "-" + botName + "-" + product + "-" + dataset
+ let event = {
+ lastFile: statusReport.file.lastFile
+ }
+ global.SYSTEM_EVENT_HANDLER.createEventHandler(key, 'Dataset Updated')
+ global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Dataset Updated', event)
+
+ }
+ }
+ }
+
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] raiseEvents -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+};
diff --git a/Project.njsproj b/Project.njsproj
index 63fc32b8b..22992fbc9 100644
--- a/Project.njsproj
+++ b/Project.njsproj
@@ -25,6 +25,10 @@
Code
+
+ Code
+
+
@@ -36,10 +40,22 @@
Code
+
+ Code
+
+
+ Code
+
Code
-
+
+ Code
+
+
+ Code
+
+
Code
diff --git a/Root.js b/Root.js
index 8c82a8125..72781d834 100644
--- a/Root.js
+++ b/Root.js
@@ -79,7 +79,7 @@
logContent: false,
intensiveLogging: false
},
- "Data Set": {
+ "Dataset": {
logInfo: true,
logWarnings: false,
logErrors: true,
@@ -92,6 +92,20 @@
logErrors: true,
logContent: false,
intensiveLogging: false
+ },
+ "Process Execution Events": {
+ logInfo: true,
+ logWarnings: false,
+ logErrors: true,
+ logContent: false,
+ intensiveLogging: false
+ },
+ "Process Output": {
+ logInfo: true,
+ logWarnings: false,
+ logErrors: true,
+ logContent: false,
+ intensiveLogging: false
}
};
@@ -121,292 +135,300 @@
const INDICATOR_BOT_MAIN_LOOP_MODULE = require('./IndicatorBotProcessMainLoop');
const SENSOR_BOT_MAIN_LOOP_MODULE = require('./SensorBotProcessMainLoop');
- const TRADING_ENGINE_MAIN_LOOP_MODULE = require('./TradingEngineProcessMainLoop');
-
- /* Loop through all the processes configured to be run by this Node.js Instance. */
- /* Now we will read the config of the bot from the path we obtained at the Task Server config. */
+ const TRADING_ENGINE_MAIN_LOOP_MODULE = require('./TradingBotProcessMainLoop');
let botConfig;
+ let processInstance = global.TASK_NODE.bot.processes[processIndex]
- getBotConfig();
+ /* Here we will check if we need to load the configuration and code of the bot from a file or we will take that from the UI. */
+ if (
+ processInstance.referenceParent.code.framework !== undefined &&
+ ( processInstance.referenceParent.code.framework.name === 'Multi-Period-Market' ||
+ processInstance.referenceParent.code.framework.name === 'Multi-Period-Daily' ||
+ processInstance.referenceParent.code.framework.name === 'Multi-Period')
+ ) {
+ botConfig = processInstance.referenceParent.parentNode.code
+ botConfig.definedByUI = true
+ bootingBot(processIndex)
+ return
+ } else {
+ getBotConfigFromFile();
+ return
+ }
- function getBotConfig() {
+ function getBotConfigFromFile() {
try {
- console.log(logDisplace + "Root : [INFO] start -> getBotConfig -> Entering function. ");
+ console.log(logDisplace + "Root : [INFO] start -> getBotConfigFromFile -> Entering function. ");
const FILE_STORAGE = require('./FileStorage.js');
let fileStorage = FILE_STORAGE.newFileStorage();
- let filePath = global.TASK_NODE.bot.code.team + '/bots/' + global.TASK_NODE.bot.code.repo + '/this.bot.config.json';
+ let filePath = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName + '/bots/' + global.TASK_NODE.bot.code.repo + '/this.bot.config.json';
- fileStorage.getTextFile(global.TASK_NODE.bot.code.team, filePath, onFileReceived);
+ fileStorage.getTextFile(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName, filePath, onFileReceived);
function onFileReceived(err, text) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
- console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err));
-
+ console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err));
+ console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> filePath = " + filePath);
+ console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> team = " + global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName);
return;
}
try {
botConfig = JSON.parse(text);
botConfig.repo = global.TASK_NODE.bot.code.repo;
- findProcess(processIndex);
+ bootingBot(processIndex);
} catch (err) {
- console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err));
+ console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> onInizialized -> onFileReceived -> err = " + JSON.stringify(err));
return;
}
}
}
catch (err) {
- console.log(logDisplace + "Root : [ERROR] start -> getBotConfig -> err = " + err.stack);
+ console.log(logDisplace + "Root : [ERROR] start -> getBotConfigFromFile -> err = " + err.stack);
return;
}
}
- function findProcess(processIndex) {
+ function bootingBot(processIndex) {
try {
- if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> Entering function. "); }
+ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> Entering function. "); }
- botConfig.process = global.TASK_NODE.bot.processes[processIndex].code.process;
+ botConfig.process = global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName
botConfig.debug = {};
- botConfig.processIndex = processIndex
botConfig.processNode = global.TASK_NODE.bot.processes[processIndex]
- /* Loop Counter */
+ /* Logs Mantainance Stuff */
+ botConfig.LOGS_TO_DELETE_QUEUE = []
+ botConfig.DELETE_QUEUE_SIZE = 10 // This number represents how many log files can be at the queue at any point in time, which means how many logs are not still deleted.
- botConfig.loopCounter = 0;
+ /* Simplifying the access to basic info */
+ botConfig.devTeam = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName
+
+ /* This stuff is still hardcoded and unresolved. */
+ botConfig.version = {
+ "major": 1,
+ "minor": 0
+ }
+ botConfig.dataSetVersion = "dataSet.V1"
+
+ /* Loop Counter */
+ botConfig.loopCounter = 0;
/* File Path Root */
-
botConfig.filePathRoot = botConfig.devTeam + "/" + botConfig.codeName + "." + botConfig.version.major + "." + botConfig.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + botConfig.dataSetVersion;
/* Process Key */
-
botConfig.processKey = global.TASK_NODE.bot.processes[processIndex].name + '-' + global.TASK_NODE.bot.processes[processIndex].type + '-' + global.TASK_NODE.bot.processes[processIndex].id
- if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> filePathRoot = " + botConfig.filePathRoot); }
+ /* Bot Type */
+ botConfig.type = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.type
- /* Now we loop throug all the configured processes at each bots configuration until we find the one we are supposed to run at this Node.js process. */
+ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> filePathRoot = " + botConfig.filePathRoot); }
+ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> Process found at the bot configuration file. -> process = " + global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName); }
- let processFound = false;
+ let processConfig = global.TASK_NODE.bot.processes[processIndex].referenceParent.code
- for (let i = 0; i < botConfig.processes.length; i++) {
-
- if (botConfig.processes[i].name === global.TASK_NODE.bot.processes[processIndex].code.process) {
-
- processFound = true;
- if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> Process found at the bot configuration file. -> process = " + global.TASK_NODE.bot.processes[processIndex].code.process); }
-
- let processConfig = botConfig.processes[i];
-
- if (processConfig.framework !== undefined) {
- if (processConfig.framework.name === "Multi-Period-Daily" || processConfig.framework.name === "Multi-Period-Market" || processConfig.framework.name === "Multi-Period") {
- processConfig.framework.startDate.resumeExecution = true;
- if (processConfig.startMode.noTime !== undefined) {
- if (processConfig.startMode.noTime.run === "true") {
- if (processConfig.startMode.noTime.beginDatetime !== undefined) {
- processConfig.framework.startDate.fixedDate = processConfig.startMode.noTime.beginDatetime;
- processConfig.framework.startDate.resumeExecution = true;
- }
- }
- }
- if (processConfig.startMode.userDefined !== undefined) {
- if (processConfig.startMode.userDefined.run === "true") {
- if (processConfig.startMode.userDefined.beginDatetime !== undefined) {
- processConfig.framework.startDate.fixedDate = processConfig.startMode.userDefined.beginDatetime;
- processConfig.framework.startDate.resumeExecution = processConfig.startMode.userDefined.resumeExecution;
- }
- }
+ if (processConfig.framework !== undefined) {
+ if (processConfig.framework.name === "Multi-Period-Daily" || processConfig.framework.name === "Multi-Period-Market" || processConfig.framework.name === "Multi-Period") {
+ processConfig.framework.startDate.resumeExecution = true;
+ if (processConfig.startMode.noTime !== undefined) {
+ if (processConfig.startMode.noTime.run === "true") {
+ if (processConfig.startMode.noTime.beginDatetime !== undefined) {
+ processConfig.framework.startDate.fixedDate = processConfig.startMode.noTime.beginDatetime;
+ processConfig.framework.startDate.resumeExecution = true;
}
}
}
-
- try {
-
- /* We test each type of start Mode to get what to run and how. */
-
- if (processConfig.startMode.allMonths !== undefined) {
-
- if (processConfig.startMode.allMonths.run === "true") {
-
- if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> allMonths start mode detected. "); }
-
- for (let year = processConfig.startMode.allMonths.maxYear; year >= processConfig.startMode.allMonths.minYear; year--) {
-
- for (let month = 12; month > 0; month--) {
-
- let padMonth = pad(month, 2)
-
- let newInstanceBotConfig = JSON.parse(JSON.stringify(botConfig));
-
- newInstanceBotConfig.debug = {
- month: pad(month, 2),
- year: pad(year, 4)
- };
-
- let timeDelay = Math.random() * 10 * 1000; // We introduce a short delay so as to not overload the machine.
- setTimeout(startProcess, timeDelay);
-
- function startProcess() {
-
- if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> startProcess -> Ready to start process."); }
-
- switch (botConfig.type) {
- case 'Sensor': {
- runSensorBot(newInstanceBotConfig, processConfig, padMonth, year);
- break;
- }
- case 'Indicator': {
- runIndicatorBot(newInstanceBotConfig, processConfig, padMonth, year);
- break;
- }
- default: {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
- }
- }
- }
- }
- }
+ if (processConfig.startMode.userDefined !== undefined) {
+ if (processConfig.startMode.userDefined.run === "true") {
+ if (processConfig.startMode.userDefined.beginDatetime !== undefined) {
+ processConfig.framework.startDate.fixedDate = processConfig.startMode.userDefined.beginDatetime;
+ processConfig.framework.startDate.resumeExecution = processConfig.startMode.userDefined.resumeExecution;
}
}
-
- if (processConfig.startMode.oneMonth !== undefined) {
-
- if (processConfig.startMode.oneMonth.run === "true") {
-
- if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> oneMonth start mode detected. "); }
-
- startProcess();
-
- function startProcess() {
-
- if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> findProcess -> startProcess -> Ready to start process."); }
-
- let month = pad(processConfig.startMode.oneMonth.month, 2);
- let year = processConfig.startMode.oneMonth.year;
-
- botConfig.debug = {
- month: pad(month, 2),
- year: pad(year, 4)
- };
-
- switch (botConfig.type) {
- case 'Sensor': {
- runSensorBot(botConfig, processConfig, month, year);
- break;
- }
- case 'Indicator': {
- runIndicatorBot(botConfig, processConfig, month, year);
- break;
- }
- default: {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
- }
- }
- }
- }
- }
-
- if (processConfig.startMode.noTime !== undefined) {
-
- if (processConfig.startMode.noTime.run === "true") {
-
- let month = pad((new Date()).getUTCMonth() + 1, 2);
- let year = (new Date()).getUTCFullYear();
-
- if (processConfig.startMode.noTime.resumeExecution === true) {
- botConfig.hasTheBotJustStarted = false;
- } else {
- botConfig.hasTheBotJustStarted = true;
- }
-
- switch (botConfig.type) {
- case 'Sensor': {
- runSensorBot(botConfig, processConfig, month, year);
- break;
- }
- case 'Indicator': {
- runIndicatorBot(botConfig, processConfig, month, year);
- break;
- }
- case 'Trading Engine': {
- runTradingEngine(botConfig, processConfig);
- break;
- }
- default: {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
- }
- }
- }
- }
-
- if (processConfig.startMode.fixedInterval !== undefined) {
-
- if (processConfig.startMode.fixedInterval.run === "true") {
-
- botConfig.runAtFixedInterval = true;
- botConfig.fixedInterval = processConfig.startMode.fixedInterval.interval;
-
- let month = pad((new Date()).getUTCMonth() + 1, 2);
- let year = (new Date()).getUTCFullYear();
-
- switch (botConfig.type) {
- case 'Sensor': {
- runSensorBot(botConfig, processConfig, month, year);
- break;
- }
- default: {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
- }
- }
- }
- }
-
- if (processConfig.startMode.userDefined !== undefined) {
-
- if (processConfig.startMode.userDefined.run === "true") {
-
- botConfig.startMode = "User Defined";
- console.log(logDisplace + "Root : [INFO] start -> findProcess -> Process found at the bot configuration file. -> Start Mode = " + botConfig.startMode);
-
- let month = pad((new Date()).getUTCMonth() + 1, 2);
- let year = (new Date()).getUTCFullYear();
-
- if (processConfig.startMode.userDefined.resumeExecution === true) {
- botConfig.hasTheBotJustStarted = false;
- } else {
- botConfig.hasTheBotJustStarted = true;
- }
-
- switch (botConfig.type) {
- case 'Trading Engine': {
- runTradingEngine(botConfig, processConfig);
- break;
- }
- default: {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
- }
- }
- }
- }
-
- } catch (err) {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Unexpected exception. -> err = " + err.stack);
}
}
}
- if (processFound === false) {
+ try {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> Process listed at the configuration file of Task Server not found at the configuration file of the bot.");
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> process = " + global.TASK_NODE.bot.processes[processIndex].code.process);
+ /* We test each type of start Mode to get what to run and how. */
+ if (processConfig.startMode.allMonths !== undefined) {
+
+ if (processConfig.startMode.allMonths.run === "true") {
+
+ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> allMonths start mode detected. "); }
+
+ for (let year = processConfig.startMode.allMonths.maxYear; year >= processConfig.startMode.allMonths.minYear; year--) {
+
+ for (let month = 12; month > 0; month--) {
+
+ let padMonth = pad(month, 2)
+
+ let newInstanceBotConfig = JSON.parse(JSON.stringify(botConfig));
+
+ newInstanceBotConfig.debug = {
+ month: pad(month, 2),
+ year: pad(year, 4)
+ };
+
+ let timeDelay = Math.random() * 10 * 1000; // We introduce a short delay so as to not overload the machine.
+ setTimeout(startProcess, timeDelay);
+
+ function startProcess() {
+
+ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> startProcess -> Ready to start process."); }
+
+ switch (botConfig.type) {
+ case 'Sensor Bot': {
+ runSensorBot(newInstanceBotConfig, processConfig, padMonth, year);
+ break;
+ }
+ case 'Indicator Bot': {
+ runIndicatorBot(newInstanceBotConfig, processConfig, padMonth, year);
+ break;
+ }
+ default: {
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (processConfig.startMode.oneMonth !== undefined) {
+
+ if (processConfig.startMode.oneMonth.run === "true") {
+
+ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> oneMonth start mode detected. "); }
+
+ startProcess();
+
+ function startProcess() {
+
+ if (FULL_LOG === true) { console.log(logDisplace + "Root : [INFO] start -> bootingBot -> startProcess -> Ready to start process."); }
+
+ let month = pad(processConfig.startMode.oneMonth.month, 2);
+ let year = processConfig.startMode.oneMonth.year;
+
+ botConfig.debug = {
+ month: pad(month, 2),
+ year: pad(year, 4)
+ };
+
+ switch (botConfig.type) {
+ case 'Sensor Bot': {
+ runSensorBot(botConfig, processConfig, month, year);
+ break;
+ }
+ case 'Indicator Bot': {
+ runIndicatorBot(botConfig, processConfig, month, year);
+ break;
+ }
+ default: {
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> startProcess -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
+ }
+ }
+ }
+ }
+ }
+
+ if (processConfig.startMode.noTime !== undefined) {
+
+ if (processConfig.startMode.noTime.run === "true") {
+
+ let month = pad((new Date()).getUTCMonth() + 1, 2);
+ let year = (new Date()).getUTCFullYear();
+
+ if (processConfig.startMode.noTime.resumeExecution === true) {
+ botConfig.hasTheBotJustStarted = false;
+ } else {
+ botConfig.hasTheBotJustStarted = true;
+ }
+
+ switch (botConfig.type) {
+ case 'Sensor Bot': {
+ runSensorBot(botConfig, processConfig, month, year);
+ break;
+ }
+ case 'Indicator Bot': {
+ runIndicatorBot(botConfig, processConfig, month, year);
+ break;
+ }
+ case 'Trading Bot': {
+ runTradingBot(botConfig, processConfig);
+ break;
+ }
+ default: {
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
+ }
+ }
+ }
+ }
+
+ if (processConfig.startMode.fixedInterval !== undefined) {
+
+ if (processConfig.startMode.fixedInterval.run === "true") {
+
+ botConfig.runAtFixedInterval = true;
+ botConfig.fixedInterval = processConfig.startMode.fixedInterval.interval;
+
+ let month = pad((new Date()).getUTCMonth() + 1, 2);
+ let year = (new Date()).getUTCFullYear();
+
+ switch (botConfig.type) {
+ case 'Sensor Bot': {
+ runSensorBot(botConfig, processConfig, month, year);
+ break;
+ }
+ default: {
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
+ }
+ }
+ }
+ }
+
+ if (processConfig.startMode.userDefined !== undefined) {
+
+ if (processConfig.startMode.userDefined.run === "true") {
+
+ botConfig.startMode = "User Defined";
+ console.log(logDisplace + "Root : [INFO] start -> bootingBot -> Process found at the bot configuration file. -> Start Mode = " + botConfig.startMode);
+
+ let month = pad((new Date()).getUTCMonth() + 1, 2);
+ let year = (new Date()).getUTCFullYear();
+
+ if (processConfig.startMode.userDefined.resumeExecution === true) {
+ botConfig.hasTheBotJustStarted = false;
+ } else {
+ botConfig.hasTheBotJustStarted = true;
+ }
+
+ switch (botConfig.type) {
+ case 'Trading Bot': {
+ runTradingBot(botConfig, processConfig);
+ break;
+ }
+ default: {
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
+ }
+ }
+ }
+ }
+
+ } catch (err) {
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected exception. -> err = " + err.stack);
}
+
function runSensorBot(pBotConfig, pProcessConfig, pMonth, pYear) {
try {
@@ -418,9 +440,9 @@
logger = DEBUG_MODULE.newDebugLog();
logger.bot = pBotConfig;
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> Entering function."); }
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> pMonth = " + pMonth); }
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> pYear = " + pYear); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> Entering function."); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> pMonth = " + pMonth); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> pYear = " + pYear); }
let extractionBotMainLoop = SENSOR_BOT_MAIN_LOOP_MODULE.newSensorBotProcessMainLoop(pBotConfig, logger);
extractionBotMainLoop.initialize(pProcessConfig, onInitializeReady);
@@ -444,31 +466,31 @@
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
- logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
- logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
+ logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
+ logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
- console.log(logDisplace + "Root : [INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
- console.log(logDisplace + "Root : [INFO] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
+ console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
+ console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
logger.persist();
} else {
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message);
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. ");
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bye.");
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. ");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bye.");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution was aborted.");
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> whenStartFinishes -> Bot execution was aborted.");
logger.persist();
}
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
} else {
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> err = " + err.message);
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> Bot will not be started. ");
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> onInitializeReady -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> Bot will not be started. ");
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> onInitializeReady -> err = " + err.message);
logger.persist();
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
@@ -476,7 +498,7 @@
}
}
catch (err) {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runSensorBot -> err = " + err.stack);
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runSensorBot -> err = " + err.stack);
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
}
@@ -492,9 +514,9 @@
logger = DEBUG_MODULE.newDebugLog();
logger.bot = pBotConfig;
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> Entering function."); }
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pMonth = " + pMonth); }
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> pYear = " + pYear); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> Entering function."); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> pMonth = " + pMonth); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> pYear = " + pYear); }
let indicatorBotMainLoop = INDICATOR_BOT_MAIN_LOOP_MODULE.newIndicatorBotProcessMainLoop(pBotConfig, logger);
indicatorBotMainLoop.initialize(pProcessConfig, onInitializeReady);
@@ -518,41 +540,41 @@
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
- logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
- logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
+ logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
+ logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
- console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
- console.log(logDisplace + "Root : [INFO] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
+ console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
+ console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
logger.persist();
} else {
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message);
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. ");
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bye.");
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs.");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. ");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bye.");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs.");
logger.persist();
}
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
} else {
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = " + err.message);
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> Failed to initialize the bot. ");
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> onInitializeReady -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> Failed to initialize the bot. ");
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> onInitializeReady -> err = " + err.message);
logger.persist();
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
}
}
catch (err) {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runIndicatorBot -> err = " + err.stack);
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runIndicatorBot -> err = " + err.stack);
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
}
- function runTradingEngine(pBotConfig, pProcessConfig) {
+ function runTradingBot(pBotConfig, pProcessConfig) {
global.TOTAL_PROCESS_INSTANCES_CREATED++
@@ -563,16 +585,16 @@
logger = DEBUG_MODULE.newDebugLog();
logger.bot = pBotConfig;
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> Entering function."); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> Entering function."); }
- let tradingEngineMainLoop = TRADING_ENGINE_MAIN_LOOP_MODULE.newTradingEngineProcessMainLoop(pBotConfig, logger);
- tradingEngineMainLoop.initialize(pProcessConfig, onInitializeReady);
+ let tradingBotMainLoop = TRADING_ENGINE_MAIN_LOOP_MODULE.newTradingBotProcessMainLoop(pBotConfig, logger);
+ tradingBotMainLoop.initialize(pProcessConfig, onInitializeReady);
function onInitializeReady(err) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
- tradingEngineMainLoop.run(whenRunFinishes);
+ tradingBotMainLoop.run(whenRunFinishes);
function whenRunFinishes(err) {
@@ -584,42 +606,42 @@
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
- logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
- logger.write(MODULE_NAME, "[INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
+ logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
+ logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
- console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
- console.log(logDisplace + "Root : [INFO] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
+ console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> botId = " + botId);
+ console.log(logDisplace + "Root : [INFO] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully.");
logger.persist();
} else {
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> err = " + err.message);
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. ");
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bye.");
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs.");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. ");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bye.");
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId);
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs.");
logger.persist();
}
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
} else {
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> err = " + err.message);
- logger.write(MODULE_NAME, "[ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> Failed to initialize the bot. ");
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> onInitializeReady -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> err = " + err.message);
+ logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> Failed to initialize the bot. ");
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> onInitializeReady -> err = " + err.message);
logger.persist();
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
}
}
catch (err) {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> runTradingEngine -> err = " + err.stack);
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runTradingBot -> err = " + err.stack);
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
}
}
catch (err) {
- console.log(logDisplace + "Root : [ERROR] start -> findProcess -> err = " + err.stack);
+ console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> err = " + err.stack);
setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START)
}
}
diff --git a/SensorBotProcessMainLoop.js b/SensorBotProcessMainLoop.js
index 3d20a878d..29e6ab980 100644
--- a/SensorBotProcessMainLoop.js
+++ b/SensorBotProcessMainLoop.js
@@ -35,7 +35,13 @@
processConfig = pProcessConfig;
- let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name;
+ if (bot.definedByUI === true) {
+ /* The code of the bot is defined at the UI. No need to load a file with the code. */
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+ return
+ }
+
+ let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName
filePath += "/User.Bot.js"
fileStorage.getTextFile(bot.devTeam, filePath, onBotDownloaded);
@@ -45,6 +51,7 @@
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message);
+ parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
@@ -130,6 +137,8 @@
const STATUS_REPORT = require(ROOT_DIR + 'StatusReport');
const EXCHANGE_API = require('@superalgos/exchange-gateway');
const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies');
+ const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents');
+ const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput');
/* 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. */
@@ -140,15 +149,165 @@
console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + pad(pMonth, 2) + "/" + pad(pYear, 4)
+ " 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. */
+ /* We will prepare first the infraestructure needed for the bot to run. There are 4 modules we need to sucessfullly initialize first. */
+ let processOutput
+ let processExecutionEvents
let userBot;
let statusDependencies;
let exchangeAPI;
let nextWaitTime;
- initializeStatusDependencies();
+ initializeProcessExecutionEvents();
+
+ function initializeProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger)
+
+ processExecutionEvents.initialize(pMonth, pYear, onInizialized);
+
+ function onInizialized(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well.");
+ startProcessExecutionEvents()
+ 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, "[WARN] run -> loop -> initializeProcessExecutionEvents -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> 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 -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + 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 -> initializeProcessExecutionEvents -> 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 -> initializeProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
+ function startProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents.start(onStarted);
+
+ function onStarted(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well.");
+ initializeStatusDependencies();
+ 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, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> 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 -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + 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 -> startProcessExecutionEvents -> onStarted -> 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 -> startProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
function initializeStatusDependencies() {
@@ -158,7 +317,7 @@
statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES);
- statusDependencies.initialize(processConfig.statusDependencies, pMonth, pYear, onInizialized);
+ statusDependencies.initialize(pMonth, pYear, onInizialized);
function onInizialized(err) {
@@ -427,8 +586,7 @@
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write(MODULE_NAME, "[INFO] run -> loop -> startUserBot -> onFinished > Execution finished well.");
- nextWaitTime = 'Normal';
- loopControl(nextWaitTime);
+ raiseEventsProcessOutput()
return;
}
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
@@ -541,6 +699,155 @@
}
}
+ function raiseEventsProcessOutput() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); }
+
+ processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger)
+
+ processOutput.raiseEvents(statusDependencies, onFinished);
+
+ function onFinished(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well.");
+ finishProcessExecutionEvents()
+ 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, "[WARN] run -> loop -> raiseEventsProcessOutput -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> 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 -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + 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 -> raiseEventsProcessOutput -> 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 -> raiseEventsProcessOutput -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
+ function finishProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents.finish(onFinished);
+
+ function onFinished(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well.");
+ nextWaitTime = 'Normal';
+ loopControl(nextWaitTime);
+ 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, "[WARN] run -> loop -> finishProcessExecutionEvents -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> 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 -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + 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 -> finishProcessExecutionEvents -> 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 -> finishProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
function loopControl(nextWaitTime) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); }
diff --git a/Session.js b/Session.js
index 2f7b77ab2..c259426ed 100644
--- a/Session.js
+++ b/Session.js
@@ -44,7 +44,7 @@
return
function runSession(message) {
-
+
/* We are going to run the Definition comming at the event. */
bot.DEFINITION = JSON.parse(message.event.definition)
bot.SESSION = JSON.parse(message.event.session)
@@ -491,9 +491,9 @@
if (socialBot.type === code.botType && socialBot.id === code.botId) {
if (socialBot.type === "Telegram Bot") {
if (announcement.formulaValue !== undefined) {
- socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue)
+ socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err))
} else {
- socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text)
+ socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err))
}
}
}
@@ -523,7 +523,7 @@
const Telegram = require('telegraf/telegram')
let telegramAPI
telegramAPI = new Telegram(botToken)
- telegramAPI.sendMessage(chatId, bot.SESSION.type + " '" + bot.SESSION.name + "' was Started.")
+ telegramAPI.sendMessage(chatId, bot.SESSION.type + " '" + bot.SESSION.name + "' was Started.").catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpTelegramBot -> Telegram API error -> err = " + err))
let botInstance = {
telegramBot: telegramBot,
diff --git a/StatusDependencies.js b/StatusDependencies.js
index a0eaa4374..542e25eec 100644
--- a/StatusDependencies.js
+++ b/StatusDependencies.js
@@ -2,40 +2,78 @@
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
-
const MODULE_NAME = "Status Dependencies";
+ let bot = BOT
+
let thisObject = {
- config: undefined,
+ nodeArray: undefined,
statusReports: new Map(),
+ reportsByMainUtility: new Map(),
initialize: initialize,
keys: []
};
return thisObject;
- function initialize(pStatusDependenciesConfig, pMonth, pYear, callBackFunction) {
+ function initialize(pMonth, pYear, callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
- thisObject.config = pStatusDependenciesConfig;
+ /* Basic Valdidations */
+ if (bot.processNode.referenceParent.processDependencies !== undefined) {
+ if (bot.processNode.referenceParent.processDependencies.statusDependencies !== undefined) {
+ thisObject.nodeArray = bot.processNode.referenceParent.processDependencies.statusDependencies
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have status dependencies at all.");
+ callBackFunction(global.DEFAULT_OK_RESPONSE)
+ return
+ }
+ } else {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not status dependencies.");
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE)
+ return
+ }
- /*
+ /*For each dependency we will initialize the status report, and load it as part of this initialization process.*/
- For each dependency declared at the bot config, we will initialize the status report, and load it as part of this initialization process.
-
- */
let alreadyCalledBack = false;
let loadCount = 0;
+ let dependenciesToProcess = []
+ for (let i = 0; i < thisObject.nodeArray.length; i++) {
+ let statusDependency = {
+ dependency: thisObject.nodeArray[i],
+ month: pMonth,
+ year: pYear
+ }
+
+ dependenciesToProcess.push(statusDependency)
- for (let i = 0; i < pStatusDependenciesConfig.length; i++) {
+ /*
+ For the special case that the statusDependency is a Self Reference AND this process is sliced in different
+ instances for each month, we will add the main self reference status report that is month/year independent.
+ */
+
+ if (statusDependency.dependency.code.mainUtility === "Self Reference" && pMonth !== undefined && pYear !== undefined) {
+ let mainDependency = {
+ dependency: thisObject.nodeArray[i],
+ month: undefined,
+ year: undefined
+ }
+
+ dependenciesToProcess.push(mainDependency)
+ }
+ }
+
+ for (let i = 0; i < dependenciesToProcess.length; i++) {
let statusReportModule = STATUS_REPORT.newStatusReport(BOT, logger, UTILITIES);
logger.write(MODULE_NAME, "[INFO] initialize -> onInitilized -> Initializing Status Report # " + (i + 1));
- statusReportModule.initialize(pStatusDependenciesConfig[i], pMonth, pYear, onInitilized);
+ let statusDependency = dependenciesToProcess[i]
+ statusReportModule.initialize(statusDependency.dependency, statusDependency.month, statusDependency.year, onInitilized);
function onInitilized(err) {
@@ -66,8 +104,7 @@
switch (err.message) {
case global.DEFAULT_OK_RESPONSE.message: {
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> bot = " + pStatusDependenciesConfig[i].bot); }
- if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> process = " + pStatusDependenciesConfig[i].process); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onLoad -> Execution finished well. -> Status Dependency = " + JSON.stringify(statusDependency.dependency)) }
addReport();
return;
@@ -75,8 +112,8 @@
case "Status Report was never created.": {
logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack);
- logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> bot = " + pStatusDependenciesConfig[i].bot);
- logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> process = " + pStatusDependenciesConfig[i].process);
+ logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(statusDependency.dependency))
+
addReport();
return;
}
@@ -84,8 +121,8 @@
case "Status Report is corrupt.": {
logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> err = " + err.stack);
- logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> bot = " + pStatusDependenciesConfig[i].bot);
- logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> process = " + pStatusDependenciesConfig[i].process);
+ logger.write(MODULE_NAME, "[WARN] initialize -> onLoad -> Report Not Found. -> Status Dependency = " + JSON.stringify(statusDependency.dependency))
+
addReport();
return;
}
@@ -117,18 +154,22 @@
let key;
- if (pStatusDependenciesConfig[i].dataSetSection === "Month") {
- key = pStatusDependenciesConfig[i].devTeam + "-" + pStatusDependenciesConfig[i].bot + "-" + pStatusDependenciesConfig[i].process + "-" + pStatusDependenciesConfig[i].dataSetVersion + "-" + pYear + "-" + pMonth;
+ if (statusDependency.dependency.processRunMonthly === true && statusDependency.month !== undefined && statusDependency.year !== undefined) {
+ key = statusDependency.dependency.devTeam + "-" + statusDependency.dependency.bot + "-" + statusDependency.dependency.process + "-" + statusDependency.dependency.dataSetVersion + "-" + statusDependency.year + "-" + statusDependency.month;
} else {
- key = pStatusDependenciesConfig[i].devTeam + "-" + pStatusDependenciesConfig[i].bot + "-" + pStatusDependenciesConfig[i].process + "-" + pStatusDependenciesConfig[i].dataSetVersion;
+ key = statusDependency.dependency.devTeam + "-" + statusDependency.dependency.bot + "-" + statusDependency.dependency.process + "-" + statusDependency.dependency.dataSetVersion;
}
thisObject.keys.push(key);
thisObject.statusReports.set(key, statusReportModule);
+ if (statusReportModule.mainUtility !== undefined) {
+ thisObject.reportsByMainUtility.set(statusReportModule.mainUtility, statusReportModule)
+ }
+
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> addReport -> Report added to Map. -> key = " + key); }
- if (loadCount === pStatusDependenciesConfig.length) {
+ if (loadCount === dependenciesToProcess.length) {
if (alreadyCalledBack === false) {
alreadyCalledBack = true
callBackFunction(global.DEFAULT_OK_RESPONSE);
diff --git a/StatusReport.js b/StatusReport.js
index 4096976a4..239f382b7 100644
--- a/StatusReport.js
+++ b/StatusReport.js
@@ -13,6 +13,7 @@
let bot = BOT;
let thisObject = {
+ mainUtility: undefined,
file: undefined, // Here we have the JSON object representing the file content.
initialize: initialize,
load: load,
@@ -21,7 +22,7 @@
verifyMarketComplete: verifyMarketComplete
};
- let owner; // This is the bot owner of the Status Report. Only owners can save the report and override the existing content.
+ let statusDependencyNode;
/* Utilities needed. */
@@ -39,63 +40,103 @@
return thisObject;
- function initialize(pOwner, pMonth, pYear, callBackFunction) {
+ function initialize(pStatusDependencyNode, pMonth, pYear, callBackFunction) {
try {
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
- logger.fileName = MODULE_NAME + "." + pOwner.bot + "." + pOwner.process;
+ statusDependencyNode = pStatusDependencyNode;
+ logger.fileName = MODULE_NAME + "." + statusDependencyNode.type + "." + statusDependencyNode.name + "." + statusDependencyNode.id;
- owner = pOwner;
month = pMonth;
year = pYear;
- if (bot.SESSION !== undefined && pOwner.type === "Trading Engine") {
+ /* Some very basic validations that we have all the information needed. */
+ if (statusDependencyNode.referenceParent === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Status Dependency without Reference Parent. Status Dependency = " + JSON.stringify(statusDependencyNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (statusDependencyNode.referenceParent.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Status Report not attached to a Process Definition. Status Report = " + JSON.stringify(statusDependencyNode.referenceParent));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (statusDependencyNode.referenceParent.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition witn no codeName defined. Process Definition = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (statusDependencyNode.referenceParent.parentNode.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Process Definition not attached to a Bot. Process Definition = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (statusDependencyNode.referenceParent.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Bot witn no codeName defined. Bot = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (statusDependencyNode.referenceParent.parentNode.parentNode.parentNode === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Bot not attached to a Team. Bot = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ if (statusDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName === undefined) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> Team witn no codeName defined. Team = " + JSON.stringify(statusDependencyNode.referenceParent.parentNode.parentNode.parentNode));
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+
+ /* Simplifying the access to basic info */
+ statusDependencyNode.bot = statusDependencyNode.referenceParent.parentNode.parentNode.code.codeName
+ statusDependencyNode.process = statusDependencyNode.referenceParent.parentNode.code.codeName
+ statusDependencyNode.type = statusDependencyNode.referenceParent.parentNode.parentNode.type
+ statusDependencyNode.devTeam = statusDependencyNode.referenceParent.parentNode.parentNode.parentNode.code.codeName
+
+ /* Let's see if the process is run monthly or not. */
+ if (statusDependencyNode.referenceParent.parentNode.code.startMode !== undefined) {
+ if (statusDependencyNode.referenceParent.parentNode.code.startMode.allMonths !== undefined) {
+ if (statusDependencyNode.referenceParent.parentNode.code.startMode.oneMonth !== undefined) {
+ if (statusDependencyNode.referenceParent.parentNode.code.startMode.allMonths.run === "true" || statusDependencyNode.referenceParent.parentNode.code.startMode.oneMonth.run === "true") {
+ statusDependencyNode.processRunMonthly = true
+ }
+ }
+ }
+ }
+
+ /* We retrieve the report main utility */
+ if (statusDependencyNode.code !== undefined) {
+ if (statusDependencyNode.code.mainUtility !== undefined) {
+ thisObject.mainUtility = statusDependencyNode.code.mainUtility
+ }
+ }
+
+ /* This stuff is still hardcoded and unresolved. */
+ statusDependencyNode.botVersion = {
+ "major": 1,
+ "minor": 0
+ }
+ statusDependencyNode.dataSetVersion = "dataSet.V1"
+
+
+
+ if (bot.SESSION !== undefined && statusDependencyNode.type === "Trading Bot Instance") {
sessionPath = bot.SESSION.folderName + "/"
}
- if (owner.dataSetSection === "Month") {
- logger.fileName = MODULE_NAME + "." + pOwner.bot + "." + pOwner.process + "." + year + "." + month;
+ if (statusDependencyNode.processRunMonthly === true && month !== undefined && year !== undefined) {
+ logger.fileName = MODULE_NAME + "." + statusDependencyNode.bot + "." + statusDependencyNode.process + "." + year + "." + month;
timePath = "/" + year + "/" + month;
}
- if (owner.waitUntilNextUpdate === true) {
-
- if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> waitUntilNextUpdate -> Entering Code Block."); }
-
- /* This forces this process to wait until the process that this one depends on, updates its status report. */
-
- let extraCallerId = ''
- if (pMonth) { extraCallerId = extraCallerId + '-' + pMonth }
- if (pYear) { extraCallerId = extraCallerId + '-' + pYear }
-
- let key = owner.devTeam + "-" + owner.bot + "-" + owner.process
- let callerId = bot.devTeam + "-" + bot.codeName + "-" + bot.process + extraCallerId
-
- let subscriptionIdStatusReport
-
- global.SYSTEM_EVENT_HANDLER.listenToEvent(key, 'Status Report Updated', undefined, callerId, responseCallBack, eventsCallBack)
-
- function responseCallBack(message) {
- if (message.result !== global.DEFAULT_OK_RESPONSE.result) {
- logger.write(MODULE_NAME, "[ERROR] initialize -> responseCallBack -> message = " + JSON.stringify(message))
- callBackFunction(global.DEFAULT_FAIL_RESPONSE);
- } else {
- subscriptionIdStatusReport = message.eventSubscriptionId
- }
- }
-
- function eventsCallBack() {
- if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> eventsCallBack -> Entering function."); }
-
- /* We continue the normal flow after we learn the dependent process has updated its status report. */
- global.SYSTEM_EVENT_HANDLER.stopListening(key, 'Status Report Updated', subscriptionIdStatusReport)
- callBackFunction(global.DEFAULT_OK_RESPONSE);
- }
- } else {
- /* In this case, the Status Report does not depends on a process which needs to wait for. */
- callBackFunction(global.DEFAULT_OK_RESPONSE);
- }
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack);
@@ -112,23 +153,23 @@
let fileName = "Status.Report." + global.MARKET.assetA + '_' + global.MARKET.assetB + ".json";
let filePath;
- let ownerId = owner.devTeam + "-" + owner.bot + "-" + owner.botVersion.major + "-" + owner.botVersion.minor + "-" + owner.process + "-" + owner.dataSetVersion;
+ let ownerId = statusDependencyNode.devTeam + "-" + statusDependencyNode.bot + "-" + statusDependencyNode.botVersion.major + "-" + statusDependencyNode.botVersion.minor + "-" + statusDependencyNode.process + "-" + statusDependencyNode.dataSetVersion;
let botId = bot.devTeam + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.process + "-" + bot.dataSetVersion;
if (ownerId !== botId) {
- let rootPath = owner.devTeam + "/" + owner.bot + "." + owner.botVersion.major + "." + owner.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + owner.dataSetVersion;
- filePath = rootPath + "/Reports/" + sessionPath + owner.process + timePath;
+ let rootPath = statusDependencyNode.devTeam + "/" + statusDependencyNode.bot + "." + statusDependencyNode.botVersion.major + "." + statusDependencyNode.botVersion.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + statusDependencyNode.dataSetVersion;
+ filePath = rootPath + "/Reports/" + sessionPath + statusDependencyNode.process + timePath;
} else {
- filePath = bot.filePathRoot + "/Reports/" + sessionPath + owner.process + timePath;
+ filePath = bot.filePathRoot + "/Reports/" + sessionPath + statusDependencyNode.process + timePath;
}
- if (owner.type === 'Trading') {
+ if (statusDependencyNode.type === 'Trading') {
fileName = bot.startMode + "." + fileName;
}
filePath += '/' + fileName
- fileStorage.getTextFile(owner.devTeam, filePath, onFileReceived);
+ fileStorage.getTextFile(statusDependencyNode.devTeam, filePath, onFileReceived);
function onFileReceived(err, text) {
@@ -196,10 +237,10 @@
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] save -> Entering function."); }
- let ownerId = owner.devTeam + "-" + owner.bot + "-" + owner.botVersion.major + "-" + owner.botVersion.minor + "-" + owner.process + "-" + owner.dataSetVersion;
+ let ownerId = statusDependencyNode.devTeam + "-" + statusDependencyNode.bot + "-" + statusDependencyNode.botVersion.major + "-" + statusDependencyNode.botVersion.minor + "-" + statusDependencyNode.process + "-" + statusDependencyNode.dataSetVersion;
let botId = bot.devTeam + "-" + bot.codeName + "-" + bot.version.major + "-" + bot.version.minor + "-" + bot.process + "-" + bot.dataSetVersion;
- if (ownerId !== botId && owner.process !== "Context") { // Context is a special case where the report is created by the Context.js module itself.
+ if (ownerId !== botId && statusDependencyNode.process !== "Context") { // Context is a special case where the report is created by the Context.js module itself.
let customErr = {
result: global.CUSTOM_FAIL_RESPONSE.result,
@@ -211,7 +252,7 @@
}
let fileName = "Status.Report." + global.MARKET.assetA + '_' + global.MARKET.assetB + ".json";
- let filePath = bot.filePathRoot + "/Reports/" + sessionPath + owner.process + timePath;
+ let filePath = bot.filePathRoot + "/Reports/" + sessionPath + statusDependencyNode.process + timePath;
if (bot.type === 'Trading' ) {
fileName = bot.startMode + "." + fileName;
@@ -220,7 +261,7 @@
filePath += '/' + fileName
let fileContent = JSON.stringify(thisObject.file);
- fileStorage.createTextFile(owner.devTeam, filePath, fileContent + '\n', onFileCreated);
+ fileStorage.createTextFile(statusDependencyNode.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
@@ -236,30 +277,6 @@
logger.write(MODULE_NAME, "[INFO] save -> onFileCreated -> Content written = " + fileContent);
}
- /* Here we raise the event stating that this status report was updated. */
- let key = bot.devTeam + "-" + bot.codeName + "-" + bot.process
- global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Status Report Updated')
-
- /* We will also reaise the events for the datasets impacted by the process that just finished. */
- for (let j = 0; j < bot.processes.length; j++) {
- let process = bot.processes[j]
- if (process.name === bot.process) {
- let updatesDatasets = process.updatesDatasets
- if (updatesDatasets !== undefined) {
- for (let i = 0; i < updatesDatasets.length; i++) {
- let updatedDataSet = updatesDatasets[i]
-
- key = bot.devTeam + "-" + bot.codeName + "-" + updatedDataSet.product + "-" + updatedDataSet.dataSet
- let event = {
- lastFile: thisObject.file.lastFile
- }
- global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Dataset Updated', event)
-
- }
- }
- }
- }
-
callBackFunction(global.DEFAULT_OK_RESPONSE);
return;
}
diff --git a/TradingBot.js b/TradingBot.js
new file mode 100644
index 000000000..c04bad9d1
--- /dev/null
+++ b/TradingBot.js
@@ -0,0 +1,973 @@
+exports.newTradingBot = function newTradingBot(bot, logger, UTILITIES, FILE_STORAGE) {
+
+ const FULL_LOG = true;
+ const LOG_FILE_CONTENT = false;
+ const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
+
+ const MODULE_NAME = "Trading Bot";
+
+ let thisObject = {
+ initialize: initialize,
+ finalize: finalize,
+ start: start
+ };
+
+ let utilities = UTILITIES.newCloudUtilities(logger);
+ let fileStorage = FILE_STORAGE.newFileStorage(logger);
+
+ const COMMONS = require('./Commons.js');
+ let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE);
+
+ let assistant;
+
+ return thisObject;
+
+ function finalize() {
+ thisObject = undefined
+ utilities = undefined
+ fileStorage = undefined
+ commons = undefined
+ }
+
+ function initialize(pAssistant, callBackFunction) {
+
+ try {
+
+ logger.fileName = MODULE_NAME;
+ logger.initialize();
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
+
+ assistant = pAssistant;
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function start(multiPeriodDataFiles, timePeriod, timePeriodLabel, currentDay, interExecutionMemory, callBackFunction) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
+
+ let chart = {}
+ let mainDependency = {}
+ let processingDailyFiles
+
+ if (currentDay !== undefined) {
+ processingDailyFiles = true
+ } else {
+ processingDailyFiles = false
+ }
+
+ /* The first phase here is about checking that we have everything we need at the definition level. */
+ let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies
+ if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) {return}
+
+ let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets
+ if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return }
+
+ /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */
+ for (let j = 0; j < global.marketFilesPeriods.length; j++) {
+
+ let timePeriodLabel = marketFilesPeriods[j][1]
+ let dataFiles = multiPeriodDataFiles.get(timePeriodLabel)
+ let products = {}
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Inflating Data Files for timePeriod = " + timePeriodLabel); }
+
+ if (dataFiles !== undefined) {
+ commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod)
+
+ let propertyName = 'at' + timePeriodLabel.replace('-', '');
+ chart[propertyName] = products
+ }
+ }
+
+ for (let j = 0; j < global.dailyFilePeriods.length; j++) {
+
+ let timePeriodLabel = global.dailyFilePeriods[j][1]
+ let dataFiles = multiPeriodDataFiles.get(timePeriodLabel)
+ let products = {}
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Inflating Data Files for timePeriod = " + timePeriodLabel); }
+
+ if (dataFiles !== undefined) {
+ commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timePeriod)
+
+ let propertyName = 'at' + timePeriodLabel.replace('-', '');
+ chart[propertyName] = products
+ }
+ }
+
+ /* From here we have more or less the same we used to have at JASON */
+ const TRADING_SIMULATION = require('./TradingSimulation.js');
+ let tradingSimulation = TRADING_SIMULATION.newTradingSimulation(bot, logger, UTILITIES);
+
+ let market = global.MARKET;
+
+ const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation";
+ const CONDITIONS_FOLDER_NAME = "Simulation-Conditions";
+ const STRATEGIES_FOLDER_NAME = "Simulation-Strategies";
+ const TRADES_FOLDER_NAME = "Simulation-Trades";
+
+ const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
+
+ let recordsArray
+ let conditionsArray
+ let strategiesArray
+ let tradesArray
+
+ let tradingSystem = {};
+
+ tradingSimulation.runSimulation(
+ chart,
+ dataDependencies,
+ timePeriod,
+ timePeriodLabel,
+ currentDay,
+ interExecutionMemory,
+ assistant,
+ writeFiles,
+ callBackFunction)
+
+ function writeFiles(pTradingSystem, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray) {
+
+ tradingSystem = pTradingSystem
+ recordsArray = pRecordsArray
+ conditionsArray = pConditionsArray
+ strategiesArray = pStrategiesArray
+ tradesArray = pTradesArray
+
+ if (timePeriod > global.dailyFilePeriods[0][0]) {
+ writeMarketFiles()
+ } else {
+ writeDailyFiles()
+ }
+ }
+
+ function writeMarketFiles() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); }
+
+ writeRecordsFile();
+
+ function writeRecordsFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < recordsArray.length; i++) {
+
+ let record = recordsArray[i];
+
+ fileContent = fileContent + separator + '[' +
+ record.begin + "," +
+ record.end + "," +
+ record.type + "," +
+ record.marketRate + "," +
+ record.amount + "," +
+ record.balanceA + "," +
+ record.balanceB + "," +
+ record.profit + "," +
+ record.lastTradeProfitLoss + "," +
+ record.stopLoss + "," +
+ record.roundtrips + "," +
+ record.hits + "," +
+ record.fails + "," +
+ record.hitRatio + "," +
+ record.ROI + "," +
+ record.periods + "," +
+ record.days + "," +
+ record.anualizedRateOfReturn + "," +
+ record.positionRate + "," +
+ record.lastTradeROI + "," +
+ record.strategy + "," +
+ record.strategyStageNumber + "," +
+ record.takeProfit + "," +
+ record.stopLossPhase + "," +
+ record.takeProfitPhase + "," +
+ JSON.stringify(record.executionRecord) + "," +
+ record.positionSize + "," +
+ record.initialBalanceA + "," +
+ record.minimumBalanceA + "," +
+ record.maximumBalanceA + "," +
+ record.initialBalanceB + "," +
+ record.minimumBalanceB + "," +
+ record.maximumBalanceB + "," +
+ record.baseAsset + "," +
+ record.positionPeriods + "," +
+ record.positionDays + "," +
+ record.distanceToLastTriggerOn + "," +
+ record.distanceToLastTriggerOff + "," +
+ record.distanceToLastTakePosition + "," +
+ record.distanceToLastClosePosition + "]";
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ writeConditionsFile();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeRecordsFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeConditionsFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < conditionsArray.length; i++) {
+
+ let record = conditionsArray[i];
+
+ let conditions = "";
+ let conditionsSeparator = "";
+
+ for (let j = 0; j < record.length - 3; j++) {
+ conditions = conditions + conditionsSeparator + record[j];
+ if (conditionsSeparator === "") { conditionsSeparator = ","; }
+ }
+
+ conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values.
+ conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors.
+ conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues.
+
+ fileContent = fileContent + separator + '[' + conditions + ']';
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+ fileContent = "[" + JSON.stringify(tradingSystem) + "," + fileContent + "]";
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ writeStrategiesFile();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeConditionsFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeStrategiesFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < strategiesArray.length; i++) {
+ let record = strategiesArray[i];
+
+ fileContent = fileContent + separator + '[' +
+ record.begin + "," +
+ record.end + "," +
+ record.status + "," +
+ record.number + "," +
+ record.beginRate + "," +
+ record.endRate + "]";
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ writeTradesFile();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeStrategiesFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeTradesFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < tradesArray.length; i++) {
+
+ let record = tradesArray[i];
+ if (record.stopRate === undefined) { record.stopRate = 0 }
+
+ fileContent = fileContent + separator + '[' +
+ record.begin + "," +
+ record.end + "," +
+ record.status + "," +
+ record.lastTradeROI + "," +
+ record.beginRate + "," +
+ record.endRate + "," +
+ record.exitType + "]";
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timePeriodLabel;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ /* Emit event that signals that simulation files were updated */
+ global.SYSTEM_EVENT_HANDLER.raiseEvent('Jason-Multi-Period', 'Simulation Files Updated')
+
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeMarketFiles -> writeTradesFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ }
+
+ function writeDailyFiles() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> Entering function."); }
+
+ writeRecordsFile();
+
+ /* TODO : NEXT function is a work in progress, not used today */
+ function writeFinalResults() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> Entering function."); }
+
+ if ( // We only write this file if we are at the head of the market.
+ Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS
+ !==
+ Math.trunc((new Date()).valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS
+ ) {
+ writeRecordsFile();
+ return;
+ }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < recordsArray.length; i++) {
+
+ let record = recordsArray[i];
+
+ /* Will only add to the file the records of the current day */
+
+ if (record.begin < currentDay.valueOf()) { continue; }
+
+ fileContent = fileContent + separator + '[' +
+ record.begin + "," +
+ record.end + "," +
+ record.type + "," +
+ record.marketRate + "," +
+ record.amount + "," +
+ record.balanceA + "," +
+ record.balanceB + "," +
+ record.profit + "," +
+ record.lastTradeProfitLoss + "," +
+ record.stopLoss + "," +
+ record.roundtrips + "," +
+ record.hits + "," +
+ record.fails + "," +
+ record.hitRatio + "," +
+ record.ROI + "," +
+ record.periods + "," +
+ record.days + "," +
+ record.anualizedRateOfReturn + "," +
+ record.positionRate + "," +
+ record.lastTradeROI + "," +
+ record.strategy + "," +
+ record.strategyStageNumber + "," +
+ record.takeProfit + "," +
+ record.stopLossPhase + "," +
+ record.takeProfitPhase + "," +
+ JSON.stringify(record.executionRecord) + "," +
+ record.positionSize + "," +
+ record.initialBalanceA + "," +
+ record.minimumBalanceA + "," +
+ record.maximumBalanceA + "," +
+ record.initialBalanceB + "," +
+ record.minimumBalanceB + "," +
+ record.maximumBalanceB + "," +
+ record.baseAsset + "," +
+ record.positionPeriods + "," +
+ record.positionDays + "]";
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ writeRecordsFile();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeFinalResults -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeRecordsFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < recordsArray.length; i++) {
+
+ let record = recordsArray[i];
+
+ /* Will only add to the file the records of the current day */
+
+ if (record.begin < currentDay.valueOf()) { continue; }
+
+ fileContent = fileContent + separator + '[' +
+ record.begin + "," +
+ record.end + "," +
+ record.type + "," +
+ record.marketRate + "," +
+ record.amount + "," +
+ record.balanceA + "," +
+ record.balanceB + "," +
+ record.profit + "," +
+ record.lastTradeProfitLoss + "," +
+ record.stopLoss + "," +
+ record.roundtrips + "," +
+ record.hits + "," +
+ record.fails + "," +
+ record.hitRatio + "," +
+ record.ROI + "," +
+ record.periods + "," +
+ record.days + "," +
+ record.anualizedRateOfReturn + "," +
+ record.positionRate + "," +
+ record.lastTradeROI + "," +
+ record.strategy + "," +
+ record.strategyStageNumber + "," +
+ record.takeProfit + "," +
+ record.stopLossPhase + "," +
+ record.takeProfitPhase + "," +
+ JSON.stringify(record.executionRecord) + "," +
+ record.positionSize + "," +
+ record.initialBalanceA + "," +
+ record.minimumBalanceA + "," +
+ record.maximumBalanceA + "," +
+ record.initialBalanceB + "," +
+ record.minimumBalanceB + "," +
+ record.maximumBalanceB + "," +
+ record.baseAsset + "," +
+ record.positionPeriods + "," +
+ record.positionDays + "]";
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+
+ let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ writeConditionsFile();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeRecordsFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeConditionsFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < conditionsArray.length; i++) {
+
+ let record = conditionsArray[i];
+
+ /* Will only add to the file the records of the current day */
+
+ if (record.begin < currentDay.valueOf()) { continue; }
+
+ let conditions = "";
+ let conditionsSeparator = "";
+
+ /* Will only add to the file the records of the current day */
+
+ if (record.begin < currentDay.valueOf()) { continue; }
+
+ for (let j = 0; j < record.length - 3; j++) {
+ conditions = conditions + conditionsSeparator + record[j];
+ if (conditionsSeparator === "") { conditionsSeparator = ","; }
+ }
+
+ conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values.
+ conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors.
+ conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues.
+
+ fileContent = fileContent + separator + '[' + conditions + ']';
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+ fileContent = "[" + JSON.stringify(tradingSystem) + "," + fileContent + "]";
+
+ let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ writeStrategiesFile();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeConditionsFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeStrategiesFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < strategiesArray.length; i++) {
+ let record = strategiesArray[i];
+
+ /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends
+ at the current date. */
+
+ if (record.end < currentDay.valueOf()) { continue; }
+
+ fileContent = fileContent + separator + '[' +
+ record.begin + "," +
+ record.end + "," +
+ record.status + "," +
+ record.number + "," +
+ record.beginRate + "," +
+ record.endRate + "]";
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+
+ let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ writeTradesFile();
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeStrategiesFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ function writeTradesFile() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> Entering function."); }
+
+ let separator = "";
+ let fileRecordCounter = 0;
+
+ let fileContent = "";
+
+ for (let i = 0; i < tradesArray.length; i++) {
+
+ let record = tradesArray[i];
+
+ /* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends
+ at the current date. */
+
+ if (record.end < currentDay.valueOf()) { continue; }
+ if (record.stopRate === undefined) { record.stopRate = 0 }
+
+ fileContent = fileContent + separator + '[' +
+ record.begin + "," +
+ record.end + "," +
+ record.status + "," +
+ record.lastTradeROI + "," +
+ record.beginRate + "," +
+ record.endRate + "," +
+ record.exitType + "]";
+
+ if (separator === "") { separator = ","; }
+
+ fileRecordCounter++;
+
+ }
+
+ fileContent = "[" + fileContent + "]";
+
+ let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
+ let fileName = '' + market.assetA + '_' + market.assetB + '.json';
+
+ let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
+ let filePath = filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + timePeriodLabel + "/" + dateForPath;
+ filePath += '/' + fileName
+
+ fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
+
+ function onFileCreated(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> Entering function."); }
+ if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); }
+
+ if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
+
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> filePath = " + filePath);
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
+
+ callBackFunction(err);
+ return;
+
+ }
+
+ /* Emit event that signals that simulation files were updated */
+ global.SYSTEM_EVENT_HANDLER.raiseEvent('Jason-Multi-Period', 'Simulation Files Updated')
+
+ callBackFunction(global.DEFAULT_OK_RESPONSE);
+
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> onFileCreated -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> writeDailyFiles -> writeTradesFile -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+};
+
diff --git a/TradingEngineProcessMainLoop.js b/TradingBotProcessMainLoop.js
similarity index 75%
rename from TradingEngineProcessMainLoop.js
rename to TradingBotProcessMainLoop.js
index efc4c8143..c4230dbde 100644
--- a/TradingEngineProcessMainLoop.js
+++ b/TradingBotProcessMainLoop.js
@@ -1,12 +1,12 @@
-exports.newTradingEngineProcessMainLoop = function newTradingEngineProcessMainLoop(bot, parentLogger) {
+exports.newTradingBotProcessMainLoop = function newTradingBotProcessMainLoop(bot, parentLogger) {
const ROOT_DIR = './';
- const MODULE_NAME = "Trading Engine Process Main Loop";
+ const MODULE_NAME = "Trading Process Main Loop";
const FULL_LOG = true;
let USER_BOT_MODULE;
- let COMMONS_MODULE;
+ let USER_BOT_COMMONS;
const MULTI_PERIOD_MARKET = require(ROOT_DIR + 'MultiPeriodMarket');
const MULTI_PERIOD_DAILY = require(ROOT_DIR + 'MultiPeriodDaily');
@@ -40,9 +40,20 @@
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
processConfig = pProcessConfig;
+
+ if (bot.definedByUI === true) {
+ /* The code of the bot is defined at the UI. No need to load a file with the code. */
+ session.initialize(onSessionInitialized)
+
+ function onSessionInitialized(err) {
+ callBackFunction(err);
+ }
+ return
+ }
+
bot.resumeExecution = processConfig.framework.startDate.resumeExecution // We are inherating this from root, but from here we need it at bot
- let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name
+ let filePath = bot.devTeam + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.codeName
filePath += "/User.Bot.js"
fileStorage.getTextFile(bot.devTeam, filePath, onBotDownloaded);
@@ -52,6 +63,7 @@
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> err = " + err.message);
+ parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> filePath = " + filePath);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
@@ -73,9 +85,9 @@
return;
}
- COMMONS_MODULE = {}
- //COMMONS_MODULE.newCommons = require(process.env.BOTS_PATH + '/aamasters/AAMasters/bots/AAJason-Trading-Engine-Bot/Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work.
- COMMONS_MODULE.newCommons = eval(text); // Use this for production
+ USER_BOT_COMMONS = {}
+ //USER_BOT_COMMONS.newCommons = require(process.env.BOTS_PATH + '/aamasters/AAMasters/bots/AAJason-Trading-Engine-Bot/Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work.
+ USER_BOT_COMMONS.newCommons = eval(text); // Use this for production
session.initialize(onSessionInitialized)
@@ -140,6 +152,8 @@
const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies');
const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies');
const DATA_SET = require(ROOT_DIR + 'DataSet');
+ const PROCESS_EXECUTION_EVENTS = require(ROOT_DIR + 'ProcessExecutionEvents');
+ const PROCESS_OUTPUT = require(ROOT_DIR + 'ProcessOutput');
/* 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. */
@@ -177,12 +191,162 @@
/* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */
+ let processOutput
+ let processExecutionEvents
let userBot;
let processFramework;
let statusDependencies;
let dataDependencies;
- initializeStatusDependencies();
+ initializeProcessExecutionEvents();
+
+ function initializeProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger)
+
+ processExecutionEvents.initialize(undefined, undefined, onInizialized);
+
+ function onInizialized(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well.");
+ startProcessExecutionEvents()
+ 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, "[WARN] run -> loop -> initializeProcessExecutionEvents -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> 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 -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + 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 -> initializeProcessExecutionEvents -> 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 -> initializeProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
+ function startProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents.start(onStarted);
+
+ function onStarted(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well.");
+ initializeStatusDependencies();
+ 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, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> 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 -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + 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 -> startProcessExecutionEvents -> onStarted -> 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 -> startProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
function initializeStatusDependencies() {
@@ -192,7 +356,7 @@
statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES);
- statusDependencies.initialize(processConfig.statusDependencies, undefined, undefined, onInizialized);
+ statusDependencies.initialize(undefined, undefined, onInizialized);
function onInizialized(err) {
@@ -268,7 +432,7 @@
dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET);
- dataDependencies.initialize(processConfig.dataDependencies, onInizialized);
+ dataDependencies.initialize(onInizialized);
function onInizialized(err) {
@@ -513,18 +677,8 @@
}
switch (processConfig.framework.name) {
- case 'Multi-Period-Market': {
- processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE);
- intitializeProcessFramework();
- break;
- }
- case 'Multi-Period-Daily': {
- processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE);
- intitializeProcessFramework();
- break;
- }
case 'Multi-Period': {
- processFramework = MULTI_PERIOD.newMultiPeriod(bot, logger, COMMONS_MODULE, UTILITIES, USER_BOT_MODULE, COMMONS_MODULE);
+ processFramework = MULTI_PERIOD.newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS);
intitializeProcessFramework();
break;
}
@@ -848,8 +1002,7 @@
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';
- loopControl(nextWaitTime);
+ raiseEventsProcessOutput()
return;
}
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
@@ -910,6 +1063,155 @@
}
}
+ function raiseEventsProcessOutput() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> Entering function."); }
+
+ processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger)
+
+ processOutput.raiseEvents(statusDependencies, onFinished);
+
+ function onFinished(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> raiseEventsProcessOutput -> onFinished -> Execution finished well.");
+ finishProcessExecutionEvents()
+ 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, "[WARN] run -> loop -> raiseEventsProcessOutput -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> 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 -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> raiseEventsProcessOutput -> onFinished -> Unhandled err.result received. -> err = " + 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 -> raiseEventsProcessOutput -> 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 -> raiseEventsProcessOutput -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
+ function finishProcessExecutionEvents() {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> Entering function."); }
+
+ processExecutionEvents.finish(onFinished);
+
+ function onFinished(err) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Entering function."); }
+
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well.");
+ nextWaitTime = 'Normal';
+ loopControl(nextWaitTime);
+ 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, "[WARN] run -> loop -> finishProcessExecutionEvents -> 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.
+ processStopped()
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> 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 -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
+ logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + 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 -> finishProcessExecutionEvents -> 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 -> finishProcessExecutionEvents -> err = " + err.stack);
+ logger.persist();
+ clearInterval(fixedTimeLoopIntervalHandle);
+ clearTimeout(nextLoopTimeoutHandle);
+ clearTimeout(checkLoopHealthHandle);
+ bot.enableCheckLoopHealth = false;
+ callBackFunction(err);
+ }
+ }
+
function loopControl(nextWaitTime) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); }
diff --git a/TradingSimulation.js b/TradingSimulation.js
new file mode 100644
index 000000000..5ca92c8c3
--- /dev/null
+++ b/TradingSimulation.js
@@ -0,0 +1,2893 @@
+exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILITIES) {
+
+ const FULL_LOG = true;
+ const LOG_FILE_CONTENT = false;
+
+ const MODULE_NAME = "Trading Simulation -> " + bot.SESSION.name;
+ const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
+ const GMT_SECONDS = ':00.000 GMT+0000';
+
+ let thisObject = {
+ finalize: finalize,
+ runSimulation: runSimulation
+ };
+
+ let utilities = UTILITIES.newCloudUtilities(bot, logger);
+
+ let percentageBandwidthAt = {}
+ let bollingerBandsAt = {}
+ let bollingerChannelsAt = {}
+ let bollingerSubChannelsAt = {}
+ let candlesAt = {}
+
+
+ return thisObject;
+
+ function finalize() {
+ percentageBandwidthAt = undefined
+ bollingerBandsAt = undefined
+ bollingerChannelsAt = undefined
+ bollingerSubChannelsAt = undefined
+ candlesAt = undefined
+ thisObject = undefined
+ }
+
+ function runSimulation(
+ chart,
+ dataDependencies,
+ timePeriod,
+ timePeriodLabel,
+ currentDay,
+ interExecutionMemory,
+ assistant,
+ callback,
+ callBackFunction) {
+
+ try {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> Entering function."); }
+ let processingDailyFiles
+ if (timePeriod > global.dailyFilePeriods[0][0]) {
+ processingDailyFiles = false
+ } else {
+ processingDailyFiles = true
+ }
+
+ let recordsArray = [];
+ let conditionsArray = [];
+ let strategiesArray = [];
+ let tradesArray = [];
+
+ let tradingSystem = bot.DEFINITION.tradingSystem;
+
+ /* Initial Default Values */
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.initialDatetime = " + bot.VALUES_TO_USE.timeRange.initialDatetime); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.finalDatetime = " + bot.VALUES_TO_USE.timeRange.finalDatetime); }
+
+ let timerToCloseStage = 0
+
+ /* Stop Loss Management */
+
+ const MIN_STOP_LOSS_VALUE = 1 // We can not let the stop be zero to avoid division by 0 error or infinity numbers as a result.
+ const MAX_STOP_LOSS_VALUE = Number.MAX_SAFE_INTEGER
+ let stopLoss = 0;
+
+ /* Take Profit Management */
+
+ const MIN_TAKE_PROFIT_VALUE = 1 // We can not let the buy order be zero to avoid division by 0 error or infinity numbers as a result.
+ const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER
+ let takeProfit = 0;
+
+ /* Simulation Records */
+
+ let hitRatio = 0;
+ let ROI = 0;
+ let days = 0;
+ let positionDays = 0;
+ let anualizedRateOfReturn = 0;
+ let type = '""';
+ let marketRate = 0;
+ let takePositionNow = false
+ let closePositionNow = false
+
+ /* In some cases we need to know if we are positioned at the last candle of the calendar day, for that we need these variables. */
+
+ let lastInstantOfTheDay = 0
+ if (currentDay) {
+ lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1;
+ }
+
+ /*
+ The following counters need to survive multiple executions of the similator and keep themselves reliable.
+ This is challenging when the simulation is executed using Daily Files, since each execution means a new
+ day and these counters are meant to be kept along the whole market.
+
+ To overcome this problem, we use the interExecutionMemory to record the values of the current execution
+ when finish. But there are a few details:
+
+ 1. When the process is at the head of the market, it executes multple times at the same day.
+ 2. The same code serves execution from Market Files.
+ 3. In Daily Files we are receiving candles from the current day and previous day, so we need to take care of
+ not adding to the counters duplicate info when processing the day before candles.
+
+ To overcome these challenges we record the values of the counters and variables on the interExecutionMemory only when
+ the day is complete and if we have a current Day. That menas that for Market Files we will never use
+ interExecutionMemory.
+ */
+
+ /*Needed for statistics */
+ let previousBalanceAssetA = 0;
+ let previousBalanceAssetB = 0;
+
+ /* Position Management */
+ let tradePositionRate = 0;
+ let tradePositionSize = 0;
+
+ /* Strategy and Phase Management */
+ let currentStrategyIndex = -1;
+ let strategyStage = 'No Stage';
+
+ let stopLossPhase = -1;
+ let stopLossStage = 'No Stage';
+
+ let takeProfitPhase = -1;
+ let takeProfitStage = 'No Stage';
+
+ /* These 2 objects will allow us to create separate files for each one of them. */
+
+ let currentStrategy = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ number: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ let currentTrade = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ profit: 0,
+ exitType: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ let balanceAssetA = bot.VALUES_TO_USE.initialBalanceA;
+ let balanceAssetB = bot.VALUES_TO_USE.initialBalanceB;
+
+ let lastTradeProfitLoss = 0;
+ let profit = 0;
+ let lastTradeROI = 0;
+
+ let roundtrips = 0;
+ let fails = 0;
+ let hits = 0;
+ let periods = 0;
+ let positionPeriods = 0;
+
+ /* Usefull counters for conditions and formulas */
+
+ let distanceToLast = {
+ triggerOn: 0,
+ triggerOff: 0,
+ takePosition: 0,
+ closePosition: 0
+ }
+
+ /* Message to the Simulation Executor */
+
+ let orderId = 0;
+ let messageId = 0;
+
+ let yesterday = {};
+
+ /* Initialization */
+
+ yesterday.stopLoss = 0
+ yesterday.takeProfit = 0
+
+ yesterday.previousBalanceAssetA = previousBalanceAssetA
+ yesterday.previousBalanceAssetB = previousBalanceAssetB
+
+ yesterday.tradePositionRate = tradePositionRate
+ yesterday.tradePositionSize = tradePositionSize
+
+ yesterday.currentStrategyIndex = currentStrategyIndex
+ yesterday.strategyStage = strategyStage
+
+ yesterday.stopLossPhase = stopLossPhase
+ yesterday.stopLossStage = stopLossStage
+
+ yesterday.takeProfitPhase = takeProfitPhase
+ yesterday.takeProfitStage = takeProfitStage
+
+ yesterday.currentStrategy = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ number: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ yesterday.currentTrade = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ profit: 0,
+ exitType: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ yesterday.balanceAssetA = balanceAssetA;
+ yesterday.balanceAssetB = balanceAssetB;
+
+ yesterday.lastTradeProfitLoss = 0;
+ yesterday.profit = 0;
+ yesterday.lastTradeROI = 0;
+
+ yesterday.roundtrips = 0;
+ yesterday.fails = 0;
+ yesterday.hits = 0;
+ yesterday.periods = 0;
+ yesterday.positionPeriods = 0;
+
+ yesterday.distanceToLast = {
+ triggerOn: 0,
+ triggerOff: 0,
+ takePosition: 0,
+ closePosition: 0
+ }
+
+ yesterday.orderId = 0;
+ yesterday.messageId = 0;
+
+ yesterday.hitRatio = 0;
+ yesterday.ROI = 0;
+ yesterday.anualizedRateOfReturn = 0;
+
+ if (interExecutionMemory.roundtrips === undefined) { // This just means that the inter execution memory was never used before.
+
+ /* Initialize the data structure we will use inter execution. */
+
+ interExecutionMemory.stopLoss = 0
+ interExecutionMemory.takeProfit = 0
+
+ interExecutionMemory.previousBalanceAssetA = previousBalanceAssetA
+ interExecutionMemory.previousBalanceAssetB = previousBalanceAssetB
+
+ interExecutionMemory.tradePositionRate = tradePositionRate
+ interExecutionMemory.tradePositionSize = tradePositionSize
+
+ interExecutionMemory.currentStrategyIndex = currentStrategyIndex
+ interExecutionMemory.strategyStage = strategyStage
+
+ interExecutionMemory.stopLossPhase = stopLossPhase
+ interExecutionMemory.stopLossStage = stopLossStage
+
+ interExecutionMemory.takeProfitPhase = takeProfitPhase
+ interExecutionMemory.takeProfitStage = takeProfitStage
+
+ interExecutionMemory.currentStrategy = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ number: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ interExecutionMemory.currentTrade = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ profit: 0,
+ exitType: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ interExecutionMemory.balanceAssetA = balanceAssetA;
+ interExecutionMemory.balanceAssetB = balanceAssetB;
+
+ interExecutionMemory.lastTradeProfitLoss = lastTradeProfitLoss;
+ interExecutionMemory.profit = profit;
+ interExecutionMemory.lastTradeROI = lastTradeROI;
+
+ interExecutionMemory.roundtrips = 0;
+ interExecutionMemory.fails = 0;
+ interExecutionMemory.hits = 0;
+ interExecutionMemory.periods = 0;
+ interExecutionMemory.positionPeriods = 0;
+
+ interExecutionMemory.distanceToLast = {
+ triggerOn: 0,
+ triggerOff: 0,
+ takePosition: 0,
+ closePosition: 0
+ }
+
+ interExecutionMemory.orderId = 0;
+ interExecutionMemory.messageId = 0;
+
+ interExecutionMemory.hitRatio = 0;
+ interExecutionMemory.ROI = 0;
+ interExecutionMemory.anualizedRateOfReturn = 0;
+
+ interExecutionMemory.announcements = []
+
+ } else {
+
+ /* We get the initial values from the day previous to the candles we receive at the current execution */
+
+ stopLoss = interExecutionMemory.stopLoss
+ takeProfit = interExecutionMemory.takeProfit
+
+ previousBalanceAssetA = interExecutionMemory.previousBalanceAssetA
+ previousBalanceAssetB = interExecutionMemory.previousBalanceAssetB
+
+ tradePositionRate = interExecutionMemory.tradePositionRate
+ tradePositionSize = interExecutionMemory.tradePositionSize
+
+ currentStrategyIndex = interExecutionMemory.currentStrategyIndex
+ strategyStage = interExecutionMemory.strategyStage
+
+ stopLossPhase = interExecutionMemory.stopLossPhase
+ stopLossStage = interExecutionMemory.stopLossStage
+
+ takeProfitPhase = interExecutionMemory.takeProfitPhase
+ takeProfitStage = interExecutionMemory.takeProfitStage
+
+ currentStrategy = {
+ begin: interExecutionMemory.currentStrategy.begin,
+ end: interExecutionMemory.currentStrategy.end,
+ status: interExecutionMemory.currentStrategy.status,
+ number: interExecutionMemory.currentStrategy.number,
+ beginRate: interExecutionMemory.currentStrategy.beginRate,
+ endRate: interExecutionMemory.currentStrategy.endRate
+ }
+
+ currentTrade = {
+ begin: interExecutionMemory.currentTrade.begin,
+ end: interExecutionMemory.currentTrade.end,
+ status: interExecutionMemory.currentTrade.status,
+ profit: interExecutionMemory.currentTrade.profit,
+ exitType: interExecutionMemory.currentTrade.exitType,
+ beginRate: interExecutionMemory.currentTrade.beginRate,
+ endRate: interExecutionMemory.currentTrade.endRate
+ }
+
+ if (currentDay) {
+ if (currentDay.valueOf() >= bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() + ONE_DAY_IN_MILISECONDS) { // Only after the first day we start grabbing the balance from this memory.
+
+ balanceAssetA = interExecutionMemory.balanceAssetA;
+ balanceAssetB = interExecutionMemory.balanceAssetB;
+
+ yesterday.balanceAssetA = balanceAssetA;
+ yesterday.balanceAssetB = balanceAssetB;
+
+ }
+ }
+
+ lastTradeProfitLoss = interExecutionMemory.lastTradeProfitLoss;
+ profit = interExecutionMemory.profit;
+ lastTradeROI = interExecutionMemory.lastTradeROI;
+
+ roundtrips = interExecutionMemory.roundtrips;
+ fails = interExecutionMemory.fails;
+ hits = interExecutionMemory.hits;
+ periods = interExecutionMemory.periods;
+ positionPeriods = interExecutionMemory.positionPeriods;
+
+ distanceToLast.triggerOn = interExecutionMemory.distanceToLast.triggerOn
+ distanceToLast.triggerOff = interExecutionMemory.distanceToLast.triggerOff
+ distanceToLast.takePosition = interExecutionMemory.distanceToLast.takePosition
+ distanceToLast.closePosition = interExecutionMemory.distanceToLast.closePosition
+
+ orderId = interExecutionMemory.orderId; // to be deprecated
+ messageId = interExecutionMemory.messageId; // to be deprecated
+
+ hitRatio = interExecutionMemory.hitRatio;
+ ROI = interExecutionMemory.ROI;
+ anualizedRateOfReturn = interExecutionMemory.anualizedRateOfReturn;
+
+ /* For the case that any of these variables are not updated during the main loop, we need to store their value at the yesterday structure, otherwise it would be lost. */
+
+ yesterday.stopLoss = stopLoss
+ yesterday.takeProfit = takeProfit
+
+ yesterday.previousBalanceAssetA = previousBalanceAssetA
+ yesterday.previousBalanceAssetB = previousBalanceAssetB
+
+ yesterday.tradePositionRate = tradePositionRate
+ yesterday.tradePositionSize = tradePositionSize
+
+ yesterday.currentStrategyIndex = currentStrategyIndex;
+ yesterday.strategyStage = strategyStage;
+
+ yesterday.stopLossPhase = stopLossPhase;
+ yesterday.stopLossStage = stopLossStage;
+
+ yesterday.takeProfitPhase = takeProfitPhase;
+ yesterday.takeProfitStage = takeProfitStage;
+
+ yesterday.currentStrategy = {
+ begin: currentStrategy.begin,
+ end: currentStrategy.end,
+ status: currentStrategy.status,
+ number: currentStrategy.number,
+ beginRate: currentStrategy.beginRate,
+ endRate: currentStrategy.endRate
+ }
+
+ yesterday.currentTrade = {
+ begin: currentTrade.begin,
+ end: currentTrade.end,
+ status: currentTrade.status,
+ profit: currentTrade.profit,
+ exitType: currentTrade.exitType,
+ beginRate: currentTrade.beginRate,
+ endRate: currentTrade.endRate
+ }
+
+ yesterday.lastTradeProfitLoss = lastTradeProfitLoss;
+ yesterday.profit = profit;
+ yesterday.lastTradeROI = lastTradeROI;
+
+ yesterday.roundtrips = roundtrips;
+ yesterday.fails = fails;
+ yesterday.hits = hits;
+ yesterday.periods = periods;
+ yesterday.positionPeriods = positionPeriods;
+
+ yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn
+ yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff
+ yesterday.distanceToLast.takePosition = distanceToLast.takePosition
+ yesterday.distanceToLast.closePosition = distanceToLast.closePosition
+
+ yesterday.hitRatio = hitRatio;
+ yesterday.ROI = ROI;
+ yesterday.anualizedRateOfReturn = anualizedRateOfReturn;
+ }
+
+ /* Main Array and Maps */
+
+ let propertyName = 'at' + timePeriodLabel.replace('-', '');
+ let candles = chart[propertyName].candles
+ let currentChart = chart[propertyName]
+
+ /* Last Candle */
+ let lastCandle = candles[candles.length - 1];
+
+ /* Main Simulation Loop: We go thourgh all the candles at this time period. */
+ let currentCandleIndex
+
+ /* For Loop Level heartbeat */
+ let loopingDay
+ let previousLoopingDay
+
+ initializeLoop()
+
+ function initializeLoop() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> initializeLoop -> Entering function."); }
+
+ /* Estimate Initial Candle */
+
+ let firstEnd = candles[0].end
+ let targetEnd = bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()
+ let diff = targetEnd - firstEnd
+ let amount = diff / timePeriod
+
+ currentCandleIndex = Math.trunc(amount)
+ if (currentCandleIndex < 0) { currentCandleIndex = 0 }
+ if (currentCandleIndex > candles.length - 1) {
+ /* This will happen when the bot.VALUES_TO_USE.timeRange.initialDatetime is beyond the last candle available, meaning that the dataSet needs to be updated with more up-to-date data. */
+ currentCandleIndex = candles.length - 1
+ }
+
+ loop()
+ }
+
+ function loop() {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Entering function."); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Processing candle # " + currentCandleIndex); }
+
+ let candle = candles[currentCandleIndex];
+
+ /* Not processing while out of user-defined time range */
+
+ if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle before the bot.VALUES_TO_USE.timeRange.initialDatetime."); }
+ controlLoop();
+ return
+ }
+ if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle after the bot.VALUES_TO_USE.timeRange.finalDatetime."); }
+ afterLoop();
+ return
+ }
+
+ /* Here we update at the chart data structure the objects for each product representing where we are currently standing at the simulation loop */
+ if (processingDailyFiles) {
+ for (let j = 0; j < global.dailyFilePeriods.length; j++) {
+
+ let mapKey = dailyFilePeriods[j][1]
+ let propertyName = 'at' + mapKey.replace('-', '');
+ let thisChart = chart[propertyName]
+
+ for (let k = 0; k < dataDependencies.length; k++) {
+ let dataDependencyNode = dataDependencies[k]
+ let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName
+ let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName
+ let currentElement = getElement(thisChart[pluralVariableName], candle, 'Daily' + '-' + mapKey + '-' + pluralVariableName)
+ thisChart[singularVariableName] = currentElement
+ }
+ }
+ }
+
+ for (let j = 0; j < global.marketFilesPeriods.length; j++) {
+
+ let mapKey = marketFilesPeriods[j][1]
+ let propertyName = 'at' + mapKey.replace('-', '');
+ let thisChart = chart[propertyName]
+
+ for (let k = 0; k < dataDependencies.length; k++) {
+ let dataDependencyNode = dataDependencies[k]
+ let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName
+ let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName
+ let currentElement = getElement(thisChart[pluralVariableName], candle, 'Market' + '-' + mapKey + '-' + pluralVariableName)
+ thisChart[singularVariableName] = currentElement
+ }
+ }
+
+ /* While we are processing the previous day. */
+ let positionedAtYesterday = false
+ if (currentDay) {
+ positionedAtYesterday = (candle.end < currentDay.valueOf())
+ }
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle Begin @ " + (new Date(candle.begin)).toLocaleString()) }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle End @ " + (new Date(candle.end)).toLocaleString()) }
+
+ /* Assistant Info */
+
+ let ticker = {
+ bid: candle.close,
+ ask: candle.close,
+ last: candle.close
+ }
+
+ /* We will produce a simulation level heartbeat in order to inform the user this is running. */
+
+ loopingDay = new Date(Math.trunc(candle.begin / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS)
+ if (loopingDay.valueOf() !== previousLoopingDay) {
+
+ let processingDate = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2);
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + currentCandleIndex + " @ " + processingDate) }
+ console.log("Jason -> " + MODULE_NAME + " -> runSimulation -> loop -> Simulation " + bot.sessionKey + " Loop # " + currentCandleIndex + " @ " + processingDate)
+
+ bot.sessionHeartBeat(processingDate) // tell the world we are alive and doing well
+ }
+ previousLoopingDay = loopingDay.valueOf()
+
+ /* If any of the needed data dependencies is missing for this particular candle, then we jump the candle*/
+ for (let k = 0; k < dataDependencies.length; k++) {
+ let dataDependencyNode = dataDependencies[k]
+ let singularVariableName = dataDependencyNode.referenceParent.parentNode.code.singularVariableName
+ let pluralVariableName = dataDependencyNode.referenceParent.parentNode.code.pluralVariableName
+ let product = currentChart[pluralVariableName]
+ let currentElement = currentChart[singularVariableName]
+
+ if (currentElement === undefined) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because " + singularVariableName + " is undefined."); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> For some indicators it is normal to start after a few candles."); }
+ controlLoop();
+ return
+ }
+ }
+
+ periods++;
+ days = periods * timePeriod / ONE_DAY_IN_MILISECONDS;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.periods = periods
+ }
+
+ /* We skip the candle at the head of the market because currentCandleIndex has not closed yet. */
+ let candlesPerDay = ONE_DAY_IN_MILISECONDS / timePeriod
+ if (currentCandleIndex === candles.length - 1) {
+ if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) {
+ /*We are at the head of the market, thus we skip the last candle because it has not close yet. */
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); }
+ controlLoop();
+ return
+ /* Note here that in the last candle of the first day or the second day it will use an incomplete candle and partially calculated indicators.
+ if we skip these two periods, then there will be a hole in the file since the last period will be missing. */
+ }
+ }
+
+ } else { // We are processing Market Files
+ if (currentCandleIndex === candles.length - 1) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet."); }
+ controlLoop();
+ return
+ }
+ }
+
+ let conditions = new Map; // Here we store the conditions values that will be use in the simulator for decision making.
+ let formulas = new Map;
+ let conditionsArrayRecord = []; // These are the records that will be saved in a file for the plotter to consume.
+ let conditionsArrayValues = []; // Here we store the conditions values that will be written on file for the plotter.
+ let formulasErrors = []; // Here we store the errors produced by all phase formulas.
+ let formulasValues = []; // Here we store the values produced by all phase formulas.
+
+ /* We define and evaluate all conditions to be used later during the simulation loop. */
+
+ conditionsArrayRecord.push(candle.begin);
+ conditionsArrayRecord.push(candle.end);
+
+ for (let j = 0; j < tradingSystem.strategies.length; j++) {
+
+ let strategy = tradingSystem.strategies[j];
+
+ let positionSize = 0
+ let positionRate = 0
+
+ /* Continue with the rest of the formulas and conditions */
+
+ let triggerStage = strategy.triggerStage
+
+ if (triggerStage !== undefined) {
+
+ if (triggerStage.triggerOn !== undefined) {
+
+ for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) {
+
+ let situation = triggerStage.triggerOn.situations[k];
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m;
+
+ if (condition.code !== undefined) {
+ newCondition(key, condition.code);
+ }
+ }
+ }
+ }
+
+ if (triggerStage.triggerOff !== undefined) {
+
+ for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) {
+
+ let situation = triggerStage.triggerOff.situations[k];
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m;
+
+ if (condition.code !== undefined) {
+ newCondition(key, condition.code);
+ }
+ }
+ }
+ }
+
+ if (triggerStage.takePosition !== undefined) {
+
+ for (let k = 0; k < triggerStage.takePosition.situations.length; k++) {
+
+ let situation = triggerStage.takePosition.situations[k];
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m;
+
+ if (condition.code !== undefined) {
+ newCondition(key, condition.code);
+ }
+ }
+ }
+ }
+ }
+
+ let openStage = strategy.openStage
+
+ if (openStage !== undefined) {
+
+ /* Default Values*/
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ positionSize = balanceAssetA;
+ positionRate = candle.close;
+ } else {
+ positionSize = balanceAssetB;
+ positionRate = candle.close;
+ }
+
+ let initialDefinition = openStage.initialDefinition
+
+ if (initialDefinition !== undefined) {
+
+ if (tradePositionSize !== 0) {
+ positionSize = tradePositionSize
+ } else {
+ if (initialDefinition.positionSize !== undefined) {
+ if (initialDefinition.positionSize.formula !== undefined) {
+ try {
+ positionSize = eval(initialDefinition.positionSize.formula.code);
+ } catch (err) {
+ initialDefinition.positionSize.formula.error = err.message
+ }
+ if (isNaN(positionSize)) {
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ positionSize = balanceAssetA;
+ } else {
+ positionSize = balanceAssetB;
+ }
+ } else {
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ if (positionSize > balanceAssetA) { positionSize = balanceAssetA }
+ } else {
+ if (positionSize > balanceAssetB) { positionSize = balanceAssetB }
+ }
+ }
+ }
+ }
+ }
+
+ if (tradePositionRate !== 0) {
+ positionRate = tradePositionRate
+ } else {
+ if (initialDefinition.positionRate !== undefined) {
+ if (initialDefinition.positionRate.formula !== undefined) {
+ try {
+ positionRate = eval(initialDefinition.positionRate.formula.code);
+ } catch (err) {
+ initialDefinition.positionRate.formula.error = err.message
+ }
+ if (isNaN(positionRate)) {
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ positionRate = candle.close;
+ } else {
+ positionRate = candle.close;
+ }
+ }
+ }
+ }
+ }
+
+ if (initialDefinition.stopLoss !== undefined) {
+
+ for (let p = 0; p < initialDefinition.stopLoss.phases.length; p++) {
+
+ let phase = initialDefinition.stopLoss.phases[p];
+
+ /* Evaluate Formula */
+ let formulaValue
+ let formulaError = ''
+
+ if (phase.formula !== undefined) {
+ try {
+ formulaValue = eval(phase.formula.code);
+ if (formulaValue === Infinity) {
+ formulaError = "Formula evaluates to Infinity."
+ formulaValue = MAX_STOP_LOSS_VALUE
+ if (stopLossStage === 'Open Stage') {
+ formulaError = "WARNING: Formula evaluates to Infinity."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.stopLoss -> MAX_STOP_LOSS_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+ } catch (err) {
+ if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) {
+ /*
+ We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined.
+ */
+ } else {
+ formulaError = err.message
+ }
+ }
+ if (isNaN(formulaValue)) { formulaValue = 0; }
+ if (formulaValue < MIN_STOP_LOSS_VALUE) {
+ formulaValue = MIN_STOP_LOSS_VALUE
+ if (stopLossStage === 'Open Stage') {
+ formulaError = "WARNING: Formula is evaluating below the MIN_STOP_LOSS_VALUE."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.stopLoss -> MIN_STOP_LOSS_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+
+ formulasErrors.push('"' + formulaError + '"')
+ formulasValues.push(formulaValue)
+ let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p;
+ formulas.set(key, formulaValue)
+ }
+
+ /* next phase event */
+ let nextPhaseEvent = phase.nextPhaseEvent;
+ if (nextPhaseEvent !== undefined) {
+
+ for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
+
+ let situation = nextPhaseEvent.situations[k];
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m;
+
+ if (condition.code !== undefined) {
+ newCondition(key, condition.code);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (initialDefinition.takeProfit !== undefined) {
+
+ for (let p = 0; p < initialDefinition.takeProfit.phases.length; p++) {
+
+ let phase = initialDefinition.takeProfit.phases[p];
+
+ /* Evaluate Formula */
+ let formulaValue
+ let formulaError = ''
+
+ if (phase.formula !== undefined) {
+ try {
+ formulaValue = eval(phase.formula.code);
+ if (formulaValue === Infinity) {
+ formulaValue = MAX_TAKE_PROFIT_VALUE
+ if (takeProfitStage === 'Open Stage') {
+ formulaError = "WARNING: Formula evaluates to Infinity."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.takeProfit -> MAX_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+ } catch (err) {
+ if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) {
+ /*
+ We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined.
+ */
+ } else {
+ formulaError = err.message
+ }
+ }
+ if (isNaN(formulaValue)) { formulaValue = 0; }
+ if (formulaValue < MIN_TAKE_PROFIT_VALUE) {
+ formulaValue = MIN_TAKE_PROFIT_VALUE
+ if (takeProfitStage === 'Open Stage') {
+ formulaError = "WARNING: Formula is evaluating below the MIN_TAKE_PROFIT_VALUE."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> initialDefinition.takeProfit -> MIN_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+
+ formulasErrors.push('"' + formulaError + '"')
+ formulasValues.push(formulaValue)
+ let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p;
+ formulas.set(key, formulaValue)
+ }
+
+ /* next phase event */
+ let nextPhaseEvent = phase.nextPhaseEvent;
+ if (nextPhaseEvent !== undefined) {
+
+ for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
+
+ let situation = nextPhaseEvent.situations[k];
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m;
+
+ if (condition.code !== undefined) {
+ newCondition(key, condition.code);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ strategy.positionSize = positionSize
+ strategy.positionRate = positionRate
+ }
+
+ let manageStage = strategy.manageStage
+
+ if (manageStage !== undefined) {
+
+ if (manageStage.stopLoss !== undefined) {
+
+ for (let p = 0; p < manageStage.stopLoss.phases.length; p++) {
+
+ let phase = manageStage.stopLoss.phases[p];
+
+ /* Evaluate Formula */
+ let formulaValue
+ let formulaError = ''
+
+ if (phase.formula !== undefined) {
+ try {
+ formulaValue = eval(phase.formula.code);
+ if (formulaValue === Infinity) {
+ formulaError = ""
+ formulaValue = MAX_STOP_LOSS_VALUE
+ if (stopLossStage === 'Manage Stage') {
+ formulaError = "WARNING: Formula evaluates to Infinity."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.stopLoss -> MAX_STOP_LOSS_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+ } catch (err) {
+ if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) {
+ /*
+ We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined.
+ */
+ } else {
+ formulaError = err.message
+ }
+ }
+ if (isNaN(formulaValue)) { formulaValue = 0; }
+ if (formulaValue < MIN_STOP_LOSS_VALUE) {
+ formulaValue = MIN_STOP_LOSS_VALUE
+ if (stopLossStage === 'Manage Stage') {
+ formulaError = "WARNING: Formula is evaluating below the MIN_STOP_LOSS_VALUE."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.stopLoss -> MIN_STOP_LOSS_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+
+ formulasErrors.push('"' + formulaError + '"')
+ formulasValues.push(formulaValue)
+ let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p;
+ formulas.set(key, formulaValue)
+ }
+
+ /* next phase event */
+ let nextPhaseEvent = phase.nextPhaseEvent;
+ if (nextPhaseEvent !== undefined) {
+
+ for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
+
+ let situation = nextPhaseEvent.situations[k];
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m;
+
+ if (condition.code !== undefined) {
+ newCondition(key, condition.code);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (manageStage.takeProfit !== undefined) {
+
+ for (let p = 0; p < manageStage.takeProfit.phases.length; p++) {
+
+ let phase = manageStage.takeProfit.phases[p];
+
+ /* Evaluate Formula */
+ let formulaValue
+ let formulaError = ''
+
+ if (phase.formula !== undefined) {
+ try {
+ formulaValue = eval(phase.formula.code);
+ if (formulaValue === Infinity) {
+ formulaError = "Formula evaluates to Infinity."
+ formulaValue = MAX_TAKE_PROFIT_VALUE
+ if (takeProfitStage === 'Manage Stage') {
+ formulaError = "WARNING: Formula evaluates to Infinity."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.takeProfit -> MAX_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+ } catch (err) {
+ if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) {
+ /*
+ We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined.
+ */
+ } else {
+ formulaError = err.message
+ }
+ }
+ if (isNaN(formulaValue)) { formulaValue = 0; }
+ if (formulaValue < MIN_TAKE_PROFIT_VALUE) {
+ formulaValue = MIN_TAKE_PROFIT_VALUE
+ if (takeProfitStage === 'Manage Stage') {
+ formulaError = "WARNING: Formula is evaluating below the MIN_TAKE_PROFIT_VALUE."
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[WARN] runSimulation -> loop -> manageStage.takeProfit -> MIN_TAKE_PROFIT_VALUE -> formulaError = " + formulaError); }
+ }
+ }
+
+ formulasErrors.push('"' + formulaError + '"')
+ formulasValues.push(formulaValue)
+ let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p;
+ formulas.set(key, formulaValue)
+ }
+
+ /* next phase event */
+ let nextPhaseEvent = phase.nextPhaseEvent;
+ if (nextPhaseEvent !== undefined) {
+
+ for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
+
+ let situation = nextPhaseEvent.situations[k];
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m;
+
+ if (condition.code !== undefined) {
+ newCondition(key, condition.code);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function newCondition(key, node) {
+
+ let condition;
+ let error = ''
+ let value
+
+ try {
+ value = eval(node.code);
+ } catch (err) {
+ /*
+ One possible error is that the conditions references a .previous that is undefined. For this
+ reason and others, we will simply set the value to false.
+ */
+ value = false
+
+ if (node.code.indexOf('previous') > -1 && err.message.indexOf('of undefined') > -1 ||
+ node.code.indexOf('chart') > -1 && err.message.indexOf('of undefined') > -1
+ ) {
+ /*
+ We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined.
+ */
+ } else {
+ node.error = err.message + " @ " + (new Date(candle.begin)).toLocaleString()
+ }
+ }
+
+ condition = {
+ key: key,
+ value: value
+ };
+
+ conditions.set(condition.key, condition);
+
+ if (condition.value) {
+ conditionsArrayValues.push(1);
+ } else {
+ conditionsArrayValues.push(0);
+ }
+ }
+ }
+
+ /* Trigger On Conditions */
+ if (
+ strategyStage === 'No Stage' &&
+ currentStrategyIndex === -1
+ ) {
+ let minimumBalance
+ let maximumBalance
+ let balance
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ balance = balanceAssetA
+ minimumBalance = bot.VALUES_TO_USE.minimumBalanceA
+ maximumBalance = bot.VALUES_TO_USE.maximumBalanceA
+ } else {
+ balance = balanceAssetB
+ minimumBalance = bot.VALUES_TO_USE.minimumBalanceB
+ maximumBalance = bot.VALUES_TO_USE.maximumBalanceB
+ }
+
+ if (balance > minimumBalance && balance < maximumBalance) {
+
+ /*
+ Here we need to pick a strategy, or if there is not suitable strategy for the current
+ market conditions, we pass until the next period.
+
+ To pick a new strategy we will evaluate what we call the trigger on. Once we enter
+ into one strategy, we will ignore market conditions for others. However there is also
+ a strategy trigger off which can be hit before taking a position. If hit, we would
+ be outside a strategy again and looking for the condition to enter all over again.
+
+ */
+
+ for (let j = 0; j < tradingSystem.strategies.length; j++) {
+
+ let strategy = tradingSystem.strategies[j];
+
+ let triggerStage = strategy.triggerStage
+
+ if (triggerStage !== undefined) {
+
+ if (triggerStage.triggerOn !== undefined) {
+
+ for (let k = 0; k < triggerStage.triggerOn.situations.length; k++) {
+
+ let situation = triggerStage.triggerOn.situations[k];
+ let passed = true;
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + 'triggerStage' + '-' + 'triggerOn' + '-' + k + '-' + m;
+
+ let value = false
+ if (conditions.get(key) !== undefined) {
+ value = conditions.get(key).value;
+ }
+
+ if (value === false) { passed = false; }
+ }
+
+ if (passed) {
+
+ strategyStage = 'Trigger Stage';
+ currentStrategyIndex = j;
+ currentStrategy.begin = candle.begin;
+ currentStrategy.beginRate = candle.min;
+ currentStrategy.endRate = candle.min; // In case the strategy does not get exited
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.strategyStage = strategyStage;
+ yesterday.currentStrategyIndex = currentStrategyIndex;
+ yesterday.currentStrategy.begin = currentStrategy.begin;
+ yesterday.currentStrategy.beginRate = currentStrategy.beginRate;
+ yesterday.currentStrategy.endRate = currentStrategy.endRate;
+ }
+ }
+
+ distanceToLast.triggerOn = 1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn
+ }
+ }
+
+ checkAnnouncements(triggerStage.triggerOn)
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Switching to Trigger Stage because conditions at Trigger On Event were met."); }
+ break;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ let stopRunningDate = new Date(candle.begin)
+ if (balance <= minimumBalance) {
+ tradingSystem.error = "Min Balance @ " + stopRunningDate.toLocaleString()
+ }
+ if (balance >= maximumBalance) {
+ tradingSystem.error = "Max Balance @ " + stopRunningDate.toLocaleString()
+ }
+ }
+ }
+
+ /* Trigger Off Condition */
+ if (strategyStage === 'Trigger Stage') {
+
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+
+ let triggerStage = strategy.triggerStage
+
+ if (triggerStage !== undefined) {
+
+ if (triggerStage.triggerOff !== undefined) {
+
+ for (let k = 0; k < triggerStage.triggerOff.situations.length; k++) {
+
+ let situation = triggerStage.triggerOff.situations[k];
+ let passed = true;
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'triggerOff' + '-' + k + '-' + m;
+
+ let value = false
+ if (conditions.get(key) !== undefined) {
+ value = conditions.get(key).value;
+ }
+
+ if (value === false) { passed = false; }
+ }
+
+ if (passed) {
+
+ currentStrategy.number = currentStrategyIndex
+ currentStrategy.end = candle.end;
+ currentStrategy.endRate = candle.min;
+ currentStrategy.status = 1;
+ strategyStage = 'No Stage';
+ currentStrategyIndex = -1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.currentStrategy.number = currentStrategy.number;
+ yesterday.currentStrategy.end = currentStrategy.end;
+ yesterday.currentStrategy.endRate = currentStrategy.endRate;
+ yesterday.currentStrategy.status = currentStrategy.status;
+ yesterday.strategyStage = strategyStage;
+ yesterday.currentStrategyIndex = currentStrategyIndex;
+ }
+ }
+
+ distanceToLast.triggerOff = 1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff
+ }
+ }
+
+ checkAnnouncements(triggerStage.triggerOff)
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Switching to No Stage because conditions at the Trigger Off Event were met."); }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Take Position Condition */
+ if (strategyStage === 'Trigger Stage') {
+
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+
+ let triggerStage = strategy.triggerStage
+
+ if (triggerStage !== undefined) {
+
+ if (triggerStage.takePosition !== undefined) {
+
+ for (let k = 0; k < triggerStage.takePosition.situations.length; k++) {
+
+ let situation = triggerStage.takePosition.situations[k];
+ let passed = true;
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = currentStrategyIndex + '-' + 'triggerStage' + '-' + 'takePosition' + '-' + k + '-' + m;
+
+ let value = false
+ if (conditions.get(key) !== undefined) {
+ value = conditions.get(key).value;
+ }
+
+ if (value === false) { passed = false; }
+ }
+
+ if (passed) {
+
+ type = '"Take Position"';
+
+ strategyStage = 'Open Stage';
+ stopLossStage = 'Open Stage';
+ takeProfitStage = 'Open Stage';
+ stopLossPhase = 0;
+ takeProfitPhase = 0;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.strategyStage = strategyStage;
+ yesterday.stopLossStage = stopLossStage;
+ yesterday.takeProfitStage = takeProfitStage;
+ yesterday.stopLossPhase = stopLossPhase;
+ yesterday.takeProfitPhase = takeProfitPhase;
+ }
+ }
+
+ takePositionNow = true
+
+ checkAnnouncements(triggerStage.takePosition)
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Conditions at the Take Position Event were met."); }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Stop Loss Management */
+ if (
+ (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') &&
+ takePositionNow !== true
+ ) {
+
+ checkStopPhases()
+ calculateStopLoss();
+
+ }
+
+ function checkStopPhases() {
+
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+
+ let openStage = strategy.openStage
+ let manageStage = strategy.manageStage
+ let parentNode
+ let j = currentStrategyIndex
+ let stageKey
+ let initialDefinitionKey = ''
+ let p
+
+ if (stopLossStage === 'Open Stage' && openStage !== undefined) {
+ if (openStage.initialDefinition !== undefined) {
+ if (openStage.initialDefinition.stopLoss !== undefined) {
+ parentNode = openStage.initialDefinition
+ initialDefinitionKey = '-' + 'initialDefinition'
+ stageKey = 'openStage'
+ p = stopLossPhase
+ }
+ }
+ }
+
+ if (stopLossStage === 'Manage Stage' && manageStage !== undefined) {
+ if (manageStage.stopLoss !== undefined) {
+ parentNode = manageStage
+ stageKey = 'manageStage'
+ p = stopLossPhase - 1
+ }
+ }
+
+ let phase = parentNode.stopLoss.phases[p];
+
+ let nextPhaseEvent = phase.nextPhaseEvent;
+ if (nextPhaseEvent !== undefined) {
+
+ for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
+
+ let situation = nextPhaseEvent.situations[k];
+ let passed = true;
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'stopLoss' + '-' + p + '-' + k + '-' + m;
+
+ let value = false
+ if (conditions.get(key) !== undefined) {
+ value = conditions.get(key).value;
+ }
+
+ if (value === false) { passed = false; }
+ }
+
+ if (passed) {
+
+ stopLossPhase++;
+ stopLossStage = 'Manage Stage'
+ if (takeProfitPhase > 0) { strategyStage = 'Manage Stage' }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.stopLossPhase = stopLossPhase;
+ yesterday.stopLossStage = stopLossStage;
+ yesterday.strategyStage = strategyStage;
+ }
+ }
+
+ checkAnnouncements(nextPhaseEvent)
+ return;
+ }
+ }
+ }
+ }
+
+ function calculateStopLoss() {
+
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+ let openStage = strategy.openStage
+ let manageStage = strategy.manageStage
+ let phase
+ let key
+
+ if (stopLossStage === 'Open Stage' && openStage !== undefined) {
+ if (openStage.initialDefinition !== undefined) {
+ if (openStage.initialDefinition.stopLoss !== undefined) {
+ phase = openStage.initialDefinition.stopLoss.phases[stopLossPhase];
+ key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'stopLoss' + '-' + (stopLossPhase);
+ }
+ }
+ }
+
+ if (stopLossStage === 'Manage Stage' && manageStage !== undefined) {
+ if (manageStage.stopLoss !== undefined) {
+ phase = manageStage.stopLoss.phases[stopLossPhase - 1];
+ key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'stopLoss' + '-' + (stopLossPhase - 1);
+ }
+ }
+
+ if (phase !== undefined) {
+ if (phase.formula !== undefined) {
+ let previousValue = stopLoss
+
+ stopLoss = formulas.get(key)
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.stopLoss = stopLoss
+ }
+ }
+
+ if (stopLoss !== previousValue) {
+ checkAnnouncements(phase, stopLoss)
+ }
+ }
+ }
+ }
+
+ /* Take Profit Management */
+ if (
+ (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') &&
+ takePositionNow !== true
+ ) {
+
+ checkTakeProfitPhases();
+ calculateTakeProfit();
+
+ }
+
+ function checkTakeProfitPhases() {
+
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+
+ let openStage = strategy.openStage
+ let manageStage = strategy.manageStage
+ let parentNode
+ let j = currentStrategyIndex
+ let stageKey
+ let initialDefinitionKey = ''
+ let p
+
+ if (takeProfitStage === 'Open Stage' && openStage !== undefined) {
+ if (openStage.initialDefinition !== undefined) {
+ if (openStage.initialDefinition.takeProfit !== undefined) {
+ parentNode = openStage.initialDefinition
+ initialDefinitionKey = '-' + 'initialDefinition'
+ stageKey = 'openStage'
+ p = takeProfitPhase
+ }
+ }
+ }
+
+ if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) {
+ if (manageStage.takeProfit !== undefined) {
+ parentNode = manageStage
+ stageKey = 'manageStage'
+ p = takeProfitPhase - 1
+ }
+ }
+
+ let phase = parentNode.takeProfit.phases[p];
+
+ let nextPhaseEvent = phase.nextPhaseEvent;
+ if (nextPhaseEvent !== undefined) {
+
+ for (let k = 0; k < nextPhaseEvent.situations.length; k++) {
+
+ let situation = nextPhaseEvent.situations[k];
+ let passed = true;
+
+ for (let m = 0; m < situation.conditions.length; m++) {
+
+ let condition = situation.conditions[m];
+ let key = j + '-' + stageKey + initialDefinitionKey + '-' + 'takeProfit' + '-' + p + '-' + k + '-' + m;
+
+ let value = false
+ if (conditions.get(key) !== undefined) {
+ value = conditions.get(key).value;
+ }
+
+ if (value === false) { passed = false; }
+ }
+
+ if (passed) {
+
+ takeProfitPhase++;
+ takeProfitStage = 'Manage Stage'
+ if (stopLossPhase > 0) { strategyStage = 'Manage Stage' }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.takeProfitPhase = takeProfitPhase;
+ yesterday.takeProfitStage = takeProfitStage;
+ yesterday.strategyStage = strategyStage;
+ }
+ }
+
+ checkAnnouncements(nextPhaseEvent)
+ return;
+ }
+ }
+ }
+ }
+
+ function calculateTakeProfit() {
+
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+ let openStage = strategy.openStage
+ let manageStage = strategy.manageStage
+ let phase
+ let key
+
+ if (takeProfitStage === 'Open Stage' && openStage !== undefined) {
+ if (openStage.initialDefinition !== undefined) {
+ if (openStage.initialDefinition.takeProfit !== undefined) {
+ phase = openStage.initialDefinition.takeProfit.phases[takeProfitPhase];
+ key = currentStrategyIndex + '-' + 'openStage' + '-' + 'initialDefinition' + '-' + 'takeProfit' + '-' + (takeProfitPhase);
+ }
+ }
+ }
+
+ if (takeProfitStage === 'Manage Stage' && manageStage !== undefined) {
+ if (manageStage.takeProfit !== undefined) {
+ phase = manageStage.takeProfit.phases[takeProfitPhase - 1];
+ key = currentStrategyIndex + '-' + 'manageStage' + '-' + 'takeProfit' + '-' + (takeProfitPhase - 1);
+ }
+ }
+
+ if (phase !== undefined) {
+ if (phase.formula !== undefined) {
+
+ let previousValue = stopLoss
+
+ takeProfit = formulas.get(key)
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.takeProfit = takeProfit
+ }
+ }
+
+ if (takeProfit !== previousValue) {
+ checkAnnouncements(phase, takeProfit)
+ }
+ }
+ }
+ }
+
+ /* Keeping Position Counters Up-to-date */
+ if (
+ (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage')
+ ) {
+
+ if (takePositionNow === true) {
+ positionPeriods = 0
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.positionPeriods = 0
+ }
+ }
+ }
+
+ positionPeriods++;
+ positionDays = positionPeriods * timePeriod / ONE_DAY_IN_MILISECONDS;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.positionPeriods = positionPeriods
+ }
+ }
+ } else {
+ positionPeriods = 0
+ positionDays = 0
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.positionPeriods = 0
+ }
+ }
+ }
+
+ /* Keeping Distance Counters Up-to-date */
+ if (
+ distanceToLast.triggerOn > 0 // with this we avoind counting before the first event happens.
+ ) {
+ distanceToLast.triggerOn++;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.triggerOn = distanceToLast.triggerOn
+ }
+ }
+ }
+
+ if (
+ distanceToLast.triggerOff > 0 // with this we avoind counting before the first event happens.
+ ) {
+ distanceToLast.triggerOff++;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff
+ }
+ }
+ }
+
+ if (
+ distanceToLast.takePosition > 0 // with this we avoind counting before the first event happens.
+ ) {
+ distanceToLast.takePosition++;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.takePosition = distanceToLast.takePosition
+ }
+ }
+ }
+
+ if (
+ distanceToLast.closePosition > 0 // with this we avoind counting before the first event happens.
+ ) {
+ distanceToLast.closePosition++;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.takePosition = distanceToLast.closePosition
+ }
+ }
+ }
+
+ /* Checking if Stop or Take Profit were hit */
+ if (
+ (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') &&
+ takePositionNow !== true
+ ) {
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+
+ /* Checking what happened since the last execution. We need to know if the Stop Loss
+ or our Take Profit were hit. */
+
+ /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */
+
+ if ((bot.VALUES_TO_USE.baseAsset === 'BTC' && candle.max >= stopLoss) || (bot.VALUES_TO_USE.baseAsset !== 'BTC' && candle.min <= stopLoss)) {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Stop Loss was hit."); }
+ /*
+ Hit Point Validation
+
+ This prevents misscalculations when a formula places the stop loss in this case way beyond the market price.
+ If we take the stop loss value at those situation would be a huge distortion of facts.
+ */
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ if (stopLoss < candle.min) {
+ stopLoss = candle.min
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.stopLoss = stopLoss
+ }
+ }
+ }
+ } else {
+ if (stopLoss > candle.max) {
+ stopLoss = candle.max
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.stopLoss = stopLoss
+ }
+ }
+ }
+ }
+
+ let slippedStopLoss = stopLoss
+
+ /* Apply the Slippage */
+ let slippageAmount = slippedStopLoss * bot.VALUES_TO_USE.slippage.stopLoss / 100
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ slippedStopLoss = slippedStopLoss + slippageAmount
+ } else {
+ slippedStopLoss = slippedStopLoss - slippageAmount
+ }
+
+ let finalStopLoss = slippedStopLoss;
+
+ let feePaid = 0
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ strategy.positionSize = balanceAssetB / finalStopLoss;
+ strategy.positionRate = finalStopLoss;
+
+ feePaid = balanceAssetB / finalStopLoss * bot.VALUES_TO_USE.feeStructure.taker / 100
+
+ balanceAssetA = balanceAssetA + balanceAssetB / finalStopLoss - feePaid;
+ balanceAssetB = 0;
+ } else {
+ strategy.positionSize = balanceAssetA * finalStopLoss;
+ strategy.positionRate = finalStopLoss;
+
+ feePaid = balanceAssetA * finalStopLoss * bot.VALUES_TO_USE.feeStructure.taker / 100
+
+ balanceAssetB = balanceAssetB + balanceAssetA * finalStopLoss - feePaid;
+ balanceAssetA = 0;
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.balanceAssetA = balanceAssetA;
+ yesterday.balanceAssetB = balanceAssetB;
+ }
+ }
+
+ marketRate = finalStopLoss;
+ type = '"Close@StopLoss"';
+ strategyStage = 'Close Stage';
+ stopLossStage = 'No Stage';
+ takeProfitStage = 'No Stage';
+ currentTrade.end = candle.end;
+ currentTrade.status = 1;
+ currentTrade.exitType = 1;
+ currentTrade.endRate = finalStopLoss;
+
+ closePositionNow = true;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.strategyStage = strategyStage;
+ yesterday.stopLossStage = stopLossStage;
+ yesterday.takeProfitStage = takeProfitStage;
+ yesterday.currentTrade.end = currentTrade.end;
+ yesterday.currentTrade.status = currentTrade.status;
+ yesterday.currentTrade.exitType = currentTrade.exitType;
+ yesterday.currentTrade.endRate = currentTrade.endRate;
+ }
+ }
+ }
+
+ /* Take Profit condition: Here we verify if the Take Profit was hit or not. */
+
+ if ((bot.VALUES_TO_USE.baseAsset === 'BTC' && candle.min <= takeProfit) || (bot.VALUES_TO_USE.baseAsset !== 'BTC' && candle.max >= takeProfit)) {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Take Profit was hit."); }
+ /*
+ Hit Point Validation:
+
+ This prevents misscalculations when a formula places the take profit in this case way beyond the market price.
+ If we take the stop loss value at those situation would be a huge distortion of facts.
+ */
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ if (takeProfit > candle.max) {
+ takeProfit = candle.max
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.takeProfit = takeProfit
+ }
+ }
+ }
+ } else {
+ if (takeProfit < candle.min) {
+ takeProfit = candle.min
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.takeProfit = takeProfit
+ }
+ }
+ }
+ }
+
+ let slippedTakeProfit = takeProfit
+ /* Apply the Slippage */
+ let slippageAmount = slippedTakeProfit * bot.VALUES_TO_USE.slippage.takeProfit / 100
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ slippedTakeProfit = slippedTakeProfit + slippageAmount
+ } else {
+ slippedTakeProfit = slippedTakeProfit - slippageAmount
+ }
+
+ let finalTakeProfit = slippedTakeProfit;
+
+ let feePaid = 0
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ strategy.positionSize = balanceAssetB / finalTakeProfit;
+ strategy.positionRate = finalTakeProfit;
+
+ feePaid = balanceAssetB / finalTakeProfit * bot.VALUES_TO_USE.feeStructure.taker / 100
+
+ balanceAssetA = balanceAssetA + balanceAssetB / finalTakeProfit - feePaid;
+ balanceAssetB = 0;
+ } else {
+ strategy.positionSize = balanceAssetA * finalTakeProfit;
+ strategy.positionRate = finalTakeProfit;
+
+ feePaid = balanceAssetA * finalTakeProfit * bot.VALUES_TO_USE.feeStructure.taker / 100
+
+ balanceAssetB = balanceAssetB + balanceAssetA * finalTakeProfit - feePaid;
+ balanceAssetA = 0;
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.balanceAssetA = balanceAssetA;
+ yesterday.balanceAssetB = balanceAssetB;
+
+ }
+ }
+
+ marketRate = finalTakeProfit;
+ type = '"Close@TakeProfit"';
+ strategyStage = 'Close Stage';
+ stopLossStage = 'No Stage';
+ takeProfitStage = 'No Stage';
+
+ currentTrade.end = candle.end;
+ currentTrade.status = 1;
+ currentTrade.exitType = 2;
+ currentTrade.endRate = finalTakeProfit;
+
+ closePositionNow = true;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.strategyStage = strategyStage;
+ yesterday.stopLossStage = stopLossStage;
+ yesterday.takeProfitStage = takeProfitStage;
+
+ yesterday.currentTrade.end = currentTrade.end;
+ yesterday.currentTrade.status = currentTrade.status;
+ yesterday.currentTrade.exitType = currentTrade.exitType;
+ yesterday.currentTrade.endRate = currentTrade.endRate;
+ }
+ }
+ }
+ }
+
+ /* Taking a Position */
+ if (
+ takePositionNow === true
+ ) {
+ takePositionNow = false
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Entering code block."); }
+
+ /* Inicializing this counter */
+ distanceToLast.takePosition = 1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.takePosition = distanceToLast.takePosition
+ }
+ }
+
+ /* Position size and rate */
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+
+ tradePositionSize = strategy.positionSize;
+ tradePositionRate = strategy.positionRate;
+
+ /* We take what was calculated at the formula and apply the slippage. */
+ let slippageAmount = tradePositionRate * bot.VALUES_TO_USE.slippage.positionRate / 100
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ tradePositionRate = tradePositionRate - slippageAmount
+ } else {
+ tradePositionRate = tradePositionRate + slippageAmount
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.tradePositionSize = tradePositionSize;
+ yesterday.tradePositionRate = tradePositionRate;
+ }
+ }
+
+ /* Update the trade record information. */
+ currentTrade.begin = candle.begin;
+ currentTrade.beginRate = tradePositionRate;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.currentTrade.begin = currentTrade.begin;
+ yesterday.currentTrade.beginRate = currentTrade.beginRate;
+ }
+ }
+
+ /* Check if we need to execute. */
+ if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/
+ /* Check that we are in LIVE MODE */
+ if (bot.startMode === "Live") {
+ /* We see if we need to put the actual order at the exchange. */
+ if (interExecutionMemory.executionContext !== undefined) {
+ switch (interExecutionMemory.executionContext.status) {
+ case "Without a Position": { // We need to put the order because It was not put yet.
+ if (strategy.openStage !== undefined) {
+ if (strategy.openStage.openExecution !== undefined) {
+ putOpeningOrder()
+ return
+ }
+ }
+ break
+ }
+ case "Position Closed": { // Waiting for a confirmation that the position was closed.
+ if (strategy.openStage !== undefined) {
+ if (strategy.openStage.openExecution !== undefined) {
+ putOpeningOrder()
+ return
+ }
+ }
+ break
+ }
+ case "Taking Position": { // Waiting for a confirmation that the position was taken.
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is Taking Position."); }
+ break
+ }
+ case "In a Position": { // This should mean that we already put the order at the exchange.
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is In a Position."); }
+ break
+ }
+ }
+ } else { // The context does not exist so it means we are not in a position.
+ if (strategy.openStage !== undefined) {
+ if (strategy.openStage.openExecution !== undefined) {
+ putOpeningOrder()
+ return
+ }
+ }
+ }
+ } else {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Not trading live."); }
+ }
+ } else {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionNow -> Not the last closed candle."); }
+ }
+
+ takePositionAtSimulation()
+ return
+
+ function putOpeningOrder() {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function."); }
+
+ /* We wont take a position unless we are withing the bot.VALUES_TO_USE.timeRange.initialDatetime and the bot.VALUES_TO_USE.timeRange.finalDatetime range */
+ if (bot.VALUES_TO_USE.timeRange.initialDatetime !== undefined) {
+ if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> bot.VALUES_TO_USE.timeRange.initialDatetime = " + bot.VALUES_TO_USE.timeRange.initialDatetime); }
+ takePositionAtSimulation()
+ return;
+ }
+ }
+
+ /*We wont take a position if we are past the final datetime */
+ if (bot.VALUES_TO_USE.timeRange.finalDatetime !== undefined) {
+ if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> bot.VALUES_TO_USE.timeRange.finalDatetime = " + bot.VALUES_TO_USE.timeRange.finalDatetime); }
+ takePositionAtSimulation()
+ return;
+ }
+ }
+
+
+ /* Checking the status of current positions */
+ let positions = assistant.getPositions();
+ if (positions.length > 0) {
+ let position = positions[positions.length - 1] // We are allways checking the the last position is not open.
+ if (position.status === 'open') {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the last position is still open. "); }
+ afterLoop();
+ return
+ }
+ }
+
+ /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */
+
+ if (interExecutionMemory.executionContext !== undefined) {
+ if (interExecutionMemory.executionContext.periods !== undefined) {
+ if (periods <= interExecutionMemory.executionContext.periods) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution."); }
+ takePositionAtSimulation()
+ return;
+ }
+ }
+ }
+
+ /* We are not going to place orders based on outdated information. The next filter prevents firing orders when backtesting. */
+ if (currentDay) {
+ let today = new Date(Math.trunc((new Date().valueOf()) / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS)
+ let processDay = new Date(Math.trunc(currentDay.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS)
+ if (today.valueOf() !== processDay.valueOf()) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the current candle belongs to the previous day and that is considered simulation and not live trading."); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> today = " + today); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> processDay = " + processDay); }
+ takePositionAtSimulation()
+ return;
+ }
+ }
+
+ let openPositionRate
+ let amountA
+ let amountB
+ let positionDirection
+ let availableBalance = assistant.getAvailableBalance()
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ positionDirection = "sell"
+
+ openPositionRate = tradePositionRate - 100 // 100 is Provisional since we do have management of orders yet
+
+ amountA = tradePositionSize * openPositionRate
+ amountB = tradePositionSize
+
+ if (amountB > availableBalance.assetB) { // The assistant know what fees were paid.
+ amountB = availableBalance.assetB
+ }
+ } else {
+ positionDirection = "buy"
+
+ openPositionRate = tradePositionRate + 100 // 100 is Provisional since we do have management of orders yet
+
+ amountA = tradePositionSize
+ amountB = tradePositionSize / openPositionRate
+
+ if (amountA > availableBalance.assetA) { // The assistant know what fees were paid.
+ amountA = availableBalance.assetA
+ }
+ }
+
+ interExecutionMemory.executionContext = {
+ status: "Taking Position",
+ periods: periods,
+ }
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to put order."); }
+ assistant.putPosition(positionDirection, openPositionRate, amountA, amountB, onOrderPut)
+
+ function onOrderPut(err) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Entering function."); }
+
+ try {
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); }
+ interExecutionMemory.executionContext = {
+ status: "In a Position",
+ periods: periods,
+ amountA: amountA,
+ amountB: amountB
+ }
+ takePositionAtSimulation()
+ return;
+ }
+ case global.DEFAULT_FAIL_RESPONSE.result: {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Message = " + err.message); }
+ strategy.openStage.openExecution.error = err.message
+ afterLoop()
+ return;
+ }
+ case global.DEFAULT_RETRY_RESPONSE.result: {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Message = " + err.message); }
+ strategy.openStage.openExecution.error = err.message
+ afterLoop()
+ return;
+ }
+ }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); }
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ }
+ }
+
+ function takePositionAtSimulation() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionAtSimulation -> Entering function."); }
+
+ /* Continue with the simulation */
+ calculateTakeProfit();
+ calculateStopLoss();
+
+ marketRate = candle.close;
+
+ previousBalanceAssetA = balanceAssetA;
+ previousBalanceAssetB = balanceAssetB;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.previousBalanceAssetA = previousBalanceAssetA;
+ yesterday.previousBalanceAssetB = previousBalanceAssetB;
+ }
+ }
+
+ lastTradeProfitLoss = 0;
+ lastTradeROI = 0;
+
+ let feePaid = 0
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+
+ feePaid = tradePositionSize * tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100
+
+ balanceAssetB = balanceAssetB + tradePositionSize * tradePositionRate - feePaid;
+ balanceAssetA = balanceAssetA - tradePositionSize;
+ } else {
+
+ feePaid = tradePositionSize / tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100
+
+ balanceAssetA = balanceAssetA + tradePositionSize / tradePositionRate - feePaid;
+ balanceAssetB = balanceAssetB - tradePositionSize;
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.balanceAssetA = balanceAssetA;
+ yesterday.balanceAssetB = balanceAssetB;
+
+ yesterday.lastTradeProfitLoss = lastTradeProfitLoss;
+ yesterday.lastTradeROI = lastTradeROI;
+ }
+ }
+
+ addRecord();
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> takePositionAtSimulation -> Exiting Loop Body after taking position at simulation."); }
+ controlLoop();
+ return
+ }
+ }
+
+ if (closePositionNow === true) {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Entering code block."); }
+
+ closePositionNow = false
+
+ /* Inicializing this counter */
+ distanceToLast.closePosition = 1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.closePosition = distanceToLast.closePosition
+ }
+ }
+
+ /* Position size and rate */
+ let strategy = tradingSystem.strategies[currentStrategyIndex];
+
+ if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not.*/
+ /* Check that we are in LIVE MODE */
+ if (bot.startMode === "Live") {
+ /* We see if we need to put the actual order at the exchange. */
+ if (interExecutionMemory.executionContext !== undefined) {
+ switch (interExecutionMemory.executionContext.status) {
+ case "Without a Position": { // No way to close anything at the exchange.
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Without a Position."); }
+ break
+ }
+ case "In a Position": { // This should mean that we already put the order at the exchange.
+ if (strategy.closeStage !== undefined) {
+ if (strategy.closeStage.closeExecution !== undefined) {
+ putClosingOrder()
+ return
+ }
+ }
+ break
+ }
+ case "Closing Position": { // Waiting for a confirmation that the position was taken.
+ if (strategy.closeStage !== undefined) {
+ if (strategy.closeStage.closeExecution !== undefined) {
+ putClosingOrder()
+ return
+ }
+ }
+ break
+ }
+
+ case "Position Closed": { //
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Position Closed."); }
+ break
+ }
+ }
+ }
+ }
+ } else {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing a Position -> Not within the last 10 candles."); }
+ }
+
+ closePositionAtSimulation()
+ return
+
+ function putClosingOrder() {
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> Entering function."); }
+
+ /* Checking the status of current positions */
+ let positions = assistant.getPositions();
+ if (positions.length > 0) {
+ let position = positions[positions.length - 1] // We are allways checking the the last position is not open.
+ if (position.status === 'open') {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because status of last position is Open."); }
+ afterLoop();
+ return
+ }
+ }
+
+ /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */
+
+ if (interExecutionMemory.executionContext !== undefined) {
+ if (interExecutionMemory.executionContext.periods !== undefined) {
+ if (periods <= interExecutionMemory.executionContext.periods) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution."); }
+ closePositionAtSimulation()
+ return;
+ }
+ }
+ }
+
+ let closePositionRate
+ let amountA
+ let amountB
+ let positionDirection
+ let availableBalance = assistant.getAvailableBalance()
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ positionDirection = "buy"
+
+ closePositionRate = ticker.last + 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff.
+
+ amountA = availableBalance.assetA
+ amountB = availableBalance.assetA / closePositionRate
+
+ } else {
+ positionDirection = "sell"
+
+ closePositionRate = ticker.last - 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff.
+
+ amountA = availableBalance.assetB * closePositionRate
+ amountB = availableBalance.assetB
+
+ }
+
+ interExecutionMemory.executionContext = {
+ status: "Closing Position",
+ periods: periods,
+ }
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange."); }
+ assistant.putPosition(positionDirection, closePositionRate, amountA, amountB, onOrderPut)
+
+ function onOrderPut(err) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> Entering function."); }
+
+ try {
+ switch (err.result) {
+ case global.DEFAULT_OK_RESPONSE.result: {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); }
+ interExecutionMemory.executionContext = {
+ status: "Position Closed",
+ periods: periods,
+ amountA: amountA,
+ amountB: amountB
+ }
+ closePositionAtSimulation()
+ return;
+ }
+ case global.DEFAULT_FAIL_RESPONSE.result: {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); }
+ /* We will assume that the problem is temporary, and expect that it will work at the next execution.*/
+ strategy.closeStage.closeExecution.error = err.message
+ afterLoop()
+ return;
+ }
+ case global.DEFAULT_RETRY_RESPONSE.result: {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); }
+ strategy.closeStage.closeExecution.error = err.message
+ afterLoop()
+ return;
+ }
+ }
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); }
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+
+ } catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderPut -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ return
+ }
+ }
+ }
+
+ function closePositionAtSimulation() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> closePositionAtSimulation -> Entering function."); }
+
+ roundtrips++;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.roundtrips = roundtrips
+ }
+ }
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ lastTradeProfitLoss = balanceAssetA - previousBalanceAssetA;
+ lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize;
+ if (isNaN(lastTradeROI)) { lastTradeROI = 0; }
+ profit = balanceAssetA - bot.VALUES_TO_USE.initialBalanceA;
+ } else {
+ lastTradeProfitLoss = balanceAssetB - previousBalanceAssetB;
+ lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize;
+ if (isNaN(lastTradeROI)) { lastTradeROI = 0; }
+ profit = balanceAssetB - bot.VALUES_TO_USE.initialBalanceB;
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.lastTradeProfitLoss = lastTradeProfitLoss;
+ yesterday.profit = profit;
+ yesterday.lastTradeROI = lastTradeROI;
+ }
+ }
+
+ currentTrade.lastTradeROI = lastTradeROI;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.currentTrade.lastTradeROI = currentTrade.lastTradeROI;
+ }
+ }
+
+ if (lastTradeProfitLoss > 0) {
+ hits++;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.hits = hits
+ }
+ }
+
+ } else {
+ fails++;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.fails = fails
+ }
+ }
+ }
+
+ if (bot.VALUES_TO_USE.baseAsset === 'BTC') {
+ ROI = (bot.VALUES_TO_USE.initialBalanceA + profit) / bot.VALUES_TO_USE.initialBalanceA - 1;
+ hitRatio = hits / roundtrips;
+ anualizedRateOfReturn = ROI / days * 365;
+ } else {
+ ROI = (bot.VALUES_TO_USE.initialBalanceB + profit) / bot.VALUES_TO_USE.initialBalanceB - 1;
+ hitRatio = hits / roundtrips;
+ anualizedRateOfReturn = ROI / days * 365;
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.ROI = ROI;
+ yesterday.hitRatio = hitRatio;
+ yesterday.anualizedRateOfReturn = anualizedRateOfReturn;
+ }
+ }
+
+ addRecord();
+
+ stopLoss = 0;
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.stopLoss = stopLoss
+ }
+ }
+ takeProfit = 0;
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.takeProfit = takeProfit
+ }
+ }
+
+ tradePositionRate = 0;
+ tradePositionSize = 0;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.tradePositionSize = tradePositionSize;
+ yesterday.tradePositionRate = tradePositionRate;
+ }
+ }
+
+ timerToCloseStage = candle.begin
+ stopLossStage = 'No Stage';
+ takeProfitStage = 'No Stage';
+ stopLossPhase = -1;
+ takeProfitPhase = -1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.stopLossStage = stopLossStage;
+ yesterday.takeProfitStage = takeProfitStage;
+ yesterday.stopLossPhase = stopLossPhase;
+ yesterday.takeProfitPhase = takeProfitPhase;
+ }
+ }
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> closePositionAtSimulation -> Exiting Loop Body after closing position at simulation."); }
+ controlLoop();
+ return
+ }
+ }
+
+ /* Closing the Closing Stage */
+ if (strategyStage === 'Close Stage') {
+ if (candle.begin - 5 * 60 * 1000 > timerToCloseStage) {
+
+ currentStrategy.number = currentStrategyIndex
+ currentStrategy.end = candle.end;
+ currentStrategy.endRate = candle.min;
+ currentStrategy.status = 1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+
+ yesterday.currentStrategy.number = currentStrategy.number;
+ yesterday.currentStrategy.end = currentStrategy.end;
+ yesterday.currentStrategy.endRate = currentStrategy.endRate;
+ yesterday.currentStrategy.status = currentStrategy.status;
+ }
+ }
+
+ currentStrategyIndex = -1;
+ strategyStage = 'No Stage';
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.currentStrategyIndex = currentStrategyIndex;
+ yesterday.strategyStage = strategyStage;
+ }
+ }
+
+ timerToCloseStage = 0
+
+ distanceToLast.triggerOff = 1;
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.distanceToLast.triggerOff = distanceToLast.triggerOff
+ }
+ }
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing the Closing Stage -> Exiting Close Stage."); }
+ } else {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing the Closing Stage -> Waiting for timer."); }
+ }
+ }
+
+ /* Not a buy or sell condition */
+
+ marketRate = candle.close;
+ addRecord();
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Exiting Loop Body after adding a record."); }
+ controlLoop();
+ return
+
+ function addRecord() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> addRecord -> Entering function."); }
+
+ // Since we are going to write the message to a file that the Simulation Executor is going to read, we use the abbreviations.
+ let messageType;
+ let message;
+ let simulationRecord;
+ let executionRecord;
+
+ let strategyStageNumber
+ switch (strategyStage) {
+ case 'No Stage': {
+ strategyStageNumber = 0
+ break
+ }
+ case 'Trigger Stage': {
+ strategyStageNumber = 1
+ break
+ }
+ case 'Open Stage': {
+ strategyStageNumber = 2
+ break
+ }
+ case 'Manage Stage': {
+ strategyStageNumber = 3
+ break
+ }
+ case 'Close Stage': {
+ strategyStageNumber = 4
+ break
+ }
+ }
+
+ if (balanceAssetA === Infinity) {
+ balanceAssetA = Number.MAX_SAFE_INTEGER
+ }
+
+ if (balanceAssetB === Infinity) {
+ balanceAssetB = Number.MAX_SAFE_INTEGER
+ }
+
+ let quotedBaseAsset = '"' + bot.VALUES_TO_USE.baseAsset + '"'
+
+ simulationRecord = {
+ begin: candle.begin,
+ end: candle.end,
+ type: type,
+ marketRate: marketRate,
+ amount: 1,
+ balanceA: balanceAssetA,
+ balanceB: balanceAssetB,
+ profit: profit,
+ lastTradeProfitLoss: lastTradeProfitLoss,
+ stopLoss: stopLoss,
+ roundtrips: roundtrips,
+ hits: hits,
+ fails: fails,
+ hitRatio: hitRatio,
+ ROI: ROI,
+ periods: periods,
+ days: days,
+ anualizedRateOfReturn: anualizedRateOfReturn,
+ positionRate: tradePositionRate,
+ lastTradeROI: lastTradeROI,
+ strategy: currentStrategyIndex,
+ strategyStageNumber: strategyStageNumber,
+ takeProfit: takeProfit,
+ stopLossPhase: stopLossPhase,
+ takeProfitPhase: takeProfitPhase,
+ executionRecord: '',
+ positionSize: tradePositionSize,
+ initialBalanceA: bot.VALUES_TO_USE.initialBalanceA,
+ minimumBalanceA: bot.VALUES_TO_USE.minimumBalanceA,
+ maximumBalanceA: bot.VALUES_TO_USE.maximumBalanceA,
+ initialBalanceB: bot.VALUES_TO_USE.initialBalanceB,
+ minimumBalanceB: bot.VALUES_TO_USE.minimumBalanceB,
+ maximumBalanceB: bot.VALUES_TO_USE.maximumBalanceB,
+ baseAsset: quotedBaseAsset,
+ positionPeriods: positionPeriods,
+ positionDays: positionDays,
+ distanceToLastTriggerOn: distanceToLast.triggerOn,
+ distanceToLastTriggerOff: distanceToLast.triggerOff,
+ distanceToLastTakePosition: distanceToLast.takePosition,
+ distanceToLastClosePosition: distanceToLast.closePosition
+ }
+
+ recordsArray.push(simulationRecord);
+
+ type = '""';
+
+ /* Prepare the information for the Conditions File */
+
+ conditionsArrayRecord.push(currentStrategyIndex);
+ conditionsArrayRecord.push(strategyStageNumber);
+ conditionsArrayRecord.push(stopLossPhase);
+ conditionsArrayRecord.push(takeProfitPhase);
+ conditionsArrayRecord.push(conditionsArrayValues);
+ conditionsArrayRecord.push(formulasErrors);
+ conditionsArrayRecord.push(formulasValues);
+
+ conditionsArray.push(conditionsArrayRecord);
+
+ /* Prepare the information for the Strategies File*/
+
+ if (
+ (currentStrategy.begin !== 0 && currentStrategy.end !== 0) ||
+ (currentStrategy.begin !== 0 && currentCandleIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay)
+ ) {
+
+ strategiesArray.push(currentStrategy);
+
+ currentStrategy = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ number: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.currentStrategy = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ number: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+ }
+ }
+ }
+
+ /* Prepare the information for the Trades File */
+
+ if (
+ (currentTrade.begin !== 0 && currentTrade.end !== 0) ||
+ (currentTrade.begin !== 0 && currentCandleIndex === candles.length - 1 && lastCandle.end !== lastInstantOfTheDay)
+ ) {
+
+ currentTrade.profit = lastTradeProfitLoss;
+
+ tradesArray.push(currentTrade);
+
+ currentTrade = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ lastTradeROI: 0,
+ exitType: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+
+ if (processingDailyFiles) {
+ if (positionedAtYesterday) {
+ yesterday.currentTrade = {
+ begin: 0,
+ end: 0,
+ status: 0,
+ lastTradeROI: 0,
+ exitType: 0,
+ beginRate: 0,
+ endRate: 0
+ }
+ }
+ }
+ }
+ }
+
+ function checkAnnouncements(node, value) {
+ /*
+ Value is an optional parameter that represents the value that the announcement is monitoring for change.
+ If we do receive this value, we will only make the annoucement if the variance is grater than the user pre-defined value
+ for this variance.
+ */
+
+ if (node.announcements !== undefined) {
+ for (let i = 0; i < node.announcements.length; i++) {
+ let announcement = node.announcements[i]
+ let key = node.type + "-" + announcement.name + "-" + announcement.id
+
+ let lastPeriodAnnounced = -1
+ let newAnnouncementRecord = {}
+
+ for (let j = 0; j < interExecutionMemory.announcements.length; j++) {
+ let announcementRecord = interExecutionMemory.announcements[j]
+ if (announcementRecord.key === key) {
+ lastPeriodAnnounced = announcementRecord.periods
+ newAnnouncementRecord = announcementRecord
+ break
+ }
+ }
+
+ if (periods > lastPeriodAnnounced) {
+
+ /* The Value Variation is what tells us how much the value already announced must change in order to annouce it again. */
+ let valueVariation
+ try {
+ let code = JSON.parse(announcement.code)
+ valueVariation = code.valueVariation
+ } catch (err) {
+ announcement.error = err.message
+ }
+
+ if (newAnnouncementRecord.value !== undefined && valueVariation !== undefined) {
+ let upperLimit = newAnnouncementRecord.value + newAnnouncementRecord.value * valueVariation / 100
+ let lowerLimit = newAnnouncementRecord.value - newAnnouncementRecord.value * valueVariation / 100
+ if (value > lowerLimit && value < upperLimit) {
+ /* There is not enough variation to announce this again. */
+ return
+ }
+ }
+
+ /* Here we check if there is a formula attached to the annoucement, we evaluate it to get the annoucement text. */
+ let formulaValue
+ if (announcement.formula !== undefined) {
+ try {
+ formulaValue = eval(announcement.formula.code);
+ } catch (err) {
+ announcement.formula.error = err.message
+ }
+ }
+ announcement.formulaValue = formulaValue
+
+ bot.SESSION.socialBots.announce(announcement)
+
+ /* Next, we will remmeber this announcement was already done, so that it is not announced again in further processing of the same day. */
+ if (newAnnouncementRecord.periods !== undefined) {
+ newAnnouncementRecord.periods = periods
+ newAnnouncementRecord.value = value
+ } else {
+ newAnnouncementRecord = {
+ key: key,
+ periods: periods,
+ value: value
+ }
+ interExecutionMemory.announcements.push(newAnnouncementRecord)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function controlLoop() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> Entering function."); }
+
+ /* Checking if we should continue processing this loop or not.*/
+ if (bot.STOP_SESSION === true) {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing."); }
+ console.log("[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing.")
+ afterLoop()
+ return
+ }
+
+ currentCandleIndex++
+ if (currentCandleIndex < candles.length) {
+ setImmediate(loop) // This will execute the next loop in the next iteration of the NodeJs event loop allowing for other callbacks to be executed.
+ } else {
+ afterLoop()
+ }
+ }
+
+ function afterLoop() {
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> afterLoop -> Entering function."); }
+
+ /*
+ Before returning we need to see if we have to record some of our counters at the interExecutionMemory.
+ To do that, the condition to be met is that this execution must include all candles of the current day.
+ */
+
+ if (processingDailyFiles) {
+
+ if (lastCandle.end === lastInstantOfTheDay) {
+
+ interExecutionMemory.stopLoss = yesterday.stopLoss
+ interExecutionMemory.takeProfit = yesterday.takeProfit
+
+ interExecutionMemory.previousBalanceAssetA = yesterday.previousBalanceAssetA
+ interExecutionMemory.previousBalanceAssetB = yesterday.previousBalanceAssetB
+
+ interExecutionMemory.tradePositionRate = yesterday.tradePositionRate
+ interExecutionMemory.tradePositionSize = yesterday.tradePositionSize
+
+ interExecutionMemory.currentStrategyIndex = yesterday.currentStrategyIndex
+ interExecutionMemory.strategyStage = yesterday.strategyStage
+
+ interExecutionMemory.stopLossPhase = yesterday.stopLossPhase
+ interExecutionMemory.stopLossStage = yesterday.stopLossStage
+
+ interExecutionMemory.takeProfitPhase = yesterday.takeProfitPhase
+ interExecutionMemory.takeProfitStage = yesterday.takeProfitStage
+
+ interExecutionMemory.currentStrategy = {
+ begin: yesterday.currentStrategy.begin,
+ end: yesterday.currentStrategy.end,
+ status: yesterday.currentStrategy.status,
+ number: yesterday.currentStrategy.number,
+ beginRate: yesterday.currentStrategy.beginRate,
+ endRate: yesterday.currentStrategy.endRate
+ }
+
+ interExecutionMemory.currentTrade = {
+ begin: yesterday.currentTrade.begin,
+ end: yesterday.currentTrade.end,
+ status: yesterday.currentTrade.status,
+ profit: yesterday.currentTrade.profit,
+ exitType: yesterday.currentTrade.exitType,
+ beginRate: yesterday.currentTrade.beginRate,
+ endRate: yesterday.currentTrade.endRate
+ }
+
+ interExecutionMemory.balanceAssetA = yesterday.balanceAssetA;
+ interExecutionMemory.balanceAssetB = yesterday.balanceAssetB;
+ interExecutionMemory.lastTradeProfitLoss = yesterday.lastTradeProfitLoss;
+ interExecutionMemory.profit = yesterday.profit;
+ interExecutionMemory.lastTradeROI = yesterday.lastTradeROI;
+
+ interExecutionMemory.roundtrips = yesterday.roundtrips;
+ interExecutionMemory.fails = yesterday.fails;
+ interExecutionMemory.hits = yesterday.hits;
+ interExecutionMemory.periods = yesterday.periods;
+ interExecutionMemory.positionPeriods = yesterday.positionPeriods;
+
+ interExecutionMemory.distanceToLast.triggerOn = yesterday.distanceToLast.triggerOn
+ interExecutionMemory.distanceToLast.triggerOff = yesterday.distanceToLast.triggerOff
+ interExecutionMemory.distanceToLast.takePosition = yesterday.distanceToLast.takePosition
+ interExecutionMemory.distanceToLast.closePosition = yesterday.distanceToLast.closePosition
+
+ interExecutionMemory.messageId = interExecutionMemory.messageId + yesterday.messageId;
+ interExecutionMemory.orderId = interExecutionMemory.orderId + yesterday.orderId;
+
+ interExecutionMemory.hitRatio = yesterday.hitRatio;
+ interExecutionMemory.ROI = yesterday.ROI;
+ interExecutionMemory.anualizedRateOfReturn = yesterday.anualizedRateOfReturn;
+ }
+ }
+
+ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> callback -> recordsArray.length = " + recordsArray.length); }
+
+ callback(tradingSystem, recordsArray, conditionsArray, strategiesArray, tradesArray);
+ }
+
+ function getElement(pArray, currentCandle, datasetName) {
+
+ try {
+ let element;
+ for (let i = 0; i < pArray.length; i++) {
+ element = pArray[i];
+
+ if (currentCandle.end === element.end) { // when there is an exact match at the end we take that element
+ return element
+ } else {
+ if (
+ i > 0 &&
+ element.end > currentCandle.end
+ ) {
+ let previousElement = pArray[i - 1]
+ if (previousElement.end < currentCandle.end) {
+ return previousElement // If one elements goes into the future of currentCandle, then we stop and take the previous element.
+ } else {
+ return
+ }
+ }
+ if (
+ i === pArray.length - 1 // If we reach the end of the array, then we return the last element.
+ &&
+ element.end < currentCandle.end
+ ) {
+ return element
+ }
+ }
+ }
+ return
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] runSimulation -> getElement -> datasetName = " + datasetName);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+ }
+ catch (err) {
+ logger.write(MODULE_NAME, "[ERROR] runSimulation -> err = " + err.stack);
+ callBackFunction(global.DEFAULT_FAIL_RESPONSE);
+ }
+ }
+};
+
+
+
diff --git a/server.js b/server.js
index 82a8fcdc7..22415c444 100644
--- a/server.js
+++ b/server.js
@@ -53,35 +53,37 @@ process.on('exit', function (code) {
console.log('[INFO] Task Server -> server -> process.on.exit -> About to exit -> code = ' + code)
})
-/*
+/* Here we listen for the message to stop this Task / Process comming from the Task Manager, which is the paret of this node js process. */
+process.on('message', message => {
+ if (message === 'Stop this Task') {
-We read the first string sent as an argument when the process was created by the Task Manager. Ther we will find the information of the identity
-of this Task and know exactly what to run within this server instance.
+ global.STOP_TASK_GRACEFULLY = true;
-*/
-global.TASK_NODE = process.argv[2]
+ /*
+ There are some process that might no be able to end grafully, for example the ones schedulle to process information in a future day or month.
+ In order to be sure that the process will be terminated, we schedulle one forced exit in 2 minutes from now.
+ */
+ console.log('[INFO] Task Server -> server -> process.on -> Executing order received from Task Manager to Stop this Task. Nodejs process will be exited in less than 1 minute.')
+ setTimeout(global.EXIT_NODE_PROCESS, 60000);
+ }
+});
-if (global.TASK_NODE !== undefined) {
+global.EXIT_NODE_PROCESS = function exitProcess() {
- try {
- global.TASK_NODE = JSON.parse(global.TASK_NODE)
- console.log('[INFO] Task Server -> server -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE))
- } catch (err) {
- console.log('[ERROR] Task Server -> server -> global.TASK_NODE -> ' + err.stack)
+ /* Cleaning Before Exiting. */
+ clearInterval(global.HEARTBEAT_INTERVAL_HANDLER)
+
+ for (let i = 0; i < global.TASK_NODE.bot.processes.length; i++) {
+ let code = global.TASK_NODE.bot.processes[i].code
+ let process = global.TASK_NODE.bot.processes[i]
+
+ key = process.name + '-' + process.type + '-' + process.id
+ global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Stopped') // Meaning Process Stopped
}
-}
-else { // I use this section to debug in standalone mode.
- let argument = '{"type":"Task","name":"Runs Backtests","bot":{"type":"Trading Engine","code":{"team":"AAMasters","bot":"AAJason","repo":"AAJason-Trading-Engine-Bot"},"processes":[{"type":"Process","subType":"Trading Engine Process","name":"Multi Period","code":{"process":"Multi-Period"},"session":{"type":"Backtesting Session","name":"Backtest 01","code":{"folderName":"Backtest-01"},"parameters":{"type":"Parameters","name":"Parameters","baseAsset":{"type":"Base Asset","name":"Base Asset","code":{"name":"BTC","initialBalance":0.001,"minimumBalance":0.0001,"maximumBalance":1},"id":"666f6f10-2f17-4f5e-951c-b532bdb574d2"},"timeRange":{"type":"Time Range","name":"Time Range","code":{"initialDatetime":"2019-09-24T00:00:00.000Z","finalDatetime":"2019-09-27T00:00:00.000Z"},"id":"172958dd-b268-4444-8163-70979538ea6e"},"timePeriod":{"type":"Time Period","name":"Time Period","code":"10-min","id":"37ee839d-9a7c-410c-9251-c33b8f1b99f7"},"slippage":{"type":"Slippage","name":"Slippage","code":{"positionRate":0.1,"stopLoss":0.1,"takeProfit":0.1},"id":"86ebd973-6ad9-4070-aece-a87623f24c2e"},"feeStructure":{"type":"Fee Structure","name":"Fee Structure","code":{"maker":0.15,"taker":0.25},"id":"a0ec3f6f-6047-4740-87fe-393b1c714adb"},"key":{"type":"Exchange Account Key","name":"New Key","code":"Paste your exchange API secret key here and the put the key name as this key object title. Secret keys are filtered out and NOT exported when using the SHARE menu option on any object at your workspace. Secret keys ARE downloaded when using the download button.","id":"368f879d-e363-47c7-b55e-7b927d896b20"},"id":"09db5e1e-a7e8-468c-ad70-0d2ab1c8b8b8"},"layerManager":{"type":"Layer Manager","name":"New Layer Manager","layers":[{"type":"Layer","name":"Live","code":{"product":"Live-Trading-History"},"id":"0ec05e1c-230b-44a1-9f2c-fba0ea2ff9e2"},{"type":"Layer","name":"Trades","code":{"product":"Simulation-Trades"},"id":"6326e03a-96c0-4d21-b877-f2c6a0419fc5"},{"type":"Layer","name":"Strategies","code":{"product":"Simulation-Strategies"},"id":"bda1e06c-e569-4bd5-95fb-90154aafb003"},{"type":"Layer","name":"Simulation","code":{"product":"Trading-Simulation"},"id":"d58d8cd2-f125-4f0c-b241-08e2af8ac4be"},{"type":"Layer","name":"Formulas & Conditions","code":{"product":"Simulation-Conditions"},"id":"15dfeca0-acd9-40d3-afe0-4805654bf0d0"}],"id":"884c4dd7-4beb-43fb-bda5-d6097d58ae09"},"socialBots":{"type":"Social Bots","name":"New Social Bots","bots":[],"id":"281eb022-cf9f-4ac2-978b-c8358bee8fec"},"id":"121202ea-6735-4bf3-8a95-aaa55f00f6e7"},"id":"3f483482-d8a1-4755-ba50-7cac6296fa5f"}],"id":"25fe9099-b53d-4757-8a63-3f8a15875f33"},"id":"5b21d2d2-0f52-4408-83eb-ddaa844ec58c"}'
- /* charly argument = '{"type":"Task","name":"Brings Trades Records from the Exchange","bot":{"type":"Sensor","name":"Charly","processes":[{"type":"Process","name":"Live Trades","code":{"team":"AAMasters","bot":"AACharly","process":"Live-Trades"},"id":"5846ebc7-1979-4a80-9cc7-94bb4b8659dc"},{"type":"Process","name":"Hole Fixing","code":{"team":"AAMasters","bot":"AACharly","process":"Hole-Fixing"},"id":"31fc4f05-75d4-419a-9fb0-73e25e856f15"}]},"id":"5bfef4dc-54c1-44db-ace6-1ab27a86746e"}'
- // olivia argument = '{"type":"Task","name":"Generates 1 min to 24 hs Candles & Volumes","bot":{"type":"Indicator","name":"Olivia","processes":[{"type":"Process","name":"Daily","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Daily"},"id":"68cc8e1b-e94a-477e-82d0-15ecc9f1b9e2"},{"type":"Process","name":"Market","code":{"team":"AAMasters","bot":"AAOlivia","process":"Multi-Period-Market"},"id":"e1ac5e3d-d491-4e90-baf4-d4e5b71a8b1a"}]},"id":"efe36e05-75ea-41b5-8d92-c6b27635c834"}'
- // bruce argument = ' {"type":"Task","name":"Converts Trades into 1 min Candles & Volumes","bot":{"type":"Indicator","name":"Bruce","processes":[{"type":"Process","name":"New Process","code":{"team":"AAMasters","bot":"AABruce","process":"Single-Period-Daily"},"id":"e184744f-5de0-41c1-ad4c-36960f4ced90"}]},"id":"42758590-a7bd-4712-a5c9-80a3a820c5b3"}'
- */
- try {
- global.TASK_NODE = JSON.parse(argument)
- } catch (err) {
- console.log(err.stack)
- }
+ console.log("[INFO] Task Server -> " + global.TASK_NODE.name + " -> EXIT_NODE_PROCESS -> Task Server will stop in 10 seconds.");
+
+ setTimeout(process.exit, 10000) // We will give 10 seconds to logs be written on file
}
require('dotenv').config();
@@ -102,54 +104,54 @@ amount of instances started, we can safelly destroy the rest of the objects runn
global.ENDED_PROCESSES_COUNTER = 0
global.TOTAL_PROCESS_INSTANCES_CREATED = 0
-/* Here we listen for the message to stop this Task / Process comming from the Task Manager, which is the paret of this node js process. */
-process.on('message', message => {
- if (message === 'Stop this Task') {
+/*
- global.STOP_TASK_GRACEFULLY = true;
+We read the first string sent as an argument when the process was created by the Task Manager. There we will find the information of the identity
+of this Task and know exactly what to run within this server instance.
- /*
- There are some process that might no be able to end grafully, for example the ones schedulle to process information in a future day or month.
- In order to be sure that the process will be terminated, we schedulle one forced exit in 2 minutes from now.
- */
- console.log('[INFO] Task Server -> server -> process.on -> Executing order received from Task Manager to Stop this Task. Nodejs process will be exited in less than 1 minute.')
- setTimeout(global.EXIT_NODE_PROCESS, 60000);
- }
-});
-
-global.EXIT_NODE_PROCESS = function exitProcess() {
-
- /* Cleaning Before Exiting. */
- clearInterval(global.HEARTBEAT_INTERVAL_HANDLER)
-
- for (let i = 0; i < global.TASK_NODE.bot.processes.length; i++) {
- let code = global.TASK_NODE.bot.processes[i].code
-
- /* Delete the event handler for each process. */
-
- let key = global.TASK_NODE.bot.code.team + "-" + global.TASK_NODE.bot.code.bot + "-" + code.process
-
- global.SYSTEM_EVENT_HANDLER.deleteEventHandler(key)
-
- let process = global.TASK_NODE.bot.processes[i]
-
- key = process.name + '-' + process.type + '-' + process.id
- global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Stopped') // Meaning Process Stopped
- }
-
- console.log("[INFO] Task Server -> " + global.TASK_NODE.name + " -> EXIT_NODE_PROCESS -> Task Server will stop in 10 seconds.");
-
- setTimeout(process.exit, 10000) // We will give 10 seconds to logs be written on file
-}
+*/
+let taskId = process.argv[2] // reading what comes as an argument of the nodejs process.
/* Setting up the global Event Handler */
const EVENT_HANDLER_MODULE = require('./SystemEventHandler.js');
const IPC = require('node-ipc');
global.SYSTEM_EVENT_HANDLER = EVENT_HANDLER_MODULE.newSystemEventHandler(IPC)
-global.SYSTEM_EVENT_HANDLER.initialize('Task Server', bootLoader)
+global.SYSTEM_EVENT_HANDLER.initialize('Task Server', preLoader)
global.STOP_TASK_GRACEFULLY = false;
+function preLoader() {
+ if (taskId !== undefined) {
+ /* The Task Manager sent the info via a process argument. In this case we listen to an event with the Task Info that should be emitted at the UI */
+ try {
+ console.log('[INFO] Task Server -> server -> preLoader -> Listening to starting event -> key = ' + 'Task Server - ' + taskId)
+ global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server - ' + taskId, 'Run Task', undefined, undefined, undefined, eventReceived)
+ function eventReceived(message) {
+ global.TASK_NODE = message
+ global.TASK_NODE = JSON.parse(message.event.definition)
+ bootLoader()
+ }
+ } catch (err) {
+ console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack)
+ console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE))
+ }
+ }
+ else { /* This process was started not by the Task Manager, but independently (most likely for debugging purposes). In this case we listen to an event with the Task Info that should be emitted at the UI */
+ try {
+ console.log('[INFO] Task Server -> server -> preLoader -> Waiting for event to start debugging...')
+ global.SYSTEM_EVENT_HANDLER.listenToEvent('Task Server', 'Debug Task Started', undefined, undefined, undefined, startDebugging)
+ function startDebugging(message) {
+ global.TASK_NODE = message
+ global.TASK_NODE = JSON.parse(message.event.definition)
+ bootLoader()
+ }
+ } catch (err) {
+ console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack)
+ console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE = ' + JSON.stringify(global.TASK_NODE))
+ }
+ }
+}
+
function bootLoader() {
/* Heartbeat sent to the UI */
@@ -174,28 +176,34 @@ function bootLoader() {
/* Validate that the minimun amount of parameters required are defined. */
- if (global.TASK_NODE.bot.code.bot === undefined) {
- console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'bot' at the Indicator | Sensor | Trading Engine is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name);
+ if (global.TASK_NODE.bot.processes[processIndex].referenceParent === undefined) {
+ console.log("[INFO] Task Server -> server -> bootLoader -> Process Instance without a Reference Parent. This process will not be executed. -> Process Instance = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex]));
continue
}
- if (global.TASK_NODE.bot.code.team === undefined) {
- console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'team' at the bot is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name);
+ if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode === undefined) {
+ console.log("[INFO] Task Server -> server -> bootLoader -> Process Definition without parent Bot Definition. -> Process Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent));
continue
}
- if (global.TASK_NODE.bot.processes[processIndex].code.process === undefined) {
- console.log("[INFO] Task Server -> server -> bootLoader -> Parameter 'process' at object Process is undefined. This process will not be executed. -> Process = " + global.TASK_NODE.bot.processes[processIndex].name);
+ if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode === undefined) {
+ console.log("[INFO] Task Server -> server -> bootLoader -> Bot Definition without parent Team. -> Bot Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode));
continue
}
- /* Create the event handler for each process. This event handlers are where the status reports updated events are raised. */
+ if (global.TASK_NODE.bot.processes[processIndex].referenceParent.code.codeName === undefined) {
+ console.log("[INFO] Task Server -> server -> bootLoader -> Process Definition without a codeName defined. -> Process Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent));
+ continue
+ }
- let key = global.TASK_NODE.bot.code.team + "-" + global.TASK_NODE.bot.code.bot + "-" + code.process
- global.SYSTEM_EVENT_HANDLER.createEventHandler(key)
+ if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.code.codeName === undefined) {
+ console.log("[INFO] Task Server -> server -> bootLoader -> Bot Definition without a codeName defined. -> Bot Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode));
+ continue
+ }
- if (global.TASK_NODE.bot.code.repo === undefined) {
- global.TASK_NODE.bot.code.repo = global.TASK_NODE.bot.code.bot + "-" + global.TASK_NODE.bot.type + "-Bot"
+ if (global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.code.codeName === undefined) {
+ console.log("[INFO] Task Server -> server -> bootLoader -> Team without a codeName defined. -> Team Definition = " + JSON.stringify(global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode));
+ continue
}
startRoot(processIndex);