Dependencies Implemented

This commit is contained in:
Luis Fernando Molina committed 2018-03-17 13:38:46 +01:00
1 parent f13e648b8b
commit 06690bdc18
5 files changed
+192 -333

No files matched your search

Binary file not shown.
+2 -241
View File
@@ -9,8 +9,7 @@
const GMT_SECONDS = ':00.000 GMT+0000';
let thisObject = {
initializeStorage: initializeStorage,
getContextVariables: getContextVariables
initializeStorage: initializeStorage
};
let bot = BOT;
@@ -23,7 +22,7 @@
return thisObject;
function initializeStorage(charlyFileStorage, bruceFileStorage, oliviaFileStorage, callBackFunction) {
function initializeStorage(oliviaFileStorage, callBackFunction) {
try {
@@ -33,40 +32,6 @@
function initializeCharlyStorage() {
charlyFileStorage.initialize("AACharly", onCharlyInizialized);
function onCharlyInizialized(err) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
initializeBruceStorage();
} else {
logger.write("[ERROR] initializeStorage -> initializeCharlyStorage -> onCharlyInizialized -> err = " + err.message);
callBackFunction(err);
}
}
}
function initializeBruceStorage() {
bruceFileStorage.initialize("AABruce", onBruceInizialized);
function onBruceInizialized(err) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
initializeOliviaStorage();
} else {
logger.write("[ERROR] initializeStorage -> initializeBruceStorage -> onBruceInizialized -> err = " + err.message);
callBackFunction(err);
}
}
}
function initializeOliviaStorage() {
oliviaFileStorage.initialize("AAOlivia", onOliviaInizialized);
function onOliviaInizialized(err) {
@@ -88,208 +53,4 @@
}
}
function getContextVariables(charlyFileStorage, bruceFileStorage, oliviaFileStorage, contextVariables, statusReportModule, callBackFunction) {
try {
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> Entering function."); }
let reportFilePath;
let fileName = "Status.Report." + global.MARKET.assetA + '_' + global.MARKET.assetB + ".json"
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> fileName = " + fileName); }
getHistoricTrades();
function getHistoricTrades() {
try {
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getHistoricTrades -> Entering function."); }
/*
We need to know where is the begining of the market, since that will help us know where the Index Files should start.
*/
reportFilePath = EXCHANGE_NAME + "/Processes/" + "Poloniex-Historic-Trades";
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getHistoricTrades -> reportFilePath = " + reportFilePath); }
charlyFileStorage.getTextFile(reportFilePath, fileName, onFileReceived);
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getHistoricTrades -> onFileReceived -> Entering function."); }
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getHistoricTrades -> onFileReceived -> text = " + text); }
let thisReport;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
thisReport = JSON.parse(text);
contextVariables.firstTradeFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + thisReport.lastFile.hours + ":" + thisReport.lastFile.minutes + GMT_SECONDS);
getOneMinDailyCandlesVolumes();
} catch (err) {
logger.write("[ERROR] getContextVariables -> getHistoricTrades -> onFileReceived -> err = " + err.message);
logger.write("[ERROR] getContextVariables -> getHistoricTrades -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
} else {
logger.write("[ERROR] getContextVariables -> getHistoricTrades -> onFileReceived -> err = " + err.message);
callBackFunction(err);
}
}
}
catch (err) {
logger.write("[ERROR] getContextVariables -> getHistoricTrades -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function getOneMinDailyCandlesVolumes() {
try {
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getOneMinDailyCandlesVolumes -> Entering function."); }
/* We need to discover the maxCandle file, which is the last file with candles we can use as input. */
let date = new Date();
let currentYear = date.getUTCFullYear();
let currentMonth = utilities.pad(date.getUTCMonth() + 1, 2);
reportFilePath = EXCHANGE_NAME + "/Processes/" + "One-Min-Daily-Candles-Volumes" + "/" + currentYear + "/" + currentMonth;
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getOneMinDailyCandlesVolumes -> reportFilePath = " + reportFilePath); }
bruceFileStorage.getTextFile(reportFilePath, fileName, onFileReceived);
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getOneMinDailyCandlesVolumes -> onFileReceived -> Entering function."); }
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getOneMinDailyCandlesVolumes -> onFileReceived -> text = " + text); }
let thisReport;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
thisReport = JSON.parse(text);
contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + "00:00" + GMT_SECONDS);
getThisProcessReport();
} catch (err) {
logger.write("[ERROR] getContextVariables -> getOneMinDailyCandlesVolumes -> onFileReceived -> err = " + err.message);
logger.write("[ERROR] getContextVariables -> getOneMinDailyCandlesVolumes -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
} else {
logger.write("[ERROR] getContextVariables -> getOneMinDailyCandlesVolumes -> onFileReceived -> err = " + err.message);
callBackFunction(err);
}
}
}
catch (err) {
logger.write("[ERROR] getContextVariables -> getOneMinDailyCandlesVolumes -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function getThisProcessReport() {
try {
if (FULL_LOG === true) { logger.write("[INFO] getContextVariables -> getThisProcessReport -> Entering function."); }
statusReportModule.initialize(bot, onInitilized);
function onInitilized(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write("[ERROR] getContextVariables -> getThisProcessReport -> err = " + err.message);
callBackFunction(err);
return;
}
statusReportModule.load(onLoad);
}
function onLoad(err) {
try {
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write("[INFO] getContextVariables -> getThisProcessReport -> onInitilized -> Execution finished well. :-)");
contextVariables.lastCandleFile = new Date(statusReportModule.file.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS);
let customOk = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Status Report did exist."
};
callBackFunction(customOk);
return;
}
case global.CUSTOM_OK_RESPONSE.result: { // We need to see if we can handle this.
logger.write("[ERROR] getContextVariables -> getThisProcessReport -> onInitilized -> err.message = " + err.message);
if (err.message === "Status Report was never created.") {
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
let customOk = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Status Report did not exist."
};
callBackFunction(customOk);
return;
} else {
callBackFunction(err); // we cant handle this here.
}
return;
}
default:
{
logger.write("[ERROR] getContextVariables -> getThisProcessReport -> onInitilized -> Operation Failed.");
callBackFunction(err);
return;
}
}
}
catch (err) {
logger.write("[ERROR] getContextVariables -> getThisProcessReport -> onInitilized -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write("[ERROR] getContextVariables -> getThisProcessReport -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write("[ERROR] getContextVariables -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
+62 -45
View File
@@ -1,4 +1,4 @@
exports.newUserBot = function newUserBot(BOT, COMMONS, UTILITIES, DEBUG_MODULE, FILE_STORAGE, STATUS_REPORT) {
exports.newUserBot = function newUserBot(BOT, COMMONS, UTILITIES, DEBUG_MODULE, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
@@ -32,33 +32,25 @@
start: start
};
let charlyFileStorage = FILE_STORAGE.newFileStorage(bot);
let bruceFileStorage = FILE_STORAGE.newFileStorage(bot);
let oliviaFileStorage = FILE_STORAGE.newFileStorage(bot);
let utilities = UTILITIES.newUtilities(bot);
let statusReportModule = STATUS_REPORT.newStatusReport(BOT, DEBUG_MODULE, FILE_STORAGE, UTILITIES);
let dependencies;
return thisObject;
function initialize(yearAssigend, monthAssigned, callBackFunction) {
function initialize(pDependencies, callBackFunction) {
try {
/* IMPORTANT NOTE:
We are ignoring in this UserBot the received Year and Month. thisObject is not depending on Year Month since it procecess the whole market at once.
*/
logger.fileName = MODULE_NAME;
if (FULL_LOG === true) { logger.write("[INFO] initialize -> Entering function."); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> yearAssigend = " + yearAssigend); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> monthAssigned = " + monthAssigned); }
commons.initializeStorage(charlyFileStorage, bruceFileStorage, oliviaFileStorage, onInizialized);
dependencies = pDependencies;
commons.initializeStorage(oliviaFileStorage, onInizialized);
function onInizialized(err) {
@@ -98,51 +90,73 @@ Read the candles and volumes from Bruce and produce a file for each day and for
/* Context Variables */
contextVariables = {
lastCandleFile: undefined, // Datetime of the last file included on the Index Files.
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be included in the Index File.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
commons.getContextVariables(charlyFileStorage, bruceFileStorage, oliviaFileStorage, contextVariables, statusReportModule, onVariablesReady);
getContextVariables();
function onVariablesReady(err) {
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write("[INFO] start -> onVariablesReady -> Entering function."); }
if (FULL_LOG === true) { logger.write("[INFO] start -> getContextVariables -> Entering function."); }
if (err.result === global.CUSTOM_OK_RESPONSE.result) {
let thisReport;
let reportKey;
switch (err.message) {
case 'Status Report did exist.': {
logger.write("[INFO] start -> onVariablesReady -> Status Report did exist.");
reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Poloniex-Historic-Trades" + "-" + "dataSet.V1";
thisReport = dependencies.statusReports.get(reportKey).file;
buildCandles();
return;
}
case 'Status Report did not exist.': {
logger.write("[ERROR] start -> onVariablesReady -> Status Report did not exist.");
if (thisReport.lastFile === undefined) {
logger.write("[ERROR] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
buildCandles();
return;
}
default:
{
logger.write("[ERROR] start -> onVariablesReady -> Operation Failed.");
callBackFunction(err);
return;
}
}
contextVariables.firstTradeFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + thisReport.lastFile.hours + ":" + thisReport.lastFile.minutes + GMT_SECONDS);
reportKey = "AAMasters" + "-" + "AABruce" + "-" + "One-Min-Daily-Candles-Volumes" + "-" + "dataSet.V1";
thisReport = dependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write("[ERROR] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + thisReport.lastFile.hours + ":" + thisReport.lastFile.minutes + GMT_SECONDS);
reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1";
thisReport = dependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS);
buildCandles();
return;
} else {
logger.write("[ERROR] start -> onVariablesReady -> Operation Failed.");
callBackFunction(err);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
} catch (err) {
logger.write("[ERROR] start -> onVariablesReady -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
logger.write("[ERROR] start -> getContextVariables -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
@@ -604,9 +618,12 @@ Read the candles and volumes from Bruce and produce a file for each day and for
try {
statusReportModule.file.lastExecution = global.processDatetime;
statusReportModule.file.lastFile = lastFileDate;
statusReportModule.save(callBack);
let reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1";
let thisReport = dependencies.statusReports.get(key);
thisReport.file.lastExecution = global.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.save(callBack);
}
catch (err) {
+62 -45
View File
@@ -1,4 +1,4 @@
exports.newUserBot = function newUserBot(BOT, COMMONS, UTILITIES, DEBUG_MODULE, FILE_STORAGE, STATUS_REPORT) {
exports.newUserBot = function newUserBot(BOT, COMMONS, UTILITIES, DEBUG_MODULE, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
@@ -32,33 +32,25 @@
start: start
};
let charlyFileStorage = FILE_STORAGE.newFileStorage(bot);
let bruceFileStorage = FILE_STORAGE.newFileStorage(bot);
let oliviaFileStorage = FILE_STORAGE.newFileStorage(bot);
let utilities = UTILITIES.newUtilities(bot);
let statusReportModule = STATUS_REPORT.newStatusReport(BOT, DEBUG_MODULE, FILE_STORAGE, UTILITIES);
let dependencies;
return thisObject;
function initialize(yearAssigend, monthAssigned, callBackFunction) {
function initialize(pDependencies, callBackFunction) {
try {
/* IMPORTANT NOTE:
We are ignoring in this UserBot the received Year and Month. thisObject is not depending on Year Month since it procecess the whole market at once.
*/
logger.fileName = MODULE_NAME;
if (FULL_LOG === true) { logger.write("[INFO] initialize -> Entering function."); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> yearAssigend = " + yearAssigend); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> monthAssigned = " + monthAssigned); }
commons.initializeStorage(charlyFileStorage, bruceFileStorage, oliviaFileStorage, onInizialized);
dependencies = pDependencies;
commons.initializeStorage(oliviaFileStorage, onInizialized);
function onInizialized(err) {
@@ -98,51 +90,73 @@ Read the candles and volumes from Bruce and produce a single Index File for Mark
/* Context Variables */
contextVariables = {
lastCandleFile: undefined, // Datetime of the last file included on the Index Files.
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be included in the Index File.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
commons.getContextVariables(charlyFileStorage, bruceFileStorage, oliviaFileStorage, contextVariables, statusReportModule, onVariablesReady);
getContextVariables();
function onVariablesReady(err) {
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write("[INFO] start -> onVariablesReady -> Entering function."); }
if (FULL_LOG === true) { logger.write("[INFO] start -> getContextVariables -> Entering function."); }
if (err.result === global.CUSTOM_OK_RESPONSE.result) {
let thisReport;
let reportKey;
switch (err.message) {
case 'Status Report did exist.': {
logger.write("[INFO] start -> onVariablesReady -> Status Report did exist.");
reportKey = "AAMasters" + "-" + "AACharly" + "-" + "Poloniex-Historic-Trades" + "-" + "dataSet.V1";
thisReport = dependencies.statusReports.get(reportKey).file;
findPreviousContent();
return;
}
case 'Status Report did not exist.': {
logger.write("[ERROR] start -> onVariablesReady -> Status Report did not exist.");
if (thisReport.lastFile === undefined) {
logger.write("[ERROR] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
buildCandles();
return;
}
default:
{
logger.write("[ERROR] start -> onVariablesReady -> Operation Failed.");
callBackFunction(err);
return;
}
}
contextVariables.firstTradeFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + thisReport.lastFile.hours + ":" + thisReport.lastFile.minutes + GMT_SECONDS);
reportKey = "AAMasters" + "-" + "AABruce" + "-" + "One-Min-Daily-Candles-Volumes" + "-" + "dataSet.V1";
thisReport = dependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write("[ERROR] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + thisReport.lastFile.hours + ":" + thisReport.lastFile.minutes + GMT_SECONDS);
reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1";
thisReport = dependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS);
findPreviousContent();
return;
} else {
logger.write("[ERROR] start -> onVariablesReady -> Operation Failed.");
callBackFunction(err);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
} catch (err) {
logger.write("[ERROR] start -> onVariablesReady -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
logger.write("[ERROR] start -> getContextVariables -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
@@ -791,9 +805,12 @@ Read the candles and volumes from Bruce and produce a single Index File for Mark
try {
statusReportModule.file.lastExecution = global.processDatetime;
statusReportModule.file.lastFile = lastFileDate;
statusReportModule.save(callBack);
let reportKey = "AAMasters" + "-" + "AAOlivia" + "-" + "Multi-Period-Market" + "-" + "dataSet.V1";
let thisReport = dependencies.statusReports.get(key);
thisReport.file.lastExecution = global.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.save(callBack);
}
catch (err) {
+66 -2
View File
@@ -29,7 +29,39 @@
}
},
"executionWaitTime": 3600000,
"retryWaitTime": 10000
"retryWaitTime": 10000,
"dependencies": [
{
"devTeam": "AAMasters",
"bot": "AACharly",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Poloniex-Historic-Trades",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AABruce",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "One-Min-Daily-Candles-Volumes",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Market",
"dataSetVersion": "dataSet.V1"
}
]
},
{
"name": "Multi-Period-Daily",
@@ -50,7 +82,39 @@
}
},
"executionWaitTime": 60000,
"retryWaitTime": 10000
"retryWaitTime": 10000,
"dependencies": [
{
"devTeam": "AAMasters",
"bot": "AACharly",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Poloniex-Historic-Trades",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AABruce",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "One-Min-Daily-Candles-Volumes",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Daily",
"dataSetVersion": "dataSet.V1"
}
]
}
],
"products": [