diff --git a/.vs/AACloudPlatform/v15/.suo b/.vs/AACloudPlatform/v15/.suo
index 4703b39c6..871593d71 100644
Binary files a/.vs/AACloudPlatform/v15/.suo and b/.vs/AACloudPlatform/v15/.suo differ
diff --git a/AACloudPlatform.njsproj b/AACloudPlatform.njsproj
index 081717a47..6f6b1681b 100644
--- a/AACloudPlatform.njsproj
+++ b/AACloudPlatform.njsproj
@@ -25,6 +25,9 @@
Code
+
+ Code
+
Code
diff --git a/Context.js b/Context.js
index 10833d0e8..867466340 100644
--- a/Context.js
+++ b/Context.js
@@ -376,7 +376,6 @@
}
}
-
} catch (err) {
logger.write("[ERROR] saveAll -> Error = " + err.message);
callBackFunction("Operation Failed.");
diff --git a/Datasource.js b/Datasource.js
new file mode 100644
index 000000000..aadf5fd5b
--- /dev/null
+++ b/Datasource.js
@@ -0,0 +1,362 @@
+exports.newDatasource = function newDatasource(BOT) {
+
+ /*
+
+ This module allows trading bots to access information stored at the cloud storage.
+
+ */
+
+ const MODULE_NAME = "Datasource";
+
+ thisObject = {
+ initialize: initialize,
+ candlesFiles: new Map,
+ candlesMap: new Map,
+ stairsFiles: new Map,
+ stairsMap: new Map
+ };
+
+ let bot = BOT;
+
+ const DEBUG_MODULE = require('./Debug Log');
+ const logger = DEBUG_MODULE.newDebugLog();
+ logger.fileName = MODULE_NAME;
+
+ return thisObject;
+
+ function initialize(callBackFunction) {
+
+ try {
+
+ getCandles(onCandlesReady);
+
+ function onCandlesReady() {getPatterns(onPatternsReady); }
+
+ function onDailyFileonPatternsReadysReady() {callBackFunction();}
+
+ } catch (err) {
+
+ logger.write("[ERROR] initialize -> err = " + err);
+ callBackFunction("Operation Failed.");
+ }
+ }
+
+ function getCandles(callBack) {
+
+ try {
+
+ /*
+
+ We will read several files with candles for the current day. We will use these files as an input
+ to make trading decitions later.
+
+ */
+
+ getMarketFiles();
+
+ function getMarketFiles() {
+
+ try {
+ /* Now we will get the market files */
+
+ for (i = 0; i < marketFilesPeriods.length; i++) {
+
+ let periodTime = marketFilesPeriods[i][0];
+ let periodName = marketFilesPeriods[i][1];
+
+ getFile(oliviaAzureFileStorage, "@AssetA_@AssetB.json", "@Exchange/Output/Candles/Multi-Period-Market/@Period", periodName, undefined, onFileReceived);
+
+ function onFileReceived(file) {
+
+ thisObject.candlesFiles.set(periodName, file);
+
+ if (thisObject.candlesFiles.size === marketFilesPeriods.length) {
+
+ getDailyFiles();
+
+ }
+ }
+ }
+ } catch (err) {
+ logger.write("[ERROR] getCandles -> getMarketFiles -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ function getDailyFiles(callBack) {
+
+ try {
+ for (i = 0; i < dailyFilePeriods.length; i++) {
+
+ let periodTime = dailyFilePeriods[i][0];
+ let periodName = dailyFilePeriods[i][1];
+
+ getFile(oliviaAzureFileStorage, "@AssetA_@AssetB.json", "@Exchange/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day", periodName, processDatetime, onFileReceived);
+
+ function onFileReceived(file) {
+
+ thisObject.candlesFiles.set(periodName, file);
+
+ if (thisObject.candlesFiles.size === dailyFilePeriods.length + marketFilesPeriods.length) {
+
+ getCandlesWeAreIn();
+
+ }
+ }
+ }
+ } catch (err) {
+ logger.write("[ERROR] getCandles -> getDailyFiles -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ function getCandlesWeAreIn() {
+
+ try {
+ let counter = 0;
+
+ thisObject.candlesFiles.forEach(getCurrentCandles);
+
+ function getCurrentCandles(pCandlesFile, pPeriodName, map) {
+
+ let candlesArray = [];
+
+ for (i = 0; i < pCandlesFile.length; i++) {
+
+ let candle = {
+ open: undefined,
+ close: undefined,
+ min: 10000000000000,
+ max: 0,
+ begin: undefined,
+ end: undefined,
+ direction: undefined
+ };
+
+ candle.min = pCandlesFile[i][0];
+ candle.max = pCandlesFile[i][1];
+
+ candle.open = pCandlesFile[i][2];
+ candle.close = pCandlesFile[i][3];
+
+ candle.begin = pCandlesFile[i][4];
+ candle.end = pCandlesFile[i][5];
+
+ if (candle.open > candle.close) { candle.direction = 'down'; }
+ if (candle.open < candle.close) { candle.direction = 'up'; }
+ if (candle.open === candle.close) { candle.direction = 'side'; }
+
+ /* We are going to store the last 10 candles per period, which will give the bot a good sense of where it is. */
+
+ let timePeriod = candle.end - candle.begin + 1; // In miliseconds. (remember each candle spans a period minus one milisecond)
+
+ if (candle.begin >= processDatetime.valueOf() - timePeriod * 10 && candle.end <= processDatetime.valueOf()) {
+
+ candlesArray.push(candle);
+
+ }
+ }
+
+ thisObject.candlesMap.set(pPeriodName, candlesArray);
+
+ counter++;
+
+ if (counter === thisObject.candlesFiles.size) {
+
+ callBack();
+
+ }
+ }
+ } catch (err) {
+ logger.write("[ERROR] getCandles -> getCandlesWeAreIn -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ } catch (err) {
+ logger.write("[ERROR] getCandles -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ function getPatterns(callBack) {
+
+ try {
+ /*
+
+ We will read several files with pattern calculations for the current day. We will use these files as an input
+ to make trading decitions later.
+
+ */
+
+ getMarketFiles();
+
+ function getMarketFiles() {
+
+ try {
+ /* Now we will get the market files */
+
+ for (i = 0; i < marketFilesPeriods.length; i++) {
+
+ let periodTime = marketFilesPeriods[i][0];
+ let periodName = marketFilesPeriods[i][1];
+
+ getFile(tomAzureFileStorage, "@AssetA_@AssetB.json", "@Exchange/Tom/dataSet.V1/Output/Candle-Stairs/Multi-Period-Market/@Period", periodName, undefined, onFileReceived);
+
+ function onFileReceived(file) {
+
+ thisObject.stairsFiles.set(periodName, file);
+
+ if (thisObject.stairsFiles.size === marketFilesPeriods.length) {
+
+ getDailyFiles();
+
+ }
+ }
+ }
+ } catch (err) {
+ logger.write("[ERROR] getPatterns -> getMarketFiles -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ function getDailyFiles() {
+
+ try {
+ for (i = 0; i < dailyFilePeriods.length; i++) {
+
+ let periodTime = dailyFilePeriods[i][0];
+ let periodName = dailyFilePeriods[i][1];
+
+ getFile(tomAzureFileStorage, "@AssetA_@AssetB.json", "@Exchange/Tom/dataSet.V1/Output/Candle-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day", periodName, processDatetime, onFileReceived);
+
+ function onFileReceived(file) {
+
+ thisObject.stairsFiles.set(periodName, file);
+
+ if (thisObject.stairsFiles.size === dailyFilePeriods.length + marketFilesPeriods.length) {
+
+ getStairsWeAreIn();
+
+ }
+ }
+ }
+ } catch (err) {
+ logger.write("[ERROR] getPatterns -> getDailyFiles -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ function getStairsWeAreIn() {
+
+ try {
+ let counter = 0;
+
+ thisObject.stairsFiles.forEach(getCurrentStairs);
+
+ function getCurrentStairs(pStairsFile, pPeriodName, map) {
+
+ for (i = 0; i < pStairsFile.length; i++) {
+
+ let stairs = {
+ open: undefined,
+ close: undefined,
+ min: 10000000000000,
+ max: 0,
+ begin: undefined,
+ end: undefined,
+ direction: undefined,
+ candleCount: 0,
+ firstMin: 0,
+ firstMax: 0,
+ lastMin: 0,
+ lastMax: 0
+ };
+
+ stairs.open = pStairsFile[i][0];
+ stairs.close = pStairsFile[i][1];
+
+ stairs.min = pStairsFile[i][2];
+ stairs.max = pStairsFile[i][3];
+
+ stairs.begin = pStairsFile[i][4];
+ stairs.end = pStairsFile[i][5];
+
+ stairs.direction = pStairsFile[i][6];
+ stairs.candleCount = pStairsFile[i][7];
+
+ stairs.firstMin = pStairsFile[i][8];
+ stairs.firstMax = pStairsFile[i][9];
+
+ stairs.lastMin = pStairsFile[i][10];
+ stairs.lastMax = pStairsFile[i][11];
+
+ if (processDatetime.valueOf() >= stairs.begin && processDatetime.valueOf() <= stairs.end) {
+
+ thisObject.stairsMap.set(pPeriodName, stairs);
+
+ }
+ }
+
+ counter++;
+
+ if (counter === thisObject.stairsFiles.size) {
+
+ callBack();
+
+ }
+ }
+ } catch (err) {
+ logger.write("[ERROR] getPatterns -> getStairsWeAreIn -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ } catch (err) {
+ logger.write("[ERROR] getPatterns -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+
+ function getFile(pFileService, pFileName, pFilePath, pPeriodName, pDatetime, callBackFunction) {
+ try {
+ pFileName = pFileName.replace("@AssetA", market.assetA);
+ pFileName = pFileName.replace("@AssetB", market.assetB);
+
+ pFilePath = pFilePath.replace("@Exchange", EXCHANGE_NAME);
+ pFilePath = pFilePath.replace("@Period", pPeriodName);
+
+ if (pDatetime !== undefined) {
+
+ pFilePath = pFilePath.replace("@Year", pDatetime.getUTCFullYear());
+ pFilePath = pFilePath.replace("@Month", utilities.pad(pDatetime.getUTCMonth() + 1, 2));
+ pFilePath = pFilePath.replace("@Day", utilities.pad(pDatetime.getUTCDate(), 2));
+
+ }
+
+ pFileService.getTextFile(pFilePath, pFileName, onFileReceived);
+
+ function onFileReceived(text) {
+
+ let data;
+
+ try {
+
+ data = JSON.parse(text);
+
+ } catch (err) {
+
+ data = JSON.parse("[]");
+
+ }
+
+ callBackFunction(data);
+
+ }
+
+ } catch (err) {
+ logger.write("[ERROR] getPatterns -> err = " + err);
+ callBack("Operation Failed.");
+ }
+ }
+};
\ No newline at end of file