330 lines
18 KiB
JavaScript
330 lines
18 KiB
JavaScript
exports.newIndicatorBotMarket = function newIndicatorBotMarket(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
|
|
|
|
const FULL_LOG = true;
|
|
const LOG_FILE_CONTENT = false;
|
|
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
|
|
|
|
const MODULE_NAME = "User Bot";
|
|
|
|
const BOLLINGER_CHANNELS_FOLDER_NAME = "Bollinger-Channels";
|
|
const BOLLINGER_STANDARD_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Channels";
|
|
const BOLLINGER_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Sub-Channels";
|
|
const BOLLINGER_STANDARD_SUB_CHANNELS_FOLDER_NAME = "Bollinger-Standard-Sub-Channels";
|
|
|
|
const commons = COMMONS.newIndicatorBotCommons(bot, logger, UTILITIES);
|
|
|
|
thisObject = {
|
|
initialize: initialize,
|
|
start: start
|
|
};
|
|
|
|
let fileStorage = FILE_STORAGE.newFileStorage(logger);
|
|
let processConfig;
|
|
|
|
return thisObject;
|
|
|
|
function initialize(pProcessConfig, callBackFunction) {
|
|
|
|
try {
|
|
|
|
logger.fileName = MODULE_NAME;
|
|
logger.initialize();
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
|
|
|
processConfig = pProcessConfig;
|
|
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, outputPeriodLabel, startDate, endDate, callBackFunction) {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
|
|
|
|
let market = global.MARKET;
|
|
let products = {}
|
|
let mainDependency
|
|
|
|
/* The first phase is about transforming the inputs into a format that can be used to apply the user defined code. */
|
|
let dataDependencies = bot.processNode.referenceParent.processDependencies.dataDependencies
|
|
for (let i = 0; i < dataDependencies.length; i++) {
|
|
|
|
let dataDependencyNode = dataDependencies[i].referenceParent
|
|
let dataFile = dataFiles[i]
|
|
let jsonData // Datafile converted into Json objects
|
|
let inputData // Includes calculated properties
|
|
let singularVariableName // name of the variable for this product
|
|
let recordDefinition
|
|
|
|
/*
|
|
For each dataDependencyNode in our data dependencies, we should have da 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.
|
|
*/
|
|
|
|
/* Basic validations to see if we have everything we need. */
|
|
if (dataDependencyNode.parentNode.code.singularVariableName === undefined) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Single Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode));
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
return
|
|
}
|
|
if (dataDependencyNode.parentNode.code.pluralVariableName === undefined) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Plural Variable Name defined. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode));
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
return
|
|
}
|
|
if (dataDependencyNode.parentNode.record === undefined) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> Product Definition without a Record Definition. Product Definition = " + JSON.stringify(dataDependencyNode.parentNode));
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
return
|
|
}
|
|
|
|
recordDefinition = dataDependencyNode.parentNode.record
|
|
singularVariableName = dataDependencyNode.parentNode.code.singularVariableName
|
|
/* Transform the raw data into JSON objects */
|
|
jsonData = commons.jsonifyDataFile(dataFile, recordDefinition)
|
|
/* Add the calculated properties */
|
|
if (dataDependencyNode.parentNode.calculations !== undefined) {
|
|
inputData = commons.calculationsProcedure(jsonData, recordDefinition, dataDependencyNode.parentNode.calculations, singularVariableName, timePeriod)
|
|
} else {
|
|
inputData = jsonData
|
|
}
|
|
products[dataDependencyNode.parentNode.code.pluralVariableName] = inputData
|
|
|
|
/* The main dependency is defined as the first dependency processed. */
|
|
if (mainDependency === undefined) {
|
|
mainDependency = inputData
|
|
}
|
|
}
|
|
|
|
/* During the second phase, we need to generate the data of the different products this process produces an output */
|
|
let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets
|
|
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.
|
|
|
|
/*
|
|
For each outputDatasetNode in our process output, we will build the information based on our input products.
|
|
*/
|
|
|
|
/* Basic validations to see if we have everything we need. */
|
|
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, "[ERROR] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode));
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
return
|
|
}
|
|
|
|
recordDefinition = outputDatasetNode.referenceParent.parentNode.record
|
|
singularVariableName = outputDatasetNode.referenceParent.parentNode.code.singularVariableName
|
|
|
|
/* Build the data */
|
|
jsonData = commons.dataBuildingProcedure(products, mainDependency, recordDefinition, outputDatasetNode.referenceParent.parentNode.dataBuilding, singularVariableName, timePeriod)
|
|
|
|
/* 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 third 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 contextSummary = {}
|
|
|
|
/* Some very basic validations that we have all the information needed. */
|
|
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
|
|
}
|
|
|
|
/* 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 = generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record)
|
|
writeFile(contextSummary, fileContent)
|
|
}
|
|
|
|
function anotherFileWritten() {
|
|
totalFilesWritten++
|
|
if (totalFilesWritten === processConfig.updatesDatasets.length) {
|
|
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function generateFileContent(records, recordDefinition) {
|
|
|
|
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];
|
|
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.isNumeric !== true) {
|
|
fileContent = fileContent + '"'
|
|
}
|
|
fileContent = fileContent + record[property.code.codeName]
|
|
if (property.code.isNumeric !== 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) {
|
|
|
|
try {
|
|
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFile -> Entering function."); }
|
|
|
|
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
|
|
|
|
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 + "/" + outputPeriodLabel;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
};
|