Allows Paralell Run on Live and Backtest from different IDEs

This commit is contained in:
Luis Fernando Molina committed 2018-04-28 10:37:40 +02:00
1 parent 6cc0e83d65
commit afb781e442
7 files changed
+466 -730

No files matched your search

Binary file not shown.
+234 -111
View File
@@ -91,78 +91,128 @@
try {
candleArray = JSON.parse(text);
if (bot.backTestingMode === true) {
switch (bot.runMode) {
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Backtest Mode detected.");
case "Live": {
/* We need to find the candle at which the process is currently running. */
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Live Mode detected.");
for (let i = 0; i < candleArray.length; i++) {
let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate.
let candle = {
open: candleArray[i][2],
close: candleArray[i][3],
min: candleArray[i][0],
max: candleArray[i][1],
begin: candleArray[i][4],
end: candleArray[i][5]
open: lastCandleRecord[2],
close: lastCandleRecord[3],
min: lastCandleRecord[0],
max: lastCandleRecord[1],
begin: lastCandleRecord[4],
end: lastCandleRecord[5]
};
if (bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) {
marketRate = candle.close;
marketRate = Number(marketRate.toFixed(8));
marketRate = (candle.open + candle.close) / 2;
marketRate = Number(marketRate.toFixed(8));
/*
Now we verify that this candle is not too old. Lets say no more than 10 minutes old. This could happen if the datasets for
any reason stops being updated.
*/
/* The Backtest Mode simulates that every trade posted is executed.
In order to do this, we will take all open orders from the context and create a trades array similar to the one returned by the Exchange. */
let delay = (bot.processDatetime.valueOf() - candle.begin) / 1000 / 60;
validateExchangeSyncronicity();
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Market Rate " + delay + " minutes old.");
if (delay > 30) {
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Market Rate more than 10 minutes old. Retrying later.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
}
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> No candle found for the current Process Datetime.");
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> bot.processDatetime = " + bot.processDatetime);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
} else { // We are running LIVE Mode.
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Live Mode detected.");
let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate.
let candle = {
open: lastCandleRecord[2],
close: lastCandleRecord[3],
min: lastCandleRecord[0],
max: lastCandleRecord[1],
begin: lastCandleRecord[4],
end: lastCandleRecord[5]
};
marketRate = candle.close;
marketRate = Number(marketRate.toFixed(8));
/*
Now we verify that this candle is not too old. Lets say no more than 2 minutes old. This could happen if the datasets for
any reason stops being updated.
*/
let delay = (bot.processDatetime.valueOf() - candle.begin) / 1000 / 60;
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Market Rate " + delay + " minutes old." );
if (delay > 30) {
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Market Rate more than 10 minutes old. Retrying later.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
validateExchangeSyncronicity();
return;
}
validateExchangeSyncronicity();
return;
case "Backtest": {
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Backtest Mode detected.");
/* We need to find the candle at which the process is currently running. */
for (let i = 0; i < candleArray.length; i++) {
let candle = {
open: candleArray[i][2],
close: candleArray[i][3],
min: candleArray[i][0],
max: candleArray[i][1],
begin: candleArray[i][4],
end: candleArray[i][5]
};
if (bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) {
marketRate = (candle.open + candle.close) / 2;
marketRate = Number(marketRate.toFixed(8));
/* The Backtest Mode simulates that every trade posted is executed.
In order to do this, we will take all open orders from the context and create a trades array similar to the one returned by the Exchange. */
validateExchangeSyncronicity();
return;
}
}
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> No candle found for the current Process Datetime.");
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> bot.processDatetime = " + bot.processDatetime);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
case "Competition": {
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Competition Mode detected.");
let lastCandleRecord = candleArray[candleArray.length - 1]; // The last candle contains at its close value the market rate.
let candle = {
open: lastCandleRecord[2],
close: lastCandleRecord[3],
min: lastCandleRecord[0],
max: lastCandleRecord[1],
begin: lastCandleRecord[4],
end: lastCandleRecord[5]
};
marketRate = candle.close;
marketRate = Number(marketRate.toFixed(8));
/*
Now we verify that this candle is not too old. Lets say no more than 10 minutes old. This could happen if the datasets for
any reason stops being updated.
*/
let delay = (bot.processDatetime.valueOf() - candle.begin) / 1000 / 60;
logger.write("[INFO] initialize -> getMarketRate -> onFileReceived -> Market Rate " + delay + " minutes old.");
if (delay > 30) {
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Market Rate more than 10 minutes old. Retrying later.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
validateExchangeSyncronicity();
return;
}
default: {
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> Unexpected bot.runMode.");
logger.write("[ERROR] initialize -> getMarketRate -> onFileReceived -> bot.runMode = " + bot.runMode);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
} catch (err) {
@@ -250,18 +300,36 @@
*/
if (bot.backTestingMode === true) {
switch (bot.runMode) {
if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Backtest Mode Detected."); }
case "Live": {
let exchangePositions = []; // We simulate all positions were executed.
if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Live Mode Detected."); }
exchangeAPI.getOpenPositions(global.MARKET, onResponse);
break;
}
onResponse(global.DEFAULT_OK_RESPONSE, exchangePositions);
} else {
case "Backtest": {
if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Live Mode Detected."); }
exchangeAPI.getOpenPositions(global.MARKET, onResponse);
if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Backtest Mode Detected."); }
let exchangePositions = []; // We simulate all positions were executed.
onResponse(global.DEFAULT_OK_RESPONSE, exchangePositions);
break;
}
case "Competition": {
if (FULL_LOG === true) { logger.write("[INFO] getPositionsAtExchange -> Competition Mode Detected."); }
exchangeAPI.getOpenPositions(global.MARKET, onResponse);
break;
}
default: {
logger.write("[ERROR] getPositionsAtExchange -> Unexpected bot.runMode.");
logger.write("[ERROR] getPositionsAtExchange -> bot.runMode = " + bot.runMode);
callBack(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function onResponse(err, pExchangePositions) {
@@ -481,47 +549,66 @@
*/
if (bot.backTestingMode === true) {
switch (bot.runMode) {
if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Backtest Mode Detected."); }
case "Live": {
let trades = [];
if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Live Mode Detected."); }
exchangeAPI.getExecutedTrades(pPositionId, onResponse);
return;
/* We look for the position at the executionContext */
for (let i = 0; i < context.executionContext.positions.length; i++) {
let thisPosition = context.executionContext.positions[i];
if (thisPosition.id === pPositionId) {
let trade = {
id: Math.trunc(Math.random(1) * 1000000),
type: thisPosition.type,
rate: thisPosition.rate,
amountA: thisPosition.amountA,
amountB: thisPosition.amountB,
fee: 0,
date: (new Date()).valueOf()
}
trades.push(trade);
onResponse(global.DEFAULT_OK_RESPONSE, trades);
return;
}
}
logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Position not found at Executioin Context.");
callBack(global.DEFAULT_FAIL_RESPONSE);
return;
case "Backtest": {
} else {
if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Backtest Mode Detected."); }
if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Live Mode Detected."); }
exchangeAPI.getExecutedTrades(pPositionId, onResponse);
let trades = [];
/* We look for the position at the executionContext */
for (let i = 0; i < context.executionContext.positions.length; i++) {
let thisPosition = context.executionContext.positions[i];
if (thisPosition.id === pPositionId) {
let trade = {
id: Math.trunc(Math.random(1) * 1000000),
type: thisPosition.type,
rate: thisPosition.rate,
amountA: thisPosition.amountA,
amountB: thisPosition.amountB,
fee: 0,
date: (new Date()).valueOf()
}
trades.push(trade);
onResponse(global.DEFAULT_OK_RESPONSE, trades);
return;
}
}
logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Position not found at Executioin Context.");
callBack(global.DEFAULT_FAIL_RESPONSE);
return;
}
case "Competition": {
if (FULL_LOG === true) { logger.write("[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Competition Mode Detected."); }
exchangeAPI.getExecutedTrades(pPositionId, onResponse);
return;
}
default: {
logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> Unexpected bot.runMode.");
logger.write("[ERROR] ordersExecutionCheck -> loopBody -> positionNotFound -> getPositionTradesAtExchange -> bot.runMode = " + bot.runMode);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function onResponse(err, pTrades) {
@@ -839,22 +926,40 @@
/* All validations passed, we proceed. */
if (bot.backTestingMode === true) {
switch (bot.runMode) {
if (pRate !== marketRate) {
case "Live": {
logger.write("[ERROR] putPosition -> Input Validations -> putPosition Rate can not be different to marketRate while in Backtesting Mode. ");
onResponse(global.DEFAULT_FAIL_RESPONSE, positionId);
exchangeAPI.putPosition(global.MARKET, pType, pRate, pAmountA, pAmountB, onResponse);
return;
}
let positionId = Math.trunc(Math.random(1) * 1000000);
if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); }
onResponse(global.DEFAULT_OK_RESPONSE, positionId);
case "Backtest": {
} else {
if (pRate !== marketRate) {
exchangeAPI.putPosition(global.MARKET, pType, pRate, pAmountA, pAmountB, onResponse);
logger.write("[ERROR] putPosition -> Input Validations -> putPosition Rate can not be different to marketRate while in Backtesting Mode. ");
onResponse(global.DEFAULT_FAIL_RESPONSE, positionId);
}
let positionId = Math.trunc(Math.random(1) * 1000000);
if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); }
onResponse(global.DEFAULT_OK_RESPONSE, positionId);
return;
}
case "Competition": {
exchangeAPI.putPosition(global.MARKET, pType, pRate, pAmountA, pAmountB, onResponse);
return;
}
default: {
logger.write("[ERROR] putPosition -> Unexpected bot.runMode.");
logger.write("[ERROR] putPosition -> bot.runMode = " + bot.runMode);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function onResponse(err, pPositionId) {
@@ -938,16 +1043,34 @@
if (FULL_LOG === true) { logger.write("[INFO] movePosition -> pPosition = " + JSON.stringify(pPosition)); }
if (FULL_LOG === true) { logger.write("[INFO] movePosition -> pNewRate = " + pNewRate); }
if (bot.backTestingMode === true) {
switch (bot.runMode) {
let positionId = Math.trunc(Math.random(1) * 1000000);
if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); }
onResponse(global.DEFAULT_OK_RESPONSE, positionId);
case "Live": {
} else {
exchangeAPI.movePosition(pPosition, pNewRate, onResponse);
return;
}
exchangeAPI.movePosition(pPosition, pNewRate, onResponse);
case "Backtest": {
let positionId = Math.trunc(Math.random(1) * 1000000);
if (FULL_LOG === true) { logger.write("[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); }
onResponse(global.DEFAULT_OK_RESPONSE, positionId);
return;
}
case "Competition": {
exchangeAPI.movePosition(pPosition, pNewRate, onResponse);
return;
}
default: {
logger.write("[ERROR] movePosition -> Unexpected bot.runMode.");
logger.write("[ERROR] movePosition -> bot.runMode = " + bot.runMode);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function onResponse(err, pPositionId) {
+99 -46
View File
@@ -78,6 +78,8 @@
let statusDependencies;
let runIndex; // This is the index for this run and depends on the runMode.
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
@@ -166,6 +168,34 @@
} else {
switch (bot.runMode) {
case "Live": {
runIndex = thisObject.statusReport.liveRuns.length - 1;
break;
}
case "Backtest": {
runIndex = thisObject.statusReport.backtestRuns.length - 1;
break;
}
case "Competition": {
runIndex = thisObject.statusReport.competitionRuns.length - 1;
break;
}
default: {
logger.write("[ERROR] initialize -> createConext -> Unexpected bot.runMode.");
logger.write("[ERROR] initialize -> createConext -> bot.runMode = " + bot.runMode);
callBack(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
getExecutionHistory(callBack);
}
@@ -182,7 +212,7 @@
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getExecutionHistory -> Entering function."); }
let fileName = "Execution.History." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex + ".json";
let fileName = "Execution.History." + bot.runMode + "." + runIndex + ".json";
let filePath = bot.filePathRoot + "/Output/" + bot.process;
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getExecutionHistory -> fileName = " + fileName); }
@@ -208,9 +238,12 @@
/* Move some values to the new record, in case there are no transactions that re-calculate them. */
thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[thisObject.executionHistory.length - 1][1];
thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[thisObject.executionHistory.length-1][1];
thisObject.newHistoryRecord.sellAvgRate = thisObject.executionHistory[thisObject.executionHistory.length - 1][2];
thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[1];
thisObject.newHistoryRecord.sellAvgRate = thisObject.executionHistory[2];
getExecutionContext(callBack);
} catch (err) {
@@ -241,25 +274,37 @@
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getExecutionContext -> Entering function."); }
let date;
let runType = thisObject.statusReport.runType;
switch (runType) {
switch (bot.runMode) {
case "Backtest": {
date = new Date(thisObject.statusReport.backtestRuns[thisObject.statusReport.runIndex].lastExecution);
thisObject.statusReport.backtestRuns[thisObject.statusReport.runIndex].lastExecution = bot.processDatetime;
case "Live": {
date = new Date(thisObject.statusReport.liveRuns[runIndex].lastExecution);
thisObject.statusReport.liveRuns[runIndex].lastExecution = bot.processDatetime;
thisObject.statusReport.liveRuns[runIndex].endDatetime = bot.processDatetime;
break;
}
case "Live": {
date = new Date(thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].lastExecution);
thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].lastExecution = bot.processDatetime;
thisObject.statusReport.liveRuns[thisObject.statusReport.runIndex].endDatetime = bot.processDatetime;
case "Backtest": {
date = new Date(thisObject.statusReport.backtestRuns[runIndex].lastExecution);
thisObject.statusReport.backtestRuns[runIndex].lastExecution = bot.processDatetime;
break;
}
case "Competition": {
date = new Date(thisObject.statusReport.competitionRuns[runIndex].lastExecution);
thisObject.statusReport.competitionRuns[runIndex].lastExecution = bot.processDatetime;
break;
}
default: {
logger.write("[ERROR] initialize -> getExecutionContext -> Unexpected bot.runMode.");
logger.write("[ERROR] initialize -> getExecutionContext -> bot.runMode = " + bot.runMode);
callBack(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
let fileName = "Execution.Context." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex + ".json";
let fileName = "Execution.Context." + bot.runMode + "." + runIndex + ".json";
let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2);
let filePath = bot.filePathRoot + "/Output/" + bot.process + '/' + dateForPath;
@@ -327,43 +372,18 @@
*/
let runType;
if (bot.backTestingMode === true) {
runType = "Backtest";
} else {
runType = "Live";
}
/* Here, we dont know if the Status Report was ever created or not. To test that we do this. */
if (thisObject.statusReport.runType === undefined) { // This means that the Status Report does not exist.
if (thisObject.statusReport.liveRuns === undefined) { // This means that the Status Report does not exist.
thisObject.statusReport = {
runType: runType,
runIndex: 0,
liveRuns: [],
backtestRuns: []
backtestRuns: [],
competitionRuns: []
};
}
thisObject.statusReport.runType = runType;
switch (runType) {
case "Backtest": {
let runContent = {
beginDatetime: (new Date(bot.timePeriod.beginDatetime)).valueOf(),
endDatetime: (new Date(bot.timePeriod.endDatetime)).valueOf(),
lastExecution: bot.processDatetime
};
thisObject.statusReport.backtestRuns.push(runContent);
thisObject.statusReport.runIndex = thisObject.statusReport.backtestRuns.length - 1;
break;
}
switch (bot.runMode) {
case "Live": {
@@ -374,9 +394,42 @@
};
thisObject.statusReport.liveRuns.push(runContent);
thisObject.statusReport.runIndex = thisObject.statusReport.liveRuns.length - 1;
runIndex = thisObject.statusReport.liveRuns.length - 1;
break;
}
case "Backtest": {
let runContent = {
beginDatetime: (new Date(bot.backtest.beginDatetime)).valueOf(),
endDatetime: (new Date(bot.backtest.endDatetime)).valueOf(),
lastExecution: bot.processDatetime
};
thisObject.statusReport.backtestRuns.push(runContent);
runIndex = thisObject.statusReport.backtestRuns.length - 1;
break;
}
case "Competition": {
let runContent = {
beginDatetime: (new Date(bot.competition.beginDatetime)).valueOf(),
endDatetime: (new Date(bot.competition.endDatetime)).valueOf(),
lastExecution: bot.processDatetime
};
thisObject.statusReport.competitionRuns.push(runContent);
runIndex = thisObject.statusReport.competitionRuns.length - 1;
break;
}
default: {
logger.write("[ERROR] initialize -> createConext -> Unexpected bot.runMode.");
logger.write("[ERROR] initialize -> createConext -> bot.runMode = " + bot.runMode);
callBack(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
thisObject.executionHistory = [];
@@ -469,7 +522,7 @@
if (FULL_LOG === true) { logger.write("[INFO] saveThemAll -> writeExecutionContext -> Entering function."); }
let fileName = "Execution.Context." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex +".json";
let fileName = "Execution.Context." + bot.runMode + "." + runIndex +".json";
let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCHours(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCMinutes(), 2);
let filePath = bot.filePathRoot + "/Output/" + bot.process + '/' + dateForPath;
@@ -530,7 +583,7 @@
if (FULL_LOG === true) { logger.write("[INFO] saveThemAll -> writeExucutionHistory -> Entering function."); }
let fileName = "Execution.History." + thisObject.statusReport.runType + "." + thisObject.statusReport.runIndex + ".json";
let fileName = "Execution.History." + bot.runMode + "." + runIndex + ".json";
let filePath = bot.filePathRoot + "/Output/" + bot.process;
if (FULL_LOG === true) { logger.write("[INFO] saveThemAll -> writeExucutionHistory -> fileName = " + fileName); }
@@ -586,8 +639,8 @@
/* Here we will write the file containing the max sequence number. */
fileContent = thisObject.statusReport.runIndex;
fileName = "Execution.History." + thisObject.statusReport.runType + "." + "Sequence" + ".json";
fileContent = runIndex;
fileName = "Execution.History." + bot.runMode + "." + "Sequence" + ".json";
filePath = bot.filePathRoot + "/Output/" + bot.process;
cloudStorage.createTextFile(filePath, fileName, fileContent + '\n', onSequenceFileCreated);
-526
View File
@@ -1,526 +0,0 @@
exports.newDatasource = function newDatasource(BOT, DEBUG_MODULE, BLOB_STORAGE, UTILITIES) {
const FULL_LOG = true;
/*
This module allows trading bots to access information stored at the cloud storage.
*/
const MODULE_NAME = "Datasource";
let thisObject = {
initialize: initialize,
candlesFiles: new Map, // Complete sets of candles for different Time Periods. For Time Periods < 1hs sets are of current day only, otherwise whole market.
candlesMap: new Map, // The last 10 candles for each Time Period will be stored here.
stairsFiles: new Map, // Complete sets of patterns for different Time Periods. For Time Periods < 1hs sets are of current day only, otherwise whole market.
stairsMap: new Map // The patterns we are currently in will be stored here.
};
let bot = BOT;
const logger = DEBUG_MODULE.newDebugLog();
logger.fileName = MODULE_NAME;
logger.bot = bot;
let oliviaStorage = BLOB_STORAGE.newBlobStorage(bot);
let tomStorage = BLOB_STORAGE.newBlobStorage(bot);
/* Utilities needed. */
let utilities = UTILITIES.newUtilities(bot);
return thisObject;
function initialize(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write("[INFO] initialize -> Entering function."); }
initializeOliviaStorage();
function initializeOliviaStorage() {
if (FULL_LOG === true) { logger.write("[INFO] initializeOliviaStorage -> Entering function."); }
oliviaStorage.initialize({ bot: "AAOlivia", devTeam: "AAMasters" }, onInizialized);
function onInizialized(err) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
initializeTomStorage();
} else {
logger.write("[ERROR] initialize -> initializeOliviaStorage -> err = " + err.message);
callBackFunction(err);
}
}
}
function initializeTomStorage() {
if (FULL_LOG === true) { logger.write("[INFO] initializeTomStorage -> Entering function."); }
tomStorage.initialize({ bot: "AATom", devTeam: "AAMasters" }, onInizialized);
function onInizialized(err) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
getCandles(onDone);
} else {
logger.write("[ERROR] initialize -> initializeTomStorage -> err = " + err.message);
callBackFunction(err);
}
}
}
function onDone(err) {
try {
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write("[INFO] initialize -> onDone -> Execution finished well. :-)");
callBackFunction(global.DEFAULT_OK_RESPONSE);
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("[ERROR] initialize -> onDone -> Retry Later. Requesting Execution Retry.");
callBackFunction(err);
return;
}
case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
logger.write("[ERROR] initialize -> onDone -> Operation Failed. Aborting the process.");
callBackFunction(err);
return;
}
}
} catch (err) {
logger.write("[ERROR] initialize -> onDone -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function getCandles(callBack) {
try {
if (FULL_LOG === true) { logger.write("[INFO] getCandles -> Entering function."); }
/*
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 {
if (FULL_LOG === true) { logger.write("[INFO] getCandles -> getMarketFiles -> Entering function."); }
let filesOk = 0;
let filesNotOk = 0;
for (i = 0; i < global.marketFilesPeriods.length; i++) {
let periodTime = global.marketFilesPeriods[i][0];
let periodName = global.marketFilesPeriods[i][1];
getFile(oliviaStorage, "@AssetA_@AssetB.json", "@Exchange/Output/Candles/Multi-Period-Market/@Period", periodName, undefined, onFileReceived, callBack);
function onFileReceived(err, file) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
filesOk++;
thisObject.candlesFiles.set(periodName, file);
} else {
filesNotOk++;
}
if (filesOk + filesNotOk === global.marketFilesPeriods.length) {
if (filesOk === global.marketFilesPeriods.length) {
getDailyFiles();
} else {
logger.write("[ERROR] getCandles -> getMarketFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk);
logger.write("[ERROR] getCandles -> getMarketFiles -> onFileReceived -> Will abort the process and request a retry.");
callBack(global.DEFAULT_RETRY_RESPONSE);
return;
}
}
}
}
} catch (err) {
logger.write("[ERROR] getCandles -> getMarketFiles -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function getDailyFiles() {
try {
if (FULL_LOG === true) { logger.write("[INFO] getCandles -> getDailyFiles -> Entering function."); }
let filesOk = 0;
let filesNotOk = 0;
for (i = 0; i < global.dailyFilePeriods.length; i++) {
let periodTime = global.dailyFilePeriods[i][0];
let periodName = global.dailyFilePeriods[i][1];
getFile(oliviaStorage, "@AssetA_@AssetB.json", "@Exchange/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day", periodName, bot.processDatetime, onFileReceived, callBack);
function onFileReceived(err, file) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
filesOk++;
thisObject.candlesFiles.set(periodName, file);
} else {
filesNotOk++;
}
if (filesOk + filesNotOk === global.dailyFilePeriods.length) {
if (filesOk === global.dailyFilePeriods.length) {
getCandlesWeAreIn();
} else {
logger.write("[ERROR] getCandles -> getDailyFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk);
logger.write("[ERROR] getCandles -> getDailyFiles -> onFileReceived -> Will abort the process and request a retry.");
callBack(global.DEFAULT_RETRY_RESPONSE);
return;
}
}
}
}
} catch (err) {
logger.write("[ERROR] getCandles -> getDailyFiles -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function getCandlesWeAreIn() {
try {
if (FULL_LOG === true) { logger.write("[INFO] getCandles -> getCandlesWeAreIn -> Entering function."); }
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 >= bot.processDatetime.valueOf() - timePeriod * 10 && candle.begin <= bot.processDatetime.valueOf()) {
candlesArray.push(candle);
}
}
thisObject.candlesMap.set(pPeriodName, candlesArray);
counter++;
if (counter === thisObject.candlesFiles.size) {
getPatterns(callBack);
}
}
} catch (err) {
logger.write("[ERROR] getCandles -> getCandlesWeAreIn -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write("[ERROR] getCandles -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function getPatterns(callBack) {
try {
if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> Entering function."); }
/*
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 {
if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> getMarketFiles -> Entering function."); }
let filesOk = 0;
let filesNotOk = 0;
for (i = 0; i < global.marketFilesPeriods.length; i++) {
let periodTime = global.marketFilesPeriods[i][0];
let periodName = global.marketFilesPeriods[i][1];
getFile(tomStorage, "@AssetA_@AssetB.json", "@Exchange/Tom/dataSet.V1/Output/Candle-Stairs/Multi-Period-Market/@Period", periodName, undefined, onFileReceived, callBack);
function onFileReceived(err, file) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
filesOk++;
thisObject.stairsFiles.set(periodName, file);
} else {
filesNotOk++;
}
if (filesOk + filesNotOk === global.marketFilesPeriods.length) {
if (filesOk === global.marketFilesPeriods.length) {
getDailyFiles();
} else {
logger.write("[ERROR] getPatterns -> getMarketFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk);
logger.write("[ERROR] getPatterns -> getMarketFiles -> onFileReceived -> Will abort the process and request a retry.");
callBack(global.DEFAULT_RETRY_RESPONSE);
return;
}
}
}
}
} catch (err) {
logger.write("[ERROR] getPatterns -> getMarketFiles -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function getDailyFiles() {
try {
if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> getDailyFiles -> Entering function."); }
let filesOk = 0;
let filesNotOk = 0;
for (i = 0; i < global.dailyFilePeriods.length; i++) {
let periodTime = global.dailyFilePeriods[i][0];
let periodName = global.dailyFilePeriods[i][1];
getFile(tomStorage, "@AssetA_@AssetB.json", "@Exchange/Tom/dataSet.V1/Output/Candle-Stairs/Multi-Period-Daily/@Period/@Year/@Month/@Day", periodName, bot.processDatetime, onFileReceived, callBack);
function onFileReceived(err, file) {
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
filesOk++;
thisObject.stairsFiles.set(periodName, file);
} else {
filesNotOk++;
}
if (filesOk + filesNotOk === global.dailyFilePeriods.length) {
if (filesOk === global.dailyFilePeriods.length) {
getStairsWeAreIn();
} else {
logger.write("[ERROR] getPatterns -> getDailyFiles -> onFileReceived -> Some files are missing -> filesNotOk = " + filesNotOk);
logger.write("[ERROR] getPatterns -> getDailyFiles -> onFileReceived -> Will abort the process and request a retry.");
callBack(global.DEFAULT_RETRY_RESPONSE);
return;
}
}
}
}
} catch (err) {
logger.write("[ERROR] getPatterns -> getDailyFiles -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function getStairsWeAreIn() {
try {
if (FULL_LOG === true) { logger.write("[INFO] getPatterns -> getStairsWeAreIn -> Entering function."); }
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 (bot.processDatetime.valueOf() >= stairs.begin && bot.processDatetime.valueOf() <= stairs.end) {
thisObject.stairsMap.set(pPeriodName, stairs);
}
}
counter++;
if (counter === thisObject.stairsFiles.size) {
callBack(global.DEFAULT_OK_RESPONSE);
}
}
} catch (err) {
logger.write("[ERROR] getPatterns -> getStairsWeAreIn -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write("[ERROR] getPatterns -> err = " + err.message);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function getFile(pFileStorage, pFileName, pFilePath, pPeriodName, pDatetime, innerCallBack, outerCallBack) {
try {
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> Entering function."); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pFileName = " + pFileName); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pFilePath = " + pFilePath); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pPeriodName = " + pPeriodName); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> pDatetime = " + pDatetime); }
pFileName = pFileName.replace("@AssetA", MARKET.assetA);
pFileName = pFileName.replace("@AssetB", MARKET.assetB);
pFilePath = pFilePath.replace("@Exchange", global.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));
}
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> final pFilePath = " + pFilePath); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> final pFileName = " + pFileName); }
pFileStorage.getTextFile(pFilePath, pFileName, onFileReceived);
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> onFileReceived -> pFilePath = " + pFilePath); }
if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> onFileReceived -> pFileName = " + pFileName); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write("[ERROR] initialize -> getFile -> onFileReceived -> err = " + err.message);
innerCallBack(err);
return;
}
try {
let data = JSON.parse(text);
innerCallBack(global.DEFAULT_OK_RESPONSE, data);
} catch (err) {
logger.write("[ERROR] initialize -> getFile -> onFileReceived -> Parsing JSON -> err = " + err.message);
innerCallBack(err);
return;
}
}
} catch (err) {
logger.write("[ERROR] initialize -> getFile -> err = " + err.message);
outerCallBack("Operation Failed");
}
}
} catch (err) {
logger.write("[ERROR] initialize -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
-3
View File
@@ -42,9 +42,6 @@
<Compile Include="Context.js">
<SubType>Code</SubType>
</Compile>
<Compile Include="Datasource.js">
<SubType>Code</SubType>
</Compile>
<Compile Include="Debug Log.js" />
<Compile Include="ExchangeAPI.js">
<SubType>Code</SubType>
+67 -12
View File
@@ -298,9 +298,8 @@ for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) {
runIndicatorBot(botConfig, processConfig, month, year);
break;
}
case 'Trading': {
runTradingBot(botConfig, processConfig);
break;
default: {
logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
}
}
}
@@ -321,23 +320,23 @@ for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) {
runExtractionBot(botConfig, processConfig, month, year);
break;
}
default: {
logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
}
}
}
}
if (processConfig.startMode.timePeriod !== undefined) {
if (processConfig.startMode.live !== undefined) {
if (processConfig.startMode.timePeriod.run === "true") {
if (processConfig.startMode.live.run === "true") {
botConfig.backTestingMode = true;
botConfig.timePeriod = processConfig.startMode.timePeriod;
botConfig.runMode = "Live";
/* We override these waitTimes to the one specified at the timePeriod configuration. */
let month = pad((new Date()).getUTCMonth() + 1, 2);
let year = (new Date()).getUTCFullYear();
processConfig.normalWaitTime = processConfig.startMode.timePeriod.waitTime;
processConfig.retryWaitTime = processConfig.startMode.timePeriod.waitTime;
if (processConfig.startMode.timePeriod.resumeExecution === "true") {
if (processConfig.startMode.live.resumeExecution === "true") {
botConfig.hasTheBotJustStarted = false;
} else {
botConfig.hasTheBotJustStarted = true;
@@ -348,6 +347,62 @@ for (let p = 0; p < global.PLATFORM_CONFIG.executionList.length; p++) {
runTradingBot(botConfig, processConfig);
break;
}
default: {
logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
}
}
}
}
if (processConfig.startMode.backtest !== undefined) {
if (processConfig.startMode.backtest.run === "true") {
botConfig.runMode = "Backtest";
botConfig.backtest = processConfig.startMode.backtest;
/* We override these waitTimes to the one specified at the backtest configuration. */
processConfig.normalWaitTime = processConfig.startMode.backtest.waitTime;
processConfig.retryWaitTime = processConfig.startMode.backtest.waitTime;
/* Backtest Mode does not support Resume Execution, so this is the only way. */
botConfig.hasTheBotJustStarted = true;
switch (botConfig.type) {
case 'Trading': {
runTradingBot(botConfig, processConfig);
break;
}
default: {
logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
}
}
}
}
if (processConfig.startMode.competition !== undefined) {
if (processConfig.startMode.competition.run === "true") {
botConfig.runMode = "Competition";
botConfig.competition = processConfig.startMode.competition;
if (processConfig.startMode.competition.resumeExecution === "true") {
botConfig.hasTheBotJustStarted = false;
} else {
botConfig.hasTheBotJustStarted = true;
}
switch (botConfig.type) {
case 'Trading': {
runTradingBot(botConfig, processConfig);
break;
}
default: {
logger.write("[ERROR] run -> Unexpected bot type. -> botConfig.type = " + botConfig.type);
}
}
}
}
+66 -32
View File
@@ -68,7 +68,6 @@
const POLONIEX_CLIENT_MODULE = require(ROOT_DIR + 'Poloniex API Client');
const EXCHANGE_API = require(ROOT_DIR + 'ExchangeAPI');
const CONTEXT = require(ROOT_DIR + 'Context');
const DATASOURCE = require(ROOT_DIR + 'Datasource');
const ASSISTANT = require(ROOT_DIR + 'Assistant');
const STATUS_REPORT = require(ROOT_DIR + 'Status Report');
const DATA_SET = require(ROOT_DIR + 'Data Set');
@@ -77,48 +76,83 @@
/* 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. */
if (bot.backTestingMode === true) {
switch (bot.runMode) {
case 'Live': {
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Backtesting Mode detected."); }
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Live Mode detected."); }
if (bot.hasTheBotJustStarted === true) {
// This will be considered the process date and time, so as to have it consistenly all over the execution.
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Bot just starting, setting initial datetime."); }
let localDate = new Date();
bot.processDatetime = new Date(bot.timePeriod.beginDatetime); // Set the starting time as the configured beginDatetime.
} else {
bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time.
let endDatetime = new Date(bot.timePeriod.endDatetime);
if (bot.processDatetime.valueOf() > endDatetime.valueOf()) {
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> End of Backtesting Period reached. Exiting Bot Process Loop."); }
callBackFunction(global.DEFAULT_OK_RESPONSE);
return;
}
bot.processDatetime = new Date(Date.UTC(
localDate.getUTCFullYear(),
localDate.getUTCMonth(),
localDate.getUTCDate(),
localDate.getUTCHours(),
localDate.getUTCMinutes(),
localDate.getUTCSeconds(),
localDate.getUTCMilliseconds()));
break;
}
case 'Backtest': {
} else { // We are running Live!
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Backtesting Mode detected."); }
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Live Mode detected."); }
if (bot.hasTheBotJustStarted === true) {
// This will be considered the process date and time, so as to have it consistenly all over the execution.
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Bot just starting, setting initial datetime."); }
let localDate = new Date();
bot.processDatetime = new Date(bot.backtest.beginDatetime); // Set the starting time as the configured beginDatetime.
bot.processDatetime = new Date(Date.UTC(
localDate.getUTCFullYear(),
localDate.getUTCMonth(),
localDate.getUTCDate(),
localDate.getUTCHours(),
localDate.getUTCMinutes(),
localDate.getUTCSeconds(),
localDate.getUTCMilliseconds()));
} else {
bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time.
let endDatetime = new Date(bot.backtest.endDatetime);
if (bot.processDatetime.valueOf() > endDatetime.valueOf()) {
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> End of Backtesting Period reached. Exiting Bot Process Loop."); }
callBackFunction(global.DEFAULT_OK_RESPONSE);
return;
}
}
break;
}
case 'Competition': {
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Competition Mode detected."); }
if (bot.hasTheBotJustStarted === true) {
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> Bot just starting, setting initial datetime."); }
bot.processDatetime = new Date(bot.competition.beginDatetime); // Set the starting time as the configured beginDatetime.
} else {
bot.processDatetime = new Date(bot.processDatetime.valueOf() + 60000); // We advance one minute in time.
let endDatetime = new Date(bot.competition.endDatetime);
if (bot.processDatetime.valueOf() > endDatetime.valueOf()) {
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> End of Competition Period reached. Exiting Bot Process Loop."); }
callBackFunction(global.DEFAULT_OK_RESPONSE);
return;
}
}
break;
}
default: {
logger.write("[ERROR] run -> loop -> Unexpected bot.runMode.");
logger.write("[ERROR] run -> loop -> bot.runMode = " + bot.runMode);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
if (FULL_LOG === true) { logger.write("[INFO] run -> loop -> bot.processDatetime = " + bot.processDatetime); }