Merge pull request #67 from AAMasters/master
Updating Master after a long, long time
This commit is contained in:
33 files changed
+4190
-11274
No files matched your search
+2
-5
@@ -9,9 +9,6 @@ bin/*
|
||||
*.cache
|
||||
node_modules/*
|
||||
exchange/Auth0.js
|
||||
configs/Email.Config.json
|
||||
|
||||
configs/User.Profile.json
|
||||
configs/Execution.Config.json
|
||||
configs/Secret.Keys.json
|
||||
.env
|
||||
|
||||
\.vscode/
|
||||
+48
-42
@@ -1,13 +1,13 @@
|
||||
exports.newAssistant = function newAssistant(BOT, logger, UTILITIES) {
|
||||
|
||||
/*
|
||||
/*
|
||||
|
||||
This module allows trading bots to execute actions on the exchange, and also on its current recorded state.
|
||||
|
||||
*/
|
||||
|
||||
const MODULE_NAME = "Assistant";
|
||||
|
||||
|
||||
var nodemailer = require('nodemailer');
|
||||
|
||||
let bot = BOT;
|
||||
@@ -23,25 +23,26 @@
|
||||
getInvestment: getInvestment,
|
||||
getProfits: getProfits,
|
||||
getCombinedProfits: getCombinedProfits,
|
||||
getROI: getROI,
|
||||
getROI: getROI,
|
||||
getMarketRate: getMarketRate,
|
||||
getTicker: getTicker,
|
||||
sendMessage: sendMessage,
|
||||
rememberThis: rememberThis,
|
||||
remindMeOf: remindMeOf,
|
||||
truncDecimals: truncDecimals,
|
||||
sendEmail: sendEmail
|
||||
truncDecimals: truncDecimals,
|
||||
sendEmail: sendEmail,
|
||||
addExtraData: addExtraData
|
||||
};
|
||||
|
||||
let utilities = UTILITIES.newCloudUtilities(bot, logger);
|
||||
|
||||
let exchangePositions = []; // These are the open positions at the exchange at the account the bot is authorized to use.
|
||||
let openPositions = []; // These are the open positions the bot knows it made by itself.
|
||||
let openPositions = []; // These are the open positions the bot knows it made by itself.
|
||||
|
||||
let context;
|
||||
let exchangeAPI;
|
||||
|
||||
let marketRate;
|
||||
let marketRate;
|
||||
let ticker;
|
||||
|
||||
return thisObject;
|
||||
@@ -99,7 +100,7 @@
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> onTicker -> Entering function."); }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> onTicker -> We could not get the Market Price now."); }
|
||||
|
||||
@@ -118,7 +119,7 @@
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromExchange -> onTicker -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromExchange -> err = " + err.message);
|
||||
@@ -404,7 +405,7 @@
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ordersExecutionCheck(callBack) {
|
||||
|
||||
try {
|
||||
@@ -533,9 +534,9 @@
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> pPositionId = " + pPositionId); }
|
||||
|
||||
/*
|
||||
|
||||
|
||||
Given one position, we request all the associated trades to it.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
switch (bot.startMode) {
|
||||
@@ -561,9 +562,9 @@
|
||||
let thisPosition = context.executionContext.positions[i];
|
||||
|
||||
if (thisPosition.id === pPositionId) {
|
||||
|
||||
|
||||
let feeRate = 0.002; // Default backtesting fee simulation
|
||||
|
||||
|
||||
let trade = {
|
||||
id: Math.trunc(Math.random(1) * 1000000),
|
||||
type: thisPosition.type,
|
||||
@@ -573,7 +574,7 @@
|
||||
fee: thisObject.truncDecimals(feeRate).toString(),
|
||||
date: (new Date()).valueOf()
|
||||
}
|
||||
|
||||
|
||||
trades.push(trade);
|
||||
|
||||
onResponse(global.DEFAULT_OK_RESPONSE, trades);
|
||||
@@ -645,11 +646,11 @@
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> pTrades = " + JSON.stringify(pTrades)); }
|
||||
|
||||
/*
|
||||
|
||||
|
||||
To confirm everything is ok, we will add all the amounts on trades asociated to the order and
|
||||
they must be equal to the one on file. Otherwise something very strange could have happened,
|
||||
in which case we will halt the bot execution.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
let sumAssetA = 0;
|
||||
@@ -660,7 +661,7 @@
|
||||
sumAssetA = sumAssetA + thisObject.truncDecimals(trade.amountA);
|
||||
sumAssetB = sumAssetB + thisObject.truncDecimals(trade.amountB);
|
||||
}
|
||||
|
||||
|
||||
sumAssetA = thisObject.truncDecimals(sumAssetA);
|
||||
sumAssetB = thisObject.truncDecimals(sumAssetB);
|
||||
|
||||
@@ -676,9 +677,9 @@
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
Confirmed that order was executed. Next thing to do is to remember the trades and change its status.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
position.status = "executed";
|
||||
@@ -715,27 +716,27 @@
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Entering function."); }
|
||||
|
||||
/*
|
||||
|
||||
|
||||
To confirm everything is ok, we will add all the amounts on trades plus the remaining amounts
|
||||
at the order at the exchange and they must be equal to the one on file. Otherwise an inconsistent
|
||||
state has been detected, in which case we will halt the bot execution.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
let sumAssetA = 0;
|
||||
let sumAssetB = 0;
|
||||
|
||||
|
||||
for (let k = 0; k < pTrades.length; k++) {
|
||||
let trade = pTrades[k];
|
||||
sumAssetA = sumAssetA + thisObject.truncDecimals(trade.amountA);
|
||||
sumAssetB = sumAssetB + thisObject.truncDecimals(trade.amountB);
|
||||
}
|
||||
|
||||
|
||||
/* To this we add the current position amounts. */
|
||||
|
||||
|
||||
sumAssetA = sumAssetA + thisObject.truncDecimals(exchangePosition.amountA);
|
||||
sumAssetB = sumAssetB + thisObject.truncDecimals(exchangePosition.amountB);
|
||||
|
||||
|
||||
sumAssetA = thisObject.truncDecimals(sumAssetA);
|
||||
sumAssetB = thisObject.truncDecimals(sumAssetB);
|
||||
|
||||
@@ -759,16 +760,16 @@
|
||||
logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumAssetB = " + sumAssetB);
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Cannot confirm that a partial execution was done well.");
|
||||
|
||||
|
||||
/* There are diferences on the responses between the getPosition and getTrades that causes some issues, let's retry. */
|
||||
callBack(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
Confirmed that order was partially executed. Next thing to do is to remember the trades and the new position.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
position.amountA = exchangePosition.amountA;
|
||||
@@ -817,8 +818,8 @@
|
||||
let assetB = 0;
|
||||
|
||||
if (trade.type === 'buy') {
|
||||
|
||||
/*
|
||||
|
||||
/*
|
||||
|
||||
The fee received at each trade is the factor by which if we multiply asset B, we get the amount of Asset B extracted as fees by the exchange.
|
||||
This happens with each individual trade. Remeber that one order can potentially be executed with 1 to many trades.
|
||||
@@ -841,7 +842,7 @@
|
||||
|
||||
if (trade.type === 'sell') {
|
||||
|
||||
/*
|
||||
/*
|
||||
|
||||
The fee received at each trade is the factor by which if we multiply asset A, we get the amount of Asset A extracted as fees by the exchange.
|
||||
This happens with each individual trade. Remeber that one order can potentially be executed with 1 to many trades.
|
||||
@@ -860,7 +861,7 @@
|
||||
|
||||
/* Not the available balance for asset B is not affected since it was already reduced when the order was placed. */
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
@@ -1040,7 +1041,7 @@
|
||||
|
||||
context.newHistoryRecord.newPositions++;
|
||||
|
||||
recalculateRateAverages();
|
||||
recalculateRateAverages();
|
||||
|
||||
/* Recalculate Available Balances */
|
||||
|
||||
@@ -1048,7 +1049,7 @@
|
||||
|
||||
context.executionContext.availableBalance.assetA = thisObject.truncDecimals(context.executionContext.availableBalance.assetA - pAmountA);
|
||||
context.newHistoryRecord.lastBuyRate = pRate;
|
||||
}
|
||||
}
|
||||
|
||||
if (position.type === 'sell') {
|
||||
|
||||
@@ -1056,7 +1057,7 @@
|
||||
context.newHistoryRecord.lastSellRate = pRate;
|
||||
}
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, position);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -1179,9 +1180,9 @@
|
||||
|
||||
context.newHistoryRecord.movedPositions++;
|
||||
|
||||
recalculateRateAverages();
|
||||
recalculateRateAverages();
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, newPosition);
|
||||
}
|
||||
break;
|
||||
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
|
||||
@@ -1316,7 +1317,7 @@
|
||||
|
||||
return JSON.parse(JSON.stringify(ROI));
|
||||
}
|
||||
|
||||
|
||||
function getMarketRate() {
|
||||
return marketRate;
|
||||
}
|
||||
@@ -1357,10 +1358,15 @@
|
||||
return context.executionContext.remember[pKey];
|
||||
|
||||
}
|
||||
|
||||
|
||||
function sendEmail(pTitle, pBody, pTo) {
|
||||
logger.write(MODULE_NAME, "[WARN] sendEmail -> Send emails is currently disabled.");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
function addExtraData(pExtraDataArray) {
|
||||
|
||||
context.extraData = pExtraDataArray;
|
||||
}
|
||||
|
||||
function truncDecimals(pFloatValue, pDecimals) {
|
||||
@@ -1370,4 +1376,4 @@
|
||||
return parseFloat(parseFloat(pFloatValue).toFixed(pDecimals));
|
||||
|
||||
}
|
||||
};
|
||||
};
|
||||
File renamed without changes.
+22
-1
@@ -38,7 +38,28 @@
|
||||
let USER_BOT_MODULE = {};
|
||||
USER_BOT_MODULE.newUserBot = eval(text);
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, USER_BOT_MODULE);
|
||||
/*
|
||||
This following code is to load the bot code from a file on the file system.
|
||||
It is useful for when you are activelly changing the code and dont wont to upload it to the storage at every run
|
||||
*/
|
||||
|
||||
/*
|
||||
let fs = require('fs');
|
||||
try {
|
||||
let fileName = "C:/Users/Luis/source/repos/Bots/AAMasters/AAChris-Indicator-Bot/Multi-Period-Daily/User.Bot.js";
|
||||
fs.readFile(fileName, onFileRead);
|
||||
|
||||
function onFileRead(err, file) {
|
||||
let fileString = file.toString();
|
||||
USER_BOT_MODULE.newUserBot = eval(fileString);
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, USER_BOT_MODULE);
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
console.log("ERROR LOADING FILE FROM FILE SYSTEM");
|
||||
}
|
||||
*/
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, USER_BOT_MODULE); // Coment this when loading from file system.
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] downloadBot -> onFileReceived -> err.message = " + err.message);
|
||||
|
||||
+5
-2
@@ -45,7 +45,8 @@
|
||||
messageBody: ""
|
||||
},
|
||||
initialize: initialize,
|
||||
saveThemAll: saveThemAll
|
||||
saveThemAll: saveThemAll,
|
||||
extraData: []
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -492,7 +493,9 @@
|
||||
thisObject.newHistoryRecord.messageBody
|
||||
];
|
||||
|
||||
thisObject.executionHistory.push(newRecord);
|
||||
let recordWithExtraData = newRecord.concat(thisObject.extraData);
|
||||
|
||||
thisObject.executionHistory.push(recordWithExtraData);
|
||||
|
||||
let fileContent = JSON.stringify(thisObject.executionHistory);
|
||||
|
||||
|
||||
+14
-4
@@ -9,6 +9,7 @@
|
||||
let ownerBot; // This is the bot owner of the Data Set.
|
||||
|
||||
let thisObject = {
|
||||
config: undefined,
|
||||
dataSets: new Map(),
|
||||
initialize: initialize,
|
||||
keys: []
|
||||
@@ -23,6 +24,15 @@
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
thisObject.config = pDataDependenciesConfig;
|
||||
|
||||
if (thisObject.config === undefined) {
|
||||
|
||||
// We allow old indicators not to declare their data dependencies.
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
|
||||
For each dependency declared at the bot config, we will initialize a DataSet as part of this initialization process.
|
||||
@@ -31,11 +41,11 @@
|
||||
let alreadyCalledBack = false;
|
||||
let addCount = 0;
|
||||
|
||||
for (let i = 0; i < pDataDependenciesConfig.length; i++) {
|
||||
for (let i = 0; i < thisObject.config.length; i++) {
|
||||
|
||||
let dataSetModule = DATA_SET.newDataSet(BOT, logger, BLOB_STORAGE, UTILITIES);
|
||||
|
||||
dataSetModule.initialize(pDataDependenciesConfig[i], onInitilized);
|
||||
dataSetModule.initialize(thisObject.config[i], onInitilized);
|
||||
|
||||
function onInitilized(err) {
|
||||
|
||||
@@ -58,14 +68,14 @@
|
||||
|
||||
let key;
|
||||
|
||||
key = pDataDependenciesConfig[i].devTeam + "-" + pDataDependenciesConfig[i].bot + "-" + pDataDependenciesConfig[i].product + "-" + pDataDependenciesConfig[i].dataSet + "-" + pDataDependenciesConfig[i].dataSetVersion;
|
||||
key = thisObject.config[i].devTeam + "-" + thisObject.config[i].bot + "-" + thisObject.config[i].product + "-" + thisObject.config[i].dataSet + "-" + thisObject.config[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 === pDataDependenciesConfig.length) {
|
||||
if (addCount === thisObject.config.length) {
|
||||
if (alreadyCalledBack === false) {
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
let bot = BOT;
|
||||
|
||||
let thisObject = {
|
||||
file: undefined, // Here we have the JSON object representing the file content.
|
||||
initialize: initialize,
|
||||
getTextFile: getTextFile,
|
||||
createTextFile: createTextFile
|
||||
|
||||
+4
-19
@@ -42,7 +42,7 @@
|
||||
function newInternalLoop(pBot, pProcess) {
|
||||
|
||||
|
||||
console.log(new Date().toISOString() + " " + strPad(pBot, 20) + " " + strPad(pProcess, 30) + " Entered into Internal Loop # " + strPad(internalLoopCounter + 1, 4));
|
||||
console.log(new Date().toISOString() + " " + strPad(pBot, 20) + " " + strPad(pProcess, 30) + " Entered into Internal Loop # " + strPad(internalLoopCounter + 1, 4) + " bot.processDatetime = " + thisObject.bot.processDatetime.toISOString());
|
||||
|
||||
persist();
|
||||
|
||||
@@ -60,13 +60,6 @@
|
||||
|
||||
internalLoopCounter++;
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT === "Browser") {
|
||||
|
||||
//console.log("[INFO] DebugLog -> persist -> We do not persist logs while running at the Browser");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
let contentToPersist = accumulatedLog;
|
||||
accumulatedLog = "[";
|
||||
|
||||
@@ -149,28 +142,20 @@
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT === "IDE") {
|
||||
|
||||
console.log("AACloud" + spacePad(pModule, 50) + " : " + pMessage);
|
||||
console.log("CloneExecutor" + spacePad(pModule, 50) + " : " + pMessage);
|
||||
|
||||
}
|
||||
|
||||
if (disableLogging === true) { return; }
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT === "Browser") {
|
||||
|
||||
/* Only at the browser we send the messages to the console. At the Cloud it does not make sense. */
|
||||
|
||||
console.log("AACloud" + spacePad(pModule, 50) + " : " + pMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT === "Cloud") {
|
||||
if (global.CURRENT_EXECUTION_AT === "Node") {
|
||||
|
||||
/* Only ERRORs go to the cloud console. */
|
||||
|
||||
if (pMessage.indexOf("[ERROR]") >= 0) {
|
||||
|
||||
let now = new Date;
|
||||
console.log("Loop." + pad(thisObject.bot.loopCounter, 8) + " " + now.toUTCString() + " AACloud " + spacePad(pModule, 20) + " : " + pMessage);
|
||||
console.log("Loop." + pad(thisObject.bot.loopCounter, 8) + " " + now.toUTCString() + " CloneExecutor " + spacePad(pModule, 20) + " : " + pMessage);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
exports.newExchangeAPI = function newExchangeAPI(bot, logger) {
|
||||
|
||||
/*
|
||||
|
||||
This module allows trading bots to connect to the exchange and do trading operations on it. So far it can only work with Poloniex.
|
||||
|
||||
*/
|
||||
const _ = require('lodash')
|
||||
const isValidOrder = require('./exchangeUtils').isValidOrder
|
||||
const graphqlClient = require('graphql-client')
|
||||
const appRoot = require('app-root-path')
|
||||
const auth = require(`${appRoot}/utils/auth`)
|
||||
|
||||
let MODULE_NAME = "Exchange API";
|
||||
|
||||
let thisObject = {
|
||||
initialize: initialize,
|
||||
getTicker: getTicker,
|
||||
getOpenPositions: getOpenPositions,
|
||||
getExecutedTrades: getExecutedTrades,
|
||||
putPosition: putPosition,
|
||||
movePosition: movePosition,
|
||||
getPublicTradeHistory: getPublicTradeHistory,
|
||||
getExchangeProperties: getExchangeProperties,
|
||||
getMaxDecimalPositions: getMaxDecimalPositions
|
||||
};
|
||||
|
||||
let apiClient;
|
||||
|
||||
return thisObject;
|
||||
|
||||
async function initialize(callBackFunction) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
// TODO integrate with financial beings module to get the exchange
|
||||
let botExchange = 'Poloniex';
|
||||
let exchange = botExchange.toLowerCase() + 'Client.js';
|
||||
let api = require('./wrappers/' + exchange);
|
||||
|
||||
const accessToken = await auth.authenticate()
|
||||
|
||||
let keyVaultAPI = createKeyVaultAPIClient(accessToken)
|
||||
apiClient = api.newAPIClient(keyVaultAPI, logger);
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function createKeyVaultAPIClient(authToken) {
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createKeyVaultAPIClient -> Entering function."); }
|
||||
|
||||
const keyVaultAPI = graphqlClient({
|
||||
url: global.GATEWAY_ENDPOINT,
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + authToken
|
||||
}
|
||||
});
|
||||
|
||||
keyVaultAPI.signTransaction = function (transaction, next) {
|
||||
let variables = {
|
||||
botId: bot.codeName,
|
||||
transaction: transaction
|
||||
}
|
||||
|
||||
keyVaultAPI.query(`
|
||||
mutation($botId: String, $transaction: String!){
|
||||
keyVault_SignTransaction(botId: $botId, transaction: $transaction){
|
||||
key,
|
||||
signature,
|
||||
date
|
||||
}
|
||||
}`, variables, function (req, res) {
|
||||
if (res.status === 401) {
|
||||
next(undefined, 'Error from graphql: Not authorized');
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.errors) {
|
||||
next(undefined, 'Error from graphql: ' + res.errors);
|
||||
} else {
|
||||
let signature = {
|
||||
Key: res.data.keyVault_SignTransaction.key,
|
||||
Sign: res.data.keyVault_SignTransaction.signature
|
||||
}
|
||||
next(signature)
|
||||
}
|
||||
}).catch(error => {
|
||||
next(undefined, 'Error signing the message on the key vault: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createKeyVaultAPIClient -> Returning graphql client."); }
|
||||
|
||||
return keyVaultAPI;
|
||||
}
|
||||
|
||||
/*
|
||||
* Position Object = {
|
||||
* id, String
|
||||
* type, String
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* fee, Number
|
||||
* datetime Date
|
||||
* };
|
||||
*/
|
||||
function truncDecimals(pFloatValue) {
|
||||
let decimals = getMaxDecimalPositions();
|
||||
return parseFloat(parseFloat(pFloatValue).toFixed(decimals));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return number of decimals for the current market
|
||||
*/
|
||||
function getMaxDecimalPositions() {
|
||||
return getMarketConfig().maxDecimals;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return number of decimals for the current market
|
||||
*/
|
||||
function getMarketConfig() {
|
||||
return _.find(getExchangeProperties().markets, (p) => {
|
||||
return _.first(p.pair) === global.MARKET.assetA.toUpperCase() &&
|
||||
_.last(p.pair) === global.MARKET.assetB.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Return number of decimals for the current market
|
||||
*/
|
||||
function getExchangeProperties() {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExchangeProperties -> Entering function."); }
|
||||
|
||||
return apiClient.getExchangeProperties();
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getExchangeProperties -> err = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the price for a given pair of assets.
|
||||
* The object returned is an array of trades:
|
||||
* ticker = {
|
||||
* bid, Number
|
||||
* ask, Number
|
||||
* last Number
|
||||
* };
|
||||
*/
|
||||
function getTicker(pMarket, callBack) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTicker -> Entering function."); }
|
||||
|
||||
apiClient.getTicker(pMarket, callBack);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getTicker -> err = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the open positions ath the exchange for a given market and user account.
|
||||
* The object returned is an array of positions
|
||||
*
|
||||
*/
|
||||
function getOpenPositions(pMarket, callBack) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getOpenPositions -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getOpenPositions -> pMarket = " + JSON.stringify(pMarket)); }
|
||||
|
||||
apiClient.getOpenPositions(pMarket, callBack);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getOpenPositions -> Error = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the trades for a given order number.
|
||||
* The object returned is an array of positions
|
||||
*/
|
||||
function getExecutedTrades(pPositionId, callBack) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getExecutedTrades -> pPositionId = " + pPositionId); }
|
||||
|
||||
apiClient.getExecutedTrades(pPositionId, callBack);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getExecutedTrades -> Error = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new buy or sell order.
|
||||
* The orderNumber is returned. String
|
||||
*/
|
||||
function putPosition(pMarket, pType, pRate, pAmountA, pAmountB, callBack) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pMarket = " + JSON.stringify(pMarket)); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pType = " + pType); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pRate = " + truncDecimals(pRate)); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + truncDecimals(pAmountA)); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + truncDecimals(pAmountB)); }
|
||||
|
||||
let check = isValidOrder({
|
||||
market: getMarketConfig(),
|
||||
api: apiClient,
|
||||
amount: truncDecimals(pAmountB),
|
||||
price: truncDecimals(pRate)
|
||||
});
|
||||
|
||||
if (check.valid) {
|
||||
if (pType === "buy") {
|
||||
apiClient.buy(pMarket.assetA, pMarket.assetB, truncDecimals(pRate), truncDecimals(pAmountB), callBack);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pType === "sell") {
|
||||
apiClient.sell(pMarket.assetA, pMarket.assetB, truncDecimals(pRate), truncDecimals(pAmountB), callBack);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] putPosition -> pType must be either 'buy' or 'sell'.");
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
|
||||
} else {
|
||||
logger.write(MODULE_NAME, "[ERROR] putPosition -> The order is invalid: " + check.reason);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] putPosition -> err = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Move an existing position to the new rate.
|
||||
* The new orderNumber is returned.
|
||||
*/
|
||||
function movePosition(pPosition, pNewRate, pNewAmountB, callBack) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> Entering function."); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> pPosition = " + JSON.stringify(pPosition)); }
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> pNewRate = " + truncDecimals(pNewRate)); }
|
||||
|
||||
apiClient.movePosition(pPosition, truncDecimals(pNewRate), truncDecimals(pNewAmountB), callBack);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] movePosition -> err = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId.
|
||||
* It's possible that the exchange doesn't support this method.
|
||||
* The object returned is an array of trades:
|
||||
* trade = {
|
||||
* tradeID, String
|
||||
* globalTradeID, String
|
||||
* type, String
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* date Date
|
||||
* };
|
||||
*/
|
||||
function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) {
|
||||
try {
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTradeHistory -> Entering function."); }
|
||||
|
||||
apiClient.getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] getTradeHistory -> err = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,100 +0,0 @@
|
||||
// generic low level reusuable utils for interacting with exchanges.
|
||||
|
||||
const retry = require('retry');
|
||||
const _ = require('lodash');
|
||||
|
||||
const retryInstance = (options, checkFn, callback) => {
|
||||
if(!options) {
|
||||
options = {
|
||||
retries: 100,
|
||||
factor: 1.2,
|
||||
minTimeout: 1 * 1000,
|
||||
maxTimeout: 4 * 1000
|
||||
};
|
||||
}
|
||||
|
||||
let attempt = 0;
|
||||
|
||||
const operation = retry.operation(options);
|
||||
operation.attempt(function(currentAttempt) {
|
||||
checkFn((err, result) => {
|
||||
if (!err) {
|
||||
return callback(global.DEFAULT_OK_RESPONSE, result);
|
||||
} else {
|
||||
if (err.result === global.CUSTOM_OK_RESPONSE.result) {
|
||||
return callback(err, result);
|
||||
}
|
||||
}
|
||||
|
||||
let maxAttempts = err.retry;
|
||||
if(maxAttempts === true)
|
||||
maxAttempts = 10;
|
||||
|
||||
if(err.retry && attempt++ < maxAttempts) {
|
||||
return operation.retry(err);
|
||||
}
|
||||
|
||||
if(err.notFatal) {
|
||||
if(err.backoffDelay) {
|
||||
return setTimeout(() => operation.retry(err), err.backoffDelay);
|
||||
}
|
||||
|
||||
return operation.retry(err);
|
||||
}
|
||||
|
||||
callback(err, result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const isValidOrder = ({api, market, amount, price}) => {
|
||||
let reason = false;
|
||||
|
||||
// Check amount
|
||||
if(amount < market.minimalOrder.amount) {
|
||||
reason = 'Amount is too small';
|
||||
}
|
||||
|
||||
// Some exchanges have restrictions on prices
|
||||
if(
|
||||
_.isFunction(api.isValidPrice) &&
|
||||
!api.isValidPrice(price)
|
||||
) {
|
||||
reason = 'Price is not valid';
|
||||
}
|
||||
|
||||
if(
|
||||
_.isFunction(api.isValidLot) &&
|
||||
!api.isValidLot(price, amount)
|
||||
) {
|
||||
reason = 'Lot size is too small';
|
||||
}
|
||||
|
||||
return {
|
||||
reason,
|
||||
valid: !reason
|
||||
}
|
||||
}
|
||||
|
||||
const includes = function(str, list) {
|
||||
if (!_.isString(str))
|
||||
return false;
|
||||
|
||||
return _.some(list, item => str.includes(item));
|
||||
}
|
||||
|
||||
|
||||
const getMarketConfig = function(exchangeProperties) {
|
||||
return _.find(exchangeProperties.markets, (p) => {
|
||||
return _.first(p.pair) === global.MARKET.assetA.toUpperCase() &&
|
||||
_.last(p.pair) === global.MARKET.assetB.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
retry: retryInstance,
|
||||
includes: includes,
|
||||
isValidOrder,
|
||||
getMarketConfig
|
||||
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
const _ = require('underscore');
|
||||
|
||||
class Beautifier {
|
||||
|
||||
constructor() {
|
||||
this._beautifications = {
|
||||
aggTrades: {
|
||||
a: 'aggTradeId',
|
||||
p: 'price',
|
||||
q: 'quantity',
|
||||
f: 'firstTradeId',
|
||||
l: 'lastTradeId',
|
||||
T: 'timestamp',
|
||||
m: 'maker',
|
||||
M: 'bestPriceMatch'
|
||||
},
|
||||
klines: {
|
||||
0: 'openTime',
|
||||
1: 'open',
|
||||
2: 'high',
|
||||
3: 'low',
|
||||
4: 'close',
|
||||
5: 'volume',
|
||||
6: 'closeTime',
|
||||
7: 'quoteAssetVolume',
|
||||
8: 'trades',
|
||||
9: 'takerBaseAssetVolume',
|
||||
10: 'takerQuoteAssetVolume',
|
||||
11: 'ignored'
|
||||
},
|
||||
bids: [
|
||||
{
|
||||
0: 'price',
|
||||
1: 'quantity',
|
||||
2: 'ignored'
|
||||
}
|
||||
],
|
||||
asks: [
|
||||
{
|
||||
0: 'price',
|
||||
1: 'quantity',
|
||||
2: 'ignored'
|
||||
}
|
||||
],
|
||||
depthUpdateEvent: {
|
||||
e: 'eventType',
|
||||
E: 'eventTime',
|
||||
s: 'symbol',
|
||||
u: 'updateId',
|
||||
b: 'bidDepthDelta',
|
||||
a: 'askDepthDelta'
|
||||
},
|
||||
bidDepthDelta: [
|
||||
{
|
||||
0: 'price',
|
||||
1: 'quantity',
|
||||
2: 'ignored'
|
||||
}
|
||||
],
|
||||
askDepthDelta: [
|
||||
{
|
||||
0: 'price',
|
||||
1: 'quantity',
|
||||
2: 'ignored'
|
||||
}
|
||||
],
|
||||
klineEvent: {
|
||||
e: 'eventType',
|
||||
E: 'eventTime',
|
||||
s: 'symbol',
|
||||
k: 'kline'
|
||||
},
|
||||
kline: {
|
||||
t: 'startTime',
|
||||
T: 'endTime',
|
||||
s: 'symbol',
|
||||
i: 'interval',
|
||||
f: 'firstTradeId',
|
||||
L: 'lastTradeId',
|
||||
o: 'open',
|
||||
c: 'close',
|
||||
h: 'high',
|
||||
l: 'low',
|
||||
v: 'volume',
|
||||
n: 'trades',
|
||||
x: 'final',
|
||||
q: 'quoteVolume',
|
||||
V: 'volumeActive',
|
||||
Q: 'quoteVolumeActive',
|
||||
B: 'ignored'
|
||||
},
|
||||
aggTradeEvent: {
|
||||
e: 'eventType',
|
||||
E: 'eventTime',
|
||||
s: 'symbol',
|
||||
a: 'tradeId',
|
||||
p: 'price',
|
||||
q: 'quantity',
|
||||
f: 'firstTradeId',
|
||||
l: 'lastTradeId',
|
||||
T: 'time',
|
||||
m: 'maker',
|
||||
M: 'ignored'
|
||||
},
|
||||
outboundAccountInfoEvent: {
|
||||
e: 'eventType',
|
||||
E: 'eventTime',
|
||||
m: 'makerCommission',
|
||||
t: 'takerCommission',
|
||||
b: 'buyerCommission',
|
||||
s: 'sellerCommission',
|
||||
T: 'canTrade',
|
||||
W: 'canWithdraw',
|
||||
D: 'canDeposit',
|
||||
B: 'balances'
|
||||
},
|
||||
balances: [
|
||||
{
|
||||
a: 'asset',
|
||||
f: 'availableBalance',
|
||||
l: 'onOrderBalance'
|
||||
}
|
||||
],
|
||||
executionReportEvent: {
|
||||
e: 'eventType',
|
||||
E: 'eventTime',
|
||||
s: 'symbol',
|
||||
c: 'newClientOrderId',
|
||||
S: 'side',
|
||||
o: 'orderType',
|
||||
f: 'cancelType', // GTC 'Good till Cancel', IOC: 'Immediate or Cancel'
|
||||
q: 'quantity',
|
||||
p: 'price',
|
||||
x: 'executionType',
|
||||
X: 'orderStatus',
|
||||
r: 'rejectReason',
|
||||
i: 'orderId',
|
||||
l: 'lastTradeQuantity',
|
||||
z: 'accumulatedQuantity',
|
||||
L: 'lastTradePrice',
|
||||
n: 'commission',
|
||||
N: 'commissionAsset',
|
||||
m: 'maker',
|
||||
T: 'tradeTime',
|
||||
t: 'tradeId'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beautify(object, type) {
|
||||
const beautifications = _.isObject(type) ? type : this._beautifications[type];
|
||||
if (!beautifications) {
|
||||
return object;
|
||||
}
|
||||
let newItem = {};
|
||||
_.each(object, (value, key) => {
|
||||
if (_.isArray(value)) {
|
||||
const newArray = [];
|
||||
if (_.isArray(this._beautifications[beautifications[key]])) {
|
||||
_.each(value, arrayValue => {
|
||||
newArray.push(this.beautify(arrayValue, this._beautifications[beautifications[key]][0]));
|
||||
});
|
||||
} else {
|
||||
_.each(value, (value, key) => {
|
||||
newItem.push(this.beautify(value, beautifications[key]))
|
||||
});
|
||||
}
|
||||
const newKey = beautifications[key] || key;
|
||||
newItem[newKey] = newArray;
|
||||
} else if (_.isObject(value)) {
|
||||
const newKey = beautifications[key] || key;
|
||||
newItem[newKey] = this.beautify(value, beautifications[key]);
|
||||
} else {
|
||||
const newKey = beautifications[key] || key;
|
||||
newItem[newKey] = value;
|
||||
}
|
||||
});
|
||||
return newItem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Beautifier;
|
||||
@@ -1,368 +0,0 @@
|
||||
// Source: https://github.com/zoeyg/binance/blob/master/lib/rest.js
|
||||
|
||||
const request = require('request');
|
||||
const qs = require('querystring');
|
||||
const _ = require('underscore');
|
||||
const crypto = require('crypto');
|
||||
const Beautifier = require('./beautifier.js');
|
||||
const assert = require('assert');
|
||||
|
||||
class BinanceRest {
|
||||
|
||||
constructor({
|
||||
key,
|
||||
secret,
|
||||
recvWindow = false,
|
||||
timeout = 15000,
|
||||
disableBeautification = false,
|
||||
handleDrift = false
|
||||
}) {
|
||||
this.key = key;
|
||||
this.secret = secret;
|
||||
this.recvWindow = recvWindow;
|
||||
this.timeout = timeout;
|
||||
this.disableBeautification = disableBeautification;
|
||||
this.handleDrift = handleDrift;
|
||||
|
||||
this._beautifier = new Beautifier();
|
||||
this._baseUrl = 'https://api.binance.com/';
|
||||
this._drift = 0;
|
||||
this._syncInterval = 0;
|
||||
}
|
||||
|
||||
_makeRequest(query, callback, route, security, method, attempt = 0) {
|
||||
assert(_.isUndefined(callback) || _.isFunction(callback), 'callback must be a function or undefined');
|
||||
assert(_.isObject(query), 'query must be an object');
|
||||
|
||||
let queryString;
|
||||
const type = _.last(route.split('/')),
|
||||
options = {
|
||||
url: `${this._baseUrl}${route}`,
|
||||
timeout: this.timeout
|
||||
};
|
||||
|
||||
if (security === 'SIGNED') {
|
||||
if (this.recvWindow) {
|
||||
query.recvWindow = this.recvWindow;
|
||||
}
|
||||
queryString = qs.stringify(query);
|
||||
options.url += '?' + queryString;
|
||||
if (options.url.substr(options.url.length - 1) !== '?') {
|
||||
options.url += '&';
|
||||
}
|
||||
options.url += `signature=${this._sign(queryString)}`;
|
||||
} else {
|
||||
queryString = qs.stringify(query);
|
||||
if (queryString) {
|
||||
options.url += '?' + queryString;
|
||||
}
|
||||
}
|
||||
if (security === 'API-KEY' || security === 'SIGNED') {
|
||||
options.headers = { 'X-MBX-APIKEY': this.key };
|
||||
}
|
||||
if (method) {
|
||||
options.method = method;
|
||||
}
|
||||
|
||||
const action = cb => {
|
||||
request(options, (err, response, body) => {
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(body);
|
||||
} catch (e) {
|
||||
payload = body;
|
||||
}
|
||||
if (err) {
|
||||
cb(err, payload);
|
||||
} else if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
/*
|
||||
* If we get a response that the timestamp is ahead of the server,
|
||||
* calculate the drift and then attempt the request again
|
||||
*/
|
||||
if (response.statusCode === 400 && payload.code === -1021 &&
|
||||
this.handleDrift && attempt === 0) {
|
||||
|
||||
this.calculateDrift()
|
||||
.then(() => {
|
||||
query.timestamp = this._getTime() + this._drift;
|
||||
return this._makeRequest(query, cb, route, security, method,
|
||||
++attempt);
|
||||
});
|
||||
} else {
|
||||
cb(new Error(`Response code ${response.statusCode}`), payload);
|
||||
}
|
||||
} else {
|
||||
if (_.isArray(payload)) {
|
||||
payload = _.map(payload, (item) => {
|
||||
return this._doBeautifications(item, type);
|
||||
});
|
||||
} else {
|
||||
payload = this._doBeautifications(payload);
|
||||
}
|
||||
cb(err, payload);
|
||||
}
|
||||
});
|
||||
};
|
||||
if (callback) {
|
||||
action(callback);
|
||||
} else {
|
||||
return new Promise((resolve, reject) => action((err, payload) => {
|
||||
if (err) {
|
||||
if (payload === undefined) {
|
||||
reject(err);
|
||||
} else {
|
||||
reject(payload);
|
||||
}
|
||||
} else {
|
||||
resolve(payload);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
_doBeautifications(response, route) {
|
||||
if (this.disableBeautification) {
|
||||
return response;
|
||||
}
|
||||
return this._beautifier.beautify(response, route);
|
||||
}
|
||||
|
||||
_sign(queryString) {
|
||||
return crypto.createHmac('sha256', this.secret)
|
||||
.update(queryString)
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
_setTimestamp(query) {
|
||||
if (!query.timestamp) {
|
||||
query.timestamp = this._getTime() + this._drift;
|
||||
}
|
||||
}
|
||||
|
||||
_getTime() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
calculateDrift() {
|
||||
const systemTime = this._getTime();
|
||||
return this.time()
|
||||
.then((response) => {
|
||||
// Calculate the approximate trip time from here to binance
|
||||
const transitTime = parseInt((this._getTime() - systemTime) / 2);
|
||||
this._drift = response.serverTime - (systemTime + transitTime);
|
||||
});
|
||||
}
|
||||
|
||||
startTimeSync(interval = 300000, onRecalculateDriftCb) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// If there's already an interval running, clear it and reset values
|
||||
if (this._syncInterval !== 0) {
|
||||
this.endTimeSync();
|
||||
return resolve();
|
||||
}
|
||||
|
||||
// Calculate initial drift value and setup interval to periodically update it
|
||||
this.calculateDrift()
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
|
||||
this._syncInterval = setInterval(() => {
|
||||
const promise = this.calculateDrift();
|
||||
|
||||
if (_.isFunction(onRecalculateDriftCb)) {
|
||||
onRecalculateDriftCb(promise);
|
||||
}
|
||||
}, interval);
|
||||
});
|
||||
}
|
||||
|
||||
endTimeSync() {
|
||||
clearInterval(this._syncInterval);
|
||||
this._drift = 0;
|
||||
this._syncInterval = 0;
|
||||
}
|
||||
|
||||
// Public APIs
|
||||
ping(callback) {
|
||||
return this._makeRequest({}, callback, 'api/v1/ping');
|
||||
}
|
||||
|
||||
time(callback) {
|
||||
return this._makeRequest({}, callback, 'api/v1/time');
|
||||
}
|
||||
|
||||
depth(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
|
||||
return this._makeRequest(query, callback, 'api/v1/depth');
|
||||
}
|
||||
|
||||
trades(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
|
||||
return this._makeRequest(query, callback, 'api/v1/trades');
|
||||
}
|
||||
|
||||
historicalTrades(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
|
||||
return this._makeRequest(query, callback, 'api/v1/historicalTrades', 'API-KEY');
|
||||
}
|
||||
|
||||
aggTrades(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
|
||||
return this._makeRequest(query, callback, 'api/v1/aggTrades');
|
||||
}
|
||||
|
||||
exchangeInfo(callback) {
|
||||
return this._makeRequest({}, callback, 'api/v1/exchangeInfo');
|
||||
}
|
||||
|
||||
klines(query = {}, callback) {
|
||||
return this._makeRequest(query, callback, 'api/v1/klines');
|
||||
}
|
||||
|
||||
ticker24hr(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
|
||||
return this._makeRequest(query, callback, 'api/v1/ticker/24hr');
|
||||
}
|
||||
|
||||
tickerPrice(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
|
||||
return this._makeRequest(query, callback, 'api/v3/ticker/price');
|
||||
}
|
||||
|
||||
bookTicker(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
|
||||
return this._makeRequest(query, callback, 'api/v3/ticker/bookTicker');
|
||||
}
|
||||
|
||||
allBookTickers(callback) {
|
||||
return this._makeRequest({}, callback, 'api/v1/ticker/allBookTickers');
|
||||
}
|
||||
|
||||
allPrices(callback) {
|
||||
return this._makeRequest({}, callback, 'api/v1/ticker/allPrices');
|
||||
}
|
||||
|
||||
// Private APIs
|
||||
newOrder(query = {}, callback) {
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED', 'POST');
|
||||
}
|
||||
|
||||
testOrder(query = {}, callback) {
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/order/test', 'SIGNED', 'POST');
|
||||
}
|
||||
|
||||
queryOrder(query = {}, callback) {
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED');
|
||||
}
|
||||
|
||||
cancelOrder(query = {}, callback) {
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED', 'DELETE');
|
||||
}
|
||||
|
||||
openOrders(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/openOrders', 'SIGNED');
|
||||
}
|
||||
|
||||
allOrders(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/allOrders', 'SIGNED');
|
||||
}
|
||||
|
||||
account(query = {}, callback) {
|
||||
if (_.isFunction(query)) {
|
||||
callback = query;
|
||||
query = {};
|
||||
}
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/account', 'SIGNED');
|
||||
}
|
||||
|
||||
myTrades(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { symbol: query };
|
||||
}
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'api/v3/myTrades', 'SIGNED');
|
||||
}
|
||||
|
||||
withdraw(query = {}, callback) {
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'wapi/v3/withdraw.html', 'SIGNED', 'POST');
|
||||
}
|
||||
|
||||
depositHistory(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { asset: query };
|
||||
}
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'wapi/v3/depositHistory.html', 'SIGNED');
|
||||
}
|
||||
|
||||
withdrawHistory(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { asset: query };
|
||||
}
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'wapi/v3/withdrawHistory.html', 'SIGNED');
|
||||
}
|
||||
|
||||
depositAddress(query = {}, callback) {
|
||||
if (_.isString(query)) {
|
||||
query = { asset: query };
|
||||
}
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'wapi/v3/depositAddress.html', 'SIGNED');
|
||||
}
|
||||
|
||||
accountStatus(callback) {
|
||||
const query = {};
|
||||
this._setTimestamp(query);
|
||||
return this._makeRequest(query, callback, 'wapi/v3/accountStatus.html', 'SIGNED');
|
||||
}
|
||||
|
||||
startUserDataStream(callback) {
|
||||
return this._makeRequest({}, callback, 'api/v1/userDataStream', 'API-KEY', 'POST');
|
||||
}
|
||||
|
||||
keepAliveUserDataStream(query = {}, callback) {
|
||||
return this._makeRequest(query, callback, 'api/v1/userDataStream', 'API-KEY', 'PUT');
|
||||
}
|
||||
|
||||
closeUserDataStream(query = {}, callback) {
|
||||
return this._makeRequest(query, callback, 'api/v1/userDataStream', 'API-KEY', 'DELETE');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BinanceRest;
|
||||
@@ -1,360 +0,0 @@
|
||||
exports.newAPIClient = function newAPIClient(pKey, pSecret, logger) {
|
||||
|
||||
/*
|
||||
/* ATENTION: This Library is used both a the cloud (AACloud) and also at the AAWeb on the server side without any modifications.
|
||||
/* There is a second version, stripped of all the inner functionality that runs at AAWeb browser side, which channels the requests to the server side one.
|
||||
*/
|
||||
|
||||
const FULL_LOG = true;
|
||||
const LOG_FILE_CONTENT = true;
|
||||
const MODULE_NAME = "binanceClient";
|
||||
|
||||
const retry = require('../exchangeUtils').retry;
|
||||
const includes = require('../exchangeUtils').includes;
|
||||
const _ = require('lodash');
|
||||
const Binance = require("./binance");
|
||||
const exchangeProperties = require('./poloniexProperties.json');
|
||||
|
||||
const recoverableErrors = [
|
||||
'SOCKETTIMEDOUT',
|
||||
'TIMEDOUT',
|
||||
'CONNRESET',
|
||||
'CONNREFUSED',
|
||||
'NOTFOUND',
|
||||
'Error -1021',
|
||||
'Response code 429',
|
||||
'Response code 5',
|
||||
'ETIMEDOUT'
|
||||
];
|
||||
|
||||
let API = new Binance({
|
||||
key: pKey,
|
||||
secret: pSecret,
|
||||
timeout: 15000,
|
||||
recvWindow: 60000,
|
||||
disableBeautification: false,
|
||||
handleDrift: true,
|
||||
});
|
||||
|
||||
let thisObject = {
|
||||
getTicker: getTicker,
|
||||
getOpenPositions: getOpenPositions,
|
||||
getExecutedTrades: getExecutedTrades,
|
||||
buy: buy,
|
||||
sell: sell,
|
||||
movePosition: movePosition,
|
||||
getPublicTradeHistory: getPublicTradeHistory,
|
||||
getExchangeProperties: getExchangeProperties,
|
||||
isValidPrice: isValidPrice,
|
||||
isValidLot: isValidLot
|
||||
};
|
||||
|
||||
return thisObject;
|
||||
|
||||
function joinCurrencies(currencyA, currencyB) {
|
||||
// If only one arg, then return the first
|
||||
if (typeof currencyB !== 'string') {
|
||||
return currencyA;
|
||||
}
|
||||
|
||||
return currencyB + '' + currencyA;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the properties for this exchange
|
||||
*/
|
||||
function getExchangeProperties() {
|
||||
return exchangeProperties;
|
||||
}
|
||||
|
||||
function truncDecimals(pFloatValue) {
|
||||
|
||||
let decimals = 6;
|
||||
|
||||
return parseFloat(parseFloat(pFloatValue).toFixed(decimals));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the price for a given pair of assets.
|
||||
* The object returned is an array of trades:
|
||||
* ticker = {
|
||||
* bid, Number
|
||||
* ask, Number
|
||||
* last Number
|
||||
* };
|
||||
*/
|
||||
function getTicker(pMarket, callBack) {
|
||||
|
||||
let market = joinCurrencies(pMarket.assetA, pMarket.assetB);
|
||||
const handle = (err, response) => {
|
||||
let ticker;
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
ticker = {
|
||||
bid: Number(response.bidPrice),
|
||||
ask: Number(response.askPrice),
|
||||
last: Number(response.lastPrice)
|
||||
};
|
||||
}
|
||||
callBack(err, ticker);
|
||||
};
|
||||
|
||||
const fetch = next => API.ticker24hr(market, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the open positions ath the exchange for a given market and user account.
|
||||
* The object returned is an array of positions:
|
||||
* position = {
|
||||
* id,
|
||||
* type,
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* fee, (not required) Number
|
||||
* datetime
|
||||
* };
|
||||
*/
|
||||
function getOpenPositions(pMarket, callBack) {
|
||||
|
||||
let market = joinCurrencies(pMarket.assetA, pMarket.assetB);
|
||||
const handle = (err, response) => {
|
||||
let exchangePositions = [];
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
let openPosition = {
|
||||
id: response[i].orderId,
|
||||
type: response[i].side.toLowerCase(),
|
||||
rate: Number(response[i].price),
|
||||
amountA: truncDecimals(response[i].origQty * response[i].price),
|
||||
amountB: Number(response[i].origQty),
|
||||
date: (new Date(response[i].time)).valueOf()
|
||||
};
|
||||
exchangePositions.push(openPosition);
|
||||
}
|
||||
}
|
||||
callBack(err, exchangePositions);
|
||||
};
|
||||
|
||||
const fetch = next => API.openOrders(market, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the trades for a given order number.
|
||||
* The object returned is an array of positions:
|
||||
* position = {
|
||||
* id,
|
||||
* type,
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* fee, Number
|
||||
* datetime
|
||||
* };
|
||||
*/
|
||||
function getExecutedTrades(pPositionId, callBack) {
|
||||
|
||||
let market = joinCurrencies(global.MARKET.assetA, global.MARKET.assetB);
|
||||
const handle = (err, response) => {
|
||||
let exchangeTrades = [];
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
if (response[i].orderId === pPositionId) {
|
||||
let type = "sell"
|
||||
if (response[i].isBuyer === true) {
|
||||
type = "buy";
|
||||
}
|
||||
let trade = {
|
||||
id: response[i].orderId,
|
||||
type: type,
|
||||
rate: Number(response[i].price),
|
||||
amountA: truncDecimals(response[i].qty * response[i].price),
|
||||
amountB: Number(response[i].qty),
|
||||
fee: Number(response[i].commission),
|
||||
date: (new Date(response[i].time)).valueOf()
|
||||
};
|
||||
exchangeTrades.push(trade);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
callBack(err, exchangeTrades);
|
||||
};
|
||||
|
||||
const fetch = next => API.myTrades(market, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new buy order.
|
||||
* The orderNumber is returned.
|
||||
*/
|
||||
function buy(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
|
||||
|
||||
var parameters = {
|
||||
symbol: joinCurrencies(pCurrencyA, pCurrencyB),
|
||||
side: 'BUY',
|
||||
type: 'LIMIT',
|
||||
quantity: pAmount,
|
||||
price: pRate,
|
||||
timeInForce: 'GTC'
|
||||
};
|
||||
const handle = (err, response) => {
|
||||
let orderNumber;
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
orderNumber = response.orderId;
|
||||
}
|
||||
callBack(err, orderNumber);
|
||||
};
|
||||
|
||||
const fetch = next => API.newOrder(parameters, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new sell order.
|
||||
* The orderNumber is returned. String
|
||||
*/
|
||||
function sell(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
|
||||
|
||||
var parameters = {
|
||||
symbol: joinCurrencies(pCurrencyA, pCurrencyB),
|
||||
side: 'SELL',
|
||||
type: 'LIMIT',
|
||||
quantity: pAmount,
|
||||
price: pRate,
|
||||
timeInForce: 'GTC'
|
||||
};
|
||||
const handle = (err, response) => {
|
||||
let orderNumber;
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
orderNumber = response.orderId;
|
||||
}
|
||||
callBack(err, orderNumber);
|
||||
};
|
||||
|
||||
const fetch = next => API.newOrder(parameters, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/* //TODO Complete partial orders
|
||||
* Move an existing position to the new rate.
|
||||
* The new orderNumber is returned. String
|
||||
*/
|
||||
function movePosition(pPosition, pNewRate, pNewAmountB, callBack) {
|
||||
|
||||
var parameters = {
|
||||
symbol: joinCurrencies(global.MARKET.assetA, global.MARKET.assetB),
|
||||
orderId: pPosition.id
|
||||
};
|
||||
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] moveOrder -> Step1 - Cancelling Position: " + JSON.stringify(pPosition)); }
|
||||
|
||||
const handle = (err, response) => {
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] moveOrder -> Step1 Complete - Position Cancelled. Proceeding to put the new position."); }
|
||||
|
||||
if (position.type === "buy")
|
||||
thisObject.buy(global.MARKET.assetA, global.MARKET.assetB, pNewRate, pNewAmountB, callBack);
|
||||
else
|
||||
thisObject.sell(global.MARKET.assetA, global.MARKET.assetB, pNewRate, pNewAmountB, callBack);
|
||||
} else {
|
||||
callBack(err);
|
||||
}
|
||||
};
|
||||
|
||||
const fetch = next => API.cancelOrder(parameters, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/* //TODO Pending Review
|
||||
* Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId.
|
||||
* It's possible that the exchange doesn't support this method.
|
||||
* The object returned is an array of trades:
|
||||
* trade = {
|
||||
* tradeIdAtExchange,
|
||||
* marketIdAtExchange,
|
||||
* type,
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* datetime
|
||||
* };
|
||||
*/
|
||||
function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] getPublicTradeHistory -> Not implemented yet."); }
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
|
||||
function isValidPrice(price) {
|
||||
return price >= market.minimalOrder.price;
|
||||
}
|
||||
|
||||
function isValidLot(price, amount) {
|
||||
return amount * price >= market.minimalOrder.order;
|
||||
}
|
||||
|
||||
function analizeResponse(callBack) {
|
||||
|
||||
return (exchangeErr, exchangeResponse) => {
|
||||
let error;
|
||||
|
||||
/* This function analizes the different situations we might encounter trying to access Poloniex and returns appropiate standard errors. */
|
||||
|
||||
let stringExchangeErr = JSON.stringify(exchangeErr);
|
||||
let stringExchangeResponse = JSON.stringify(exchangeResponse);
|
||||
|
||||
try {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeErr = " + stringExchangeErr); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeResponse = " + stringExchangeResponse); }
|
||||
|
||||
if (exchangeErr) {
|
||||
error = global.DEFAULT_FAIL_RESPONSE;
|
||||
} else if (!exchangeResponse) {
|
||||
error = {
|
||||
result: global.DEFAULT_FAIL_RESPONSE.result,
|
||||
message: 'Empty response'
|
||||
};
|
||||
} else if (includes(exchangeResponse.error, ['<!DOCTYPE html>'])) {
|
||||
error = {
|
||||
result: global.DEFAULT_FAIL_RESPONSE.result,
|
||||
message: exchangeResponse
|
||||
};
|
||||
} else if (includes(exchangeResponse.error, ['Order does not exist.'])) {
|
||||
error = {
|
||||
result: global.CUSTOM_OK_RESPONSE.result,
|
||||
message: exchangeResponse.error
|
||||
};
|
||||
} else if (exchangeResponse.error) {
|
||||
error = {
|
||||
result: global.DEFAULT_FAIL_RESPONSE.result,
|
||||
message: exchangeResponse.error
|
||||
};
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (includes(error.message, recoverableErrors)) {
|
||||
error.notFatal = true;
|
||||
}
|
||||
}
|
||||
|
||||
return callBack(error, exchangeResponse);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> err.message = " + err.message);
|
||||
return callBack(global.DEFAULT_FAIL_RESPONSE, exchangeResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,440 +0,0 @@
|
||||
// Source: https://github.com/premasagar/poloniex.js
|
||||
// Documentation: https://poloniex.com/support/api/
|
||||
|
||||
module.exports = (function () {
|
||||
'use strict';
|
||||
|
||||
// Module dependencies
|
||||
var request = require('request'),
|
||||
nonce = require('nonce')();
|
||||
|
||||
// Constants
|
||||
var version = '0.0.9',
|
||||
PUBLIC_API_URL = 'https://poloniex.com/public',
|
||||
PRIVATE_API_URL = 'https://poloniex.com/tradingApi',
|
||||
USER_AGENT = 'poloniex.js ' + version;
|
||||
//USER_AGENT = 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0'
|
||||
|
||||
// Helper methods
|
||||
function joinCurrencies(currencyA, currencyB) {
|
||||
// If only one arg, then return the first
|
||||
if (typeof currencyB !== 'string') {
|
||||
return currencyA;
|
||||
}
|
||||
|
||||
return currencyA + '_' + currencyB;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
function Poloniex(keyVaultAPI) {
|
||||
// Generate headers signed by this user's key and secret.
|
||||
// The secret is encapsulated and never exposed
|
||||
this._getPrivateHeaders = function (parameters, next) {
|
||||
var paramString, signature;
|
||||
|
||||
// Convert to `arg1=foo&arg2=bar`
|
||||
paramString = Object.keys(parameters).map(function (param) {
|
||||
return encodeURIComponent(param) + '=' + encodeURIComponent(parameters[param]);
|
||||
}).join('&');
|
||||
|
||||
keyVaultAPI.signTransaction(paramString, next);
|
||||
};
|
||||
}
|
||||
|
||||
// Currently, this fails with `Error: CERT_UNTRUSTED`
|
||||
// Poloniex.STRICT_SSL can be set to `false` to avoid this. Use with caution.
|
||||
// Will be removed in future, once this is resolved.
|
||||
Poloniex.STRICT_SSL = true;
|
||||
|
||||
// Customisable user agent string
|
||||
Poloniex.USER_AGENT = USER_AGENT;
|
||||
|
||||
// Prototype
|
||||
Poloniex.prototype = {
|
||||
constructor: Poloniex,
|
||||
|
||||
// Make an API request
|
||||
_request: function (options, callback) {
|
||||
if (!('headers' in options)) {
|
||||
options.headers = {};
|
||||
}
|
||||
|
||||
options.json = true;
|
||||
options.headers['User-Agent'] = Poloniex.USER_AGENT;
|
||||
options.strictSSL = Poloniex.STRICT_SSL;
|
||||
options.timeout = 60 * 1000;
|
||||
|
||||
request(options, function (err, response, body) {
|
||||
// Empty response
|
||||
if (!err && (typeof body === 'undefined' || body === null)) {
|
||||
err = 'Empty response';
|
||||
}
|
||||
|
||||
callback(err, body);
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Make a public API request
|
||||
_public: function (command, parameters, callback) {
|
||||
var options;
|
||||
|
||||
if (typeof parameters === 'function') {
|
||||
callback = parameters;
|
||||
parameters = {};
|
||||
}
|
||||
|
||||
parameters || (parameters = {});
|
||||
parameters.command = command;
|
||||
options = {
|
||||
method: 'GET',
|
||||
url: PUBLIC_API_URL,
|
||||
qs: parameters
|
||||
};
|
||||
|
||||
options.qs.command = command;
|
||||
return this._request(options, callback);
|
||||
},
|
||||
|
||||
// Make a private API request
|
||||
_private: function (command, parameters, callback) {
|
||||
var options;
|
||||
|
||||
if (typeof parameters === 'function') {
|
||||
callback = parameters;
|
||||
parameters = {};
|
||||
}
|
||||
|
||||
parameters || (parameters = {});
|
||||
parameters.command = command;
|
||||
parameters.nonce = nonce();
|
||||
|
||||
options = {
|
||||
method: 'POST',
|
||||
url: PRIVATE_API_URL,
|
||||
form: parameters
|
||||
};
|
||||
|
||||
let headers = this._getPrivateHeaders(parameters, next)
|
||||
|
||||
let thisObject = this
|
||||
|
||||
function next(pHeaders, error) {
|
||||
if (!error) {
|
||||
options.headers = pHeaders;
|
||||
return thisObject._request(options, callback);
|
||||
} else {
|
||||
callback(error);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/////
|
||||
// PUBLIC METHODS
|
||||
|
||||
returnTicker: function (callback) {
|
||||
return this._public('returnTicker', callback);
|
||||
},
|
||||
|
||||
return24hVolume: function (callback) {
|
||||
return this._public('return24hVolume', callback);
|
||||
},
|
||||
|
||||
returnOrderBook: function (currencyA, currencyB, callback) {
|
||||
var currencyPair;
|
||||
|
||||
if (typeof currencyB === 'function') {
|
||||
currencyPair = currencyA;
|
||||
callback = currencyB;
|
||||
currencyB = null;
|
||||
}
|
||||
|
||||
else {
|
||||
currencyPair = joinCurrencies(currencyA, currencyB);
|
||||
}
|
||||
|
||||
var parameters = {
|
||||
currencyPair: currencyPair
|
||||
};
|
||||
|
||||
return this._public('returnOrderBook', parameters, callback);
|
||||
},
|
||||
|
||||
returnChartData: function (currencyA, currencyB, period, start, end, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
period: period,
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
|
||||
return this._public('returnChartData', parameters, callback);
|
||||
},
|
||||
|
||||
returnCurrencies: function (callback) {
|
||||
return this._public('returnCurrencies', callback);
|
||||
},
|
||||
|
||||
returnLoanOrders: function (currency, callback) {
|
||||
return this._public('returnLoanOrders', { currency: currency }, callback);
|
||||
},
|
||||
|
||||
returnTradeHistory: function (currencyA, currencyB, start, end, callback) {
|
||||
if (arguments.length < 5) {
|
||||
callback = start;
|
||||
start = Date.now() / 1000 - 60 * 60;
|
||||
end = Date.now() / 1000 + 60 * 60; // Some buffer in case of client/server time out of sync.
|
||||
}
|
||||
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
|
||||
return this._public('returnTradeHistory', parameters, callback);
|
||||
},
|
||||
|
||||
/////
|
||||
// PRIVATE METHODS
|
||||
|
||||
returnBalances: function (callback) {
|
||||
return this._private('returnBalances', {}, callback);
|
||||
},
|
||||
|
||||
returnCompleteBalances: function (account, callback) {
|
||||
var parameters = {};
|
||||
|
||||
if (typeof account === 'function') {
|
||||
callback = account;
|
||||
}
|
||||
|
||||
else if (typeof account === 'string' && !!account) {
|
||||
parameters.account = account;
|
||||
}
|
||||
|
||||
return this._private('returnCompleteBalances', parameters, callback);
|
||||
},
|
||||
|
||||
returnDepositAddresses: function (callback) {
|
||||
return this._private('returnDepositAddresses', {}, callback);
|
||||
},
|
||||
|
||||
generateNewAddress: function (currency, callback) {
|
||||
return this._private('generateNewAddress', { currency: currency }, callback);
|
||||
},
|
||||
|
||||
returnDepositsWithdrawals: function (start, end, callback) {
|
||||
return this._private('returnDepositsWithdrawals', { start: start, end: end }, callback);
|
||||
},
|
||||
|
||||
// can be called with `returnOpenOrders('all', callback)`
|
||||
returnOpenOrders: function (currencyA, currencyB, callback) {
|
||||
var currencyPair;
|
||||
|
||||
if (typeof currencyB === 'function') {
|
||||
currencyPair = currencyA;
|
||||
callback = currencyB;
|
||||
currencyB = null;
|
||||
}
|
||||
|
||||
else {
|
||||
currencyPair = joinCurrencies(currencyA, currencyB);
|
||||
}
|
||||
|
||||
var parameters = {
|
||||
currencyPair: currencyPair
|
||||
};
|
||||
|
||||
return this._private('returnOpenOrders', parameters, callback);
|
||||
},
|
||||
|
||||
returnOrderTrades: function (orderNumber, callback) {
|
||||
var parameters = {
|
||||
orderNumber: orderNumber
|
||||
};
|
||||
|
||||
return this._private('returnOrderTrades', parameters, callback);
|
||||
},
|
||||
|
||||
buy: function (currencyA, currencyB, rate, amount, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
rate: rate,
|
||||
amount: amount
|
||||
};
|
||||
|
||||
return this._private('buy', parameters, callback);
|
||||
},
|
||||
|
||||
sell: function (currencyA, currencyB, rate, amount, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
rate: rate,
|
||||
amount: amount
|
||||
};
|
||||
|
||||
return this._private('sell', parameters, callback);
|
||||
},
|
||||
|
||||
cancelOrder: function (currencyA, currencyB, orderNumber, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
orderNumber: orderNumber
|
||||
};
|
||||
|
||||
return this._private('cancelOrder', parameters, callback);
|
||||
},
|
||||
|
||||
moveOrder: function (orderNumber, rate, amount, callback) {
|
||||
var parameters = {
|
||||
orderNumber: orderNumber,
|
||||
rate: rate,
|
||||
amount: amount ? amount : null
|
||||
};
|
||||
|
||||
return this._private('moveOrder', parameters, callback);
|
||||
},
|
||||
|
||||
withdraw: function (currency, amount, address, callback) {
|
||||
var parameters = {
|
||||
currency: currency,
|
||||
amount: amount,
|
||||
address: address
|
||||
};
|
||||
|
||||
return this._private('withdraw', parameters, callback);
|
||||
},
|
||||
|
||||
returnFeeInfo: function (callback) {
|
||||
return this._private('returnFeeInfo', {}, callback);
|
||||
},
|
||||
|
||||
returnAvailableAccountBalances: function (account, callback) {
|
||||
var parameters = {};
|
||||
|
||||
if (typeof account === 'function') {
|
||||
callback = account;
|
||||
}
|
||||
|
||||
else if (typeof account === 'string' && !!account) {
|
||||
parameters.account = account;
|
||||
}
|
||||
|
||||
return this._private('returnAvailableAccountBalances', parameters, callback);
|
||||
},
|
||||
|
||||
returnTradableBalances: function (callback) {
|
||||
return this._private('returnTradableBalances', {}, callback);
|
||||
},
|
||||
|
||||
transferBalance: function (currency, amount, fromAccount, toAccount, callback) {
|
||||
var parameters = {
|
||||
currency: currency,
|
||||
amount: amount,
|
||||
fromAccount: fromAccount,
|
||||
toAccount: toAccount
|
||||
};
|
||||
|
||||
return this._private('transferBalance', parameters, callback);
|
||||
},
|
||||
|
||||
returnMarginAccountSummary: function (callback) {
|
||||
return this._private('returnMarginAccountSummary', {}, callback);
|
||||
},
|
||||
|
||||
marginBuy: function (currencyA, currencyB, rate, amount, lendingRate, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
rate: rate,
|
||||
amount: amount,
|
||||
lendingRate: lendingRate ? lendingRate : null
|
||||
};
|
||||
|
||||
return this._private('marginBuy', parameters, callback);
|
||||
},
|
||||
|
||||
marginSell: function (currencyA, currencyB, rate, amount, lendingRate, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB),
|
||||
rate: rate,
|
||||
amount: amount,
|
||||
lendingRate: lendingRate ? lendingRate : null
|
||||
};
|
||||
|
||||
return this._private('marginSell', parameters, callback);
|
||||
},
|
||||
|
||||
getMarginPosition: function (currencyA, currencyB, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||
};
|
||||
|
||||
return this._private('getMarginPosition', parameters, callback);
|
||||
},
|
||||
|
||||
closeMarginPosition: function (currencyA, currencyB, callback) {
|
||||
var parameters = {
|
||||
currencyPair: joinCurrencies(currencyA, currencyB)
|
||||
};
|
||||
|
||||
return this._private('closeMarginPosition', parameters, callback);
|
||||
},
|
||||
|
||||
createLoanOffer: function (currency, amount, duration, autoRenew, lendingRate, callback) {
|
||||
var parameters = {
|
||||
currency: currency,
|
||||
amount: amount,
|
||||
duration: duration,
|
||||
autoRenew: autoRenew,
|
||||
lendingRate: lendingRate
|
||||
};
|
||||
|
||||
return this._private('createLoanOffer', parameters, callback);
|
||||
},
|
||||
|
||||
cancelLoanOffer: function (orderNumber, callback) {
|
||||
var parameters = {
|
||||
orderNumber: orderNumber
|
||||
};
|
||||
|
||||
return this._private('cancelLoanOffer', parameters, callback);
|
||||
},
|
||||
|
||||
returnOpenLoanOffers: function (callback) {
|
||||
return this._private('returnOpenLoanOffers', {}, callback);
|
||||
},
|
||||
|
||||
returnActiveLoans: function (callback) {
|
||||
return this._private('returnActiveLoans', {}, callback);
|
||||
},
|
||||
|
||||
returnLendingHistory: function (start, end, limit, callback) {
|
||||
var parameters = {
|
||||
start: start,
|
||||
end: end,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
return this._private('returnLendingHistory', parameters, callback);
|
||||
},
|
||||
|
||||
toggleAutoRenew: function (orderNumber, callback) {
|
||||
return this._private('toggleAutoRenew', { orderNumber: orderNumber }, callback);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Backwards Compatibility
|
||||
Poloniex.prototype.getTicker = Poloniex.prototype.returnTicker;
|
||||
Poloniex.prototype.get24hVolume = Poloniex.prototype.return24hVolume;
|
||||
Poloniex.prototype.getOrderBook = Poloniex.prototype.returnOrderBook;
|
||||
Poloniex.prototype.getTradeHistory = Poloniex.prototype.returnChartData;
|
||||
Poloniex.prototype.myBalances = Poloniex.prototype.returnBalances;
|
||||
Poloniex.prototype.myOpenOrders = Poloniex.prototype.returnOpenOrders;
|
||||
Poloniex.prototype.myTradeHistory = Poloniex.prototype.returnTradeHistory;
|
||||
|
||||
return Poloniex;
|
||||
})();
|
||||
@@ -1,353 +0,0 @@
|
||||
exports.newAPIClient = function newAPIClient(keyVaultAPI, logger) {
|
||||
|
||||
/*
|
||||
/* ATENTION: This Library is used both a the cloud (AACloud) and also at the AAWeb on the server side without any modifications.
|
||||
/* There is a second version, stripped of all the inner functionality that runs at AAWeb browser side, which channels the requests to the server side one.
|
||||
*/
|
||||
|
||||
const FULL_LOG = true;
|
||||
const LOG_FILE_CONTENT = true;
|
||||
const MODULE_NAME = "poloniexClient";
|
||||
|
||||
const retry = require('../exchangeUtils').retry;
|
||||
const includes = require('../exchangeUtils').includes;
|
||||
const _ = require('lodash');
|
||||
const Poloniex = require("./poloniex");
|
||||
const exchangeProperties = require('./poloniexProperties.json');
|
||||
|
||||
const recoverableErrors = [
|
||||
'SOCKETTIMEDOUT',
|
||||
'TIMEDOUT',
|
||||
'CONNRESET',
|
||||
'CONNREFUSED',
|
||||
'NOTFOUND',
|
||||
'429',
|
||||
'522',
|
||||
'504',
|
||||
'503',
|
||||
'500',
|
||||
'502',
|
||||
'Empty response',
|
||||
'Please try again in a few minutes.',
|
||||
'Nonce must be greater than',
|
||||
'Internal error. Please try again.',
|
||||
'Connection timed out. Please try again.',
|
||||
'ENOTFOUND',
|
||||
'ECONNREFUSED',
|
||||
'CONNREFUSED',
|
||||
'NOTFOUND',
|
||||
'ESOCKETTIMEDOUT',
|
||||
'ECONNRESET',
|
||||
'Connection timed out',
|
||||
'Connection Error',
|
||||
'Order not found, or you are not the person who placed it.',
|
||||
'ETIMEDOUT'
|
||||
];
|
||||
|
||||
let API = new Poloniex(keyVaultAPI);
|
||||
|
||||
let thisObject = {
|
||||
getTicker: getTicker,
|
||||
getOpenPositions: getOpenPositions,
|
||||
getExecutedTrades: getExecutedTrades,
|
||||
buy: buy,
|
||||
sell: sell,
|
||||
movePosition: movePosition,
|
||||
getPublicTradeHistory: getPublicTradeHistory,
|
||||
getExchangeProperties: getExchangeProperties,
|
||||
isValidLot: isValidLot
|
||||
};
|
||||
|
||||
return thisObject;
|
||||
|
||||
function joinCurrencies(currencyA, currencyB) {
|
||||
// If only one arg, then return the first
|
||||
if (typeof currencyB !== 'string') {
|
||||
return currencyA;
|
||||
}
|
||||
|
||||
return currencyA + '_' + currencyB;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the properties for this exchange
|
||||
*/
|
||||
function getExchangeProperties() {
|
||||
return exchangeProperties;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the price for a given pair of assets.
|
||||
* The object returned is an array of trades:
|
||||
* ticker = {
|
||||
* bid, Number
|
||||
* ask, Number
|
||||
* last Number
|
||||
* };
|
||||
*/
|
||||
function getTicker(pMarket, callBack) {
|
||||
|
||||
const handle = (err, response) => {
|
||||
let ticker;
|
||||
let market = joinCurrencies(pMarket.assetA, pMarket.assetB);
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
ticker = {
|
||||
bid: Number(response[market].highestBid),
|
||||
ask: Number(response[market].lowestAsk),
|
||||
last: Number(response[market].last)
|
||||
};
|
||||
}
|
||||
callBack(err, ticker);
|
||||
};
|
||||
|
||||
const fetch = next => API.getTicker(analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the open positions ath the exchange for a given market and user account.
|
||||
* The object returned is an array of positions:
|
||||
* position = {
|
||||
* id,
|
||||
* type,
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* fee, (not required) Number
|
||||
* datetime
|
||||
* };
|
||||
*/
|
||||
function getOpenPositions(pMarket, callBack) {
|
||||
|
||||
const handle = (err, response) => {
|
||||
let exchangePositions = [];
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
let openPosition = {
|
||||
id: response[i].orderNumber,
|
||||
type: response[i].type,
|
||||
rate: Number(response[i].rate),
|
||||
amountA: Number(response[i].total),
|
||||
amountB: Number(response[i].amount),
|
||||
date: (new Date(response[i].time)).valueOf()
|
||||
};
|
||||
exchangePositions.push(openPosition);
|
||||
}
|
||||
}
|
||||
callBack(err, exchangePositions);
|
||||
};
|
||||
|
||||
const fetch = next => API.returnOpenOrders(pMarket.assetA, pMarket.assetB, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the trades for a given order number.
|
||||
* The object returned is an array of positions:
|
||||
* position = {
|
||||
* id,
|
||||
* type,
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* fee, Number
|
||||
* datetime
|
||||
* };
|
||||
*/
|
||||
function getExecutedTrades(pPositionId, callBack) {
|
||||
|
||||
const handle = (err, response) => {
|
||||
let trades = [];
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
let trade = {
|
||||
id: response[i].tradeID,
|
||||
type: response[i].type,
|
||||
rate: Number(response[i].rate),
|
||||
amountA: Number(response[i].total),
|
||||
amountB: Number(response[i].amount),
|
||||
fee: Number(response[i].fee),
|
||||
date: (new Date(response[i].date)).valueOf()
|
||||
}
|
||||
trades.push(trade);
|
||||
}
|
||||
} else if (err.result === global.CUSTOM_OK_RESPONSE.result) {
|
||||
// No trades found at the exchange for the position, but returning empy trades object.
|
||||
err = global.DEFAULT_OK_RESPONSE;
|
||||
}
|
||||
callBack(err, trades);
|
||||
};
|
||||
|
||||
const fetch = next => API.returnOrderTrades(pPositionId, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new buy order.
|
||||
* The orderNumber is returned. String
|
||||
*/
|
||||
function buy(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
|
||||
|
||||
const handle = (err, response) => {
|
||||
let orderNumber;
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
orderNumber = response.orderNumber;
|
||||
}
|
||||
callBack(err, orderNumber);
|
||||
};
|
||||
|
||||
const fetch = next => API.buy(pCurrencyA, pCurrencyB, pRate, pAmount, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new sell order.
|
||||
* The orderNumber is returned. String
|
||||
*/
|
||||
function sell(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) {
|
||||
|
||||
const handle = (err, response) => {
|
||||
let orderNumber;
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
orderNumber = response.orderNumber;
|
||||
}
|
||||
callBack(err, orderNumber);
|
||||
};
|
||||
|
||||
const fetch = next => API.sell(pCurrencyA, pCurrencyB, pRate, pAmount, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move an existing position to the new rate.
|
||||
* The orderNumber is returned. String
|
||||
*/
|
||||
function movePosition(pPosition, pNewRate, pNewAmountB, callBack) {
|
||||
|
||||
const handle = (err, response) => {
|
||||
let orderNumber;
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
orderNumber = response.orderNumber;
|
||||
}
|
||||
callBack(err, orderNumber);
|
||||
};
|
||||
|
||||
const fetch = next => API.moveOrder(pPosition.id, pNewRate, pNewAmountB, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId.
|
||||
* It's possible that the exchange doesn't support this method.
|
||||
* The object returned is an array of trades:
|
||||
* trade = {
|
||||
* tradeID, String
|
||||
* globalTradeID, String
|
||||
* type, String
|
||||
* rate, Number
|
||||
* amountA, Number
|
||||
* amountB, Number
|
||||
* date Date
|
||||
* };
|
||||
*/
|
||||
function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) {
|
||||
|
||||
const handle = (err, response) => {
|
||||
let trades = [];
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
for (let i = 0; i < response.length; i++) {
|
||||
let trade = {
|
||||
tradeID: response[i].tradeID,
|
||||
globalTradeID: response[i].globalTradeID,
|
||||
type: response[i].type,
|
||||
rate: Number(response[i].rate),
|
||||
total: Number(response[i].total),
|
||||
amount: Number(response[i].amount),
|
||||
date: new Date(response[i].date)
|
||||
}
|
||||
trades.push(trade);
|
||||
}
|
||||
}
|
||||
callBack(err, trades);
|
||||
};
|
||||
|
||||
const fetch = next => API.returnTradeHistory(assetA, assetB, startTime, endTime, analizeResponse(next));
|
||||
|
||||
retry(null, fetch, handle);
|
||||
}
|
||||
|
||||
function isValidLot(price, amount) {
|
||||
return amount * price >= 0.0001;
|
||||
}
|
||||
|
||||
function analizeResponse(callBack) {
|
||||
|
||||
return (exchangeErr, exchangeResponse) => {
|
||||
let error;
|
||||
|
||||
/* This function analizes the different situations we might encounter trying to access Poloniex and returns appropiate standard errors. */
|
||||
|
||||
let stringExchangeErr = JSON.stringify(exchangeErr);
|
||||
let stringExchangeResponse = JSON.stringify(exchangeResponse);
|
||||
|
||||
try {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeErr = " + stringExchangeErr); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] analizeResponse -> exchangeResponse = " + stringExchangeResponse); }
|
||||
|
||||
if (exchangeErr) {
|
||||
error = global.DEFAULT_FAIL_RESPONSE;
|
||||
|
||||
if (includes(exchangeErr.code, recoverableErrors)) {
|
||||
error.notFatal = true;
|
||||
}
|
||||
} else if (!exchangeResponse || exchangeResponse === undefined) {
|
||||
error = {
|
||||
result: global.DEFAULT_FAIL_RESPONSE.result,
|
||||
message: 'Empty response',
|
||||
notFatal: true
|
||||
};
|
||||
} else if (includes(exchangeResponse.error, ['Please complete the security check to proceed.'])) {
|
||||
error = {
|
||||
result: global.DEFAULT_FAIL_RESPONSE.result,
|
||||
message: 'The IP has been flagged by CloudFlare.'
|
||||
};
|
||||
} else if (includes(exchangeResponse.error, ['<!DOCTYPE html>'])) {
|
||||
error = {
|
||||
result: global.DEFAULT_FAIL_RESPONSE.result,
|
||||
message: exchangeResponse
|
||||
};
|
||||
} else if (includes(exchangeResponse.error, ['Not enough'])) {
|
||||
error = {
|
||||
result: global.DEFAULT_RETRY_RESPONSE.result,
|
||||
message: exchangeResponse.error
|
||||
};
|
||||
} else if (includes(exchangeResponse.error, ['Order not found, or you are not the person who placed it.'])) {
|
||||
error = {
|
||||
result: global.CUSTOM_OK_RESPONSE.result,
|
||||
message: exchangeResponse.error
|
||||
};
|
||||
} else if (exchangeResponse.error) {
|
||||
error = {
|
||||
result: global.DEFAULT_FAIL_RESPONSE.result,
|
||||
message: exchangeResponse.error
|
||||
};
|
||||
}
|
||||
|
||||
return callBack(error, exchangeResponse);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] analizeResponse -> err.message = " + err.message);
|
||||
return callBack(global.DEFAULT_FAIL_RESPONSE, exchangeResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+349
-67
@@ -9,6 +9,8 @@
|
||||
let COMMONS_MODULE;
|
||||
|
||||
const BLOB_STORAGE = require(ROOT_DIR + 'BlobStorage');
|
||||
const MULTI_PERIOD_MARKET = require(ROOT_DIR + 'MultiPeriodMarket');
|
||||
const MULTI_PERIOD_DAILY = require(ROOT_DIR + 'MultiPeriodDaily');
|
||||
|
||||
const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog');
|
||||
let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble.
|
||||
@@ -48,25 +50,10 @@
|
||||
|
||||
let filePath;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) { // This is what determines if the bot is loaded from the devTeam or an endUser copy.
|
||||
case "Cloud": {
|
||||
if (global.RUN_AS_TEAM) {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name; // DevTeams bots only are run at the cloud.
|
||||
} else {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
if (global.RUN_AS_TEAM) {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name; // DevTeams bots only are run at the cloud.
|
||||
} else {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
}
|
||||
|
||||
const CLOUD_REQUIRE = require(ROOT_DIR + 'CloudRequire');
|
||||
@@ -85,22 +72,7 @@
|
||||
|
||||
USER_BOT_MODULE = pMODULE;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) {
|
||||
case "Cloud": {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo;
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo;
|
||||
|
||||
cloudRequire.downloadCommons(cloudStorage, filePath, onCommonsDownloaded);
|
||||
|
||||
@@ -175,6 +147,8 @@
|
||||
const UTILITIES = require(ROOT_DIR + 'CloudUtilities');
|
||||
const STATUS_REPORT = require(ROOT_DIR + 'StatusReport');
|
||||
const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies');
|
||||
const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies');
|
||||
const DATA_SET = require(ROOT_DIR + 'DataSet');
|
||||
|
||||
/* 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. */
|
||||
|
||||
@@ -182,13 +156,15 @@
|
||||
|
||||
/* High level log entry */
|
||||
|
||||
console.log(bot.processDatetime.toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + pad(pMonth, 2) + "/" + pad(pYear, 4)
|
||||
+ " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8));
|
||||
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. */
|
||||
|
||||
let userBot;
|
||||
let processFramework;
|
||||
let statusDependencies;
|
||||
let dataDependencies;
|
||||
|
||||
let nextWaitTime;
|
||||
|
||||
@@ -213,7 +189,7 @@
|
||||
switch (err.result) {
|
||||
case global.DEFAULT_OK_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well.");
|
||||
initializeUserBot();
|
||||
initializeDataDependencies();
|
||||
return;
|
||||
}
|
||||
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
|
||||
@@ -269,6 +245,109 @@
|
||||
}
|
||||
}
|
||||
|
||||
function initializeDataDependencies() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> Entering function."); }
|
||||
|
||||
dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET, BLOB_STORAGE, UTILITIES);
|
||||
|
||||
dataDependencies.initialize(processConfig.dataDependencies, onInizialized);
|
||||
|
||||
function onInizialized(err) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Entering function."); }
|
||||
|
||||
switch (err.result) {
|
||||
case global.DEFAULT_OK_RESPONSE.result: {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); }
|
||||
|
||||
/* If the process is configured to run inside a framework, we continue there, otherwise we run the bot directly. */
|
||||
if (processConfig.framework === undefined) {
|
||||
initializeUserBot();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (processConfig.framework.name) {
|
||||
case 'Multi-Period-Market': {
|
||||
processFramework = MULTI_PERIOD_MARKET.newMultiPeriodMarket(bot, logger, COMMONS_MODULE, UTILITIES, BLOB_STORAGE, USER_BOT_MODULE, COMMONS_MODULE);
|
||||
intitializeProcessFramework();
|
||||
break;
|
||||
}
|
||||
case 'Multi-Period-Daily': {
|
||||
processFramework = MULTI_PERIOD_DAILY.newMultiPeriodDaily(bot, logger, COMMONS_MODULE, UTILITIES, BLOB_STORAGE, USER_BOT_MODULE, COMMONS_MODULE);
|
||||
intitializeProcessFramework();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework not Supported.");
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework Name = " + processConfig.framework.name);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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, "[ERROR] run -> loop -> initializeDataDependencies -> 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.
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> 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 -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.message = " + 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 -> initializeDataDependencies -> onInizialized -> err = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> err = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
function initializeUserBot() {
|
||||
|
||||
try {
|
||||
@@ -500,6 +579,235 @@
|
||||
}
|
||||
}
|
||||
|
||||
function intitializeProcessFramework() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); }
|
||||
|
||||
processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized, dataDependencies);
|
||||
|
||||
function onInizialized(err) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Entering function."); }
|
||||
|
||||
switch (err.result) {
|
||||
case global.DEFAULT_OK_RESPONSE.result: {
|
||||
logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Execution finished well.");
|
||||
startProcessFramework();
|
||||
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, "[ERROR] run -> loop -> intitializeProcessFramework -> 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.
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Operation Failed. Aborting the process.");
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
case global.CUSTOM_OK_RESPONSE.result: {
|
||||
|
||||
switch (err.message) {
|
||||
case "Too far in the future.": {
|
||||
logger.write(MODULE_NAME, "[WARN] run -> loop -> intitializeProcessFramework -> onInizialized > Too far in the future. This Loop will enter in coma.");
|
||||
nextWaitTime = 'Coma';
|
||||
loopControl(nextWaitTime);
|
||||
return;
|
||||
}
|
||||
case "Not needed now, but soon.": {
|
||||
logger.write(MODULE_NAME, "[WARN] run -> loop -> intitializeProcessFramework -> onInizialized > Not needed now, but soon. This Loop will continue with Normal wait time.");
|
||||
nextWaitTime = 'Normal';
|
||||
loopControl(nextWaitTime);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized > Unhandled custom response received. -> err.message = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.message = " + 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 -> intitializeProcessFramework -> onInizialized -> err = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> err = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
function startProcessFramework() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> Entering function."); }
|
||||
|
||||
processFramework.start(onFinished);
|
||||
|
||||
function onFinished(err) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Entering function."); }
|
||||
|
||||
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);
|
||||
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, "[ERROR] run -> loop -> startProcessFramework -> 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.
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Operation Failed. Aborting the process.");
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
case global.CUSTOM_OK_RESPONSE.result: {
|
||||
|
||||
switch (err.message) {
|
||||
case "Dependency does not exist.": {
|
||||
logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency does not exist. This Loop will go to sleep.");
|
||||
nextWaitTime = 'Sleep';
|
||||
loopControl(nextWaitTime);
|
||||
return;
|
||||
}
|
||||
case "Dependency not ready.": {
|
||||
logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency not ready. This Loop will go to sleep.");
|
||||
nextWaitTime = 'Sleep';
|
||||
loopControl(nextWaitTime);
|
||||
return;
|
||||
}
|
||||
case "Month before it is needed.": {
|
||||
logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Month before it is needed. This Loop will be terminated.");
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
case "Month fully processed.": {
|
||||
logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Month fully processed. This Loop will be terminated.");
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
case "End of the month reached.": {
|
||||
logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> End of the month reached. This Loop will be terminated.");
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled custom response received. -> err.message = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.message = " + 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 -> startProcessFramework -> onFinished -> err = " + err.message);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> err = " + err.message);
|
||||
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."); }
|
||||
@@ -560,7 +868,7 @@
|
||||
|
||||
function checkLoopHealth(pLastLoop) {
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT !== "Cloud") {
|
||||
if (global.CURRENT_EXECUTION_AT !== "Node") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -610,34 +918,8 @@
|
||||
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
let filePath;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) { // This is what determines if the bot is loaded from the devTeam or an endUser copy.
|
||||
case "Cloud": {
|
||||
filePath = global.DEV_TEAM + "/" + "AACloud"; // DevTeams bots only are run at the cloud.
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
if (global.SHALL_BOT_STOP === false) {
|
||||
continueCallBack();
|
||||
} else {
|
||||
stopCallBack();
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> onInizialized -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> onInizialized -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let filePath = global.DEV_TEAM + "/" + "AACloud"; // DevTeams bots only are run at the cloud.
|
||||
|
||||
let fileName = "this.config.json";
|
||||
|
||||
cloudStorage.getTextFile(filePath, fileName, onFileReceived);
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
const axios = require('axios')
|
||||
const appRoot = require('app-root-path')
|
||||
const { logger } = require(`${appRoot}/utils/logger`)
|
||||
const auth = require(`${appRoot}/utils/auth`)
|
||||
const auth = require('../utils/auth')
|
||||
|
||||
exports.updateExecutionResults = async function (summaryDate, buyAverage, sellAverage,
|
||||
marketRate, combinedProfitsA, combinedProfitsB) {
|
||||
|
||||
logger.debug('updateExecutionResults -> Entering Function.')
|
||||
|
||||
try {
|
||||
|
||||
const accessToken = await auth.authenticate()
|
||||
@@ -50,12 +46,10 @@ exports.updateExecutionResults = async function (summaryDate, buyAverage, sellAv
|
||||
}
|
||||
})
|
||||
|
||||
if (!operationsResponse.data.errors)
|
||||
logger.debug('updateExecutionResults -> Operations Module Response: %s', operationsResponse.data.data.operations_UpdateExecutionSummary)
|
||||
else throw new Error(operationsResponse.data.errors[0].message)
|
||||
if (operationsResponse.data.errors)
|
||||
throw new Error(operationsResponse.data.errors[0].message)
|
||||
|
||||
} catch (error) {
|
||||
logger.error('updateExecutionResults -> Error: ' + error.stack)
|
||||
throw new Error('There has been an error updating the execution report: ' + error)
|
||||
throw new Error('There has been an error updating the execution on the clone: ' + error)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,759 @@
|
||||
exports.newMultiPeriodDaily = function newMultiPeriodDaily(bot, logger, COMMONS, UTILITIES, BLOB_STORAGE, USER_BOT_MODULE, COMMONS_MODULE) {
|
||||
|
||||
const FULL_LOG = true;
|
||||
const LOG_FILE_CONTENT = false;
|
||||
const MODULE_NAME = "Multi Period Daily";
|
||||
const commons = COMMONS.newCommons(bot, logger, UTILITIES);
|
||||
const GMT_SECONDS = ':00.000 GMT+0000';
|
||||
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
|
||||
|
||||
thisObject = {
|
||||
initialize: initialize,
|
||||
start: start
|
||||
};
|
||||
|
||||
let utilities = UTILITIES.newCloudUtilities(bot, logger);
|
||||
|
||||
let statusDependencies;
|
||||
let dataDependencies;
|
||||
let storages = [];
|
||||
let dataFiles = [];
|
||||
|
||||
let usertBot;
|
||||
let currentBotStorage = BLOB_STORAGE.newBlobStorage(bot, logger);
|
||||
|
||||
let processConfig;
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
logger.fileName = MODULE_NAME;
|
||||
logger.initialize();
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
statusDependencies = pStatusDependencies;
|
||||
dataDependencies = pDataDependencies;
|
||||
processConfig = pProcessConfig;
|
||||
|
||||
for (let i = 0; i < dataDependencies.config.length; i++) {
|
||||
|
||||
let key;
|
||||
let storage;
|
||||
let dependency = dataDependencies.config[i];
|
||||
|
||||
key = dependency.devTeam + "-" +
|
||||
dependency.bot + "-" +
|
||||
dependency.product + "-" +
|
||||
dependency.dataSet + "-" +
|
||||
dependency.dataSetVersion
|
||||
|
||||
storage = dataDependencies.dataSets.get(key);
|
||||
|
||||
storages.push(storage);
|
||||
|
||||
}
|
||||
|
||||
currentBotStorage.initialize(bot.devTeam, onStorageInizialized);
|
||||
|
||||
function onStorageInizialized(err) {
|
||||
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, BLOB_STORAGE);
|
||||
usertBot.initialize(dataDependencies, callBackFunction);
|
||||
|
||||
} else {
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> onStorageInizialized -> err = " + err.message);
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function start(callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
|
||||
|
||||
let market = global.MARKET;
|
||||
|
||||
/* Context Variables */
|
||||
|
||||
let contextVariables = {
|
||||
lastFile: undefined, // Datetime of the last file files sucessfully produced by this process.
|
||||
dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history.
|
||||
dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process.
|
||||
};
|
||||
|
||||
let previousDay; // Holds the date of the previous day relative to the processing date.
|
||||
|
||||
let interExecutionMemoryArray;
|
||||
|
||||
getContextVariables();
|
||||
|
||||
function getContextVariables() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); }
|
||||
|
||||
let thisReport;
|
||||
let reportKey;
|
||||
let statusReport;
|
||||
|
||||
if (processConfig.framework.startDate.fixedDate !== undefined) {
|
||||
|
||||
/* The starting date is fixed, we will start from there. */
|
||||
|
||||
contextVariables.dateBeginOfMarket = new Date(processConfig.framework.startDate.fixedDate);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
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. ");
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusReport.status === "Status Report is corrupt.") {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
thisReport = statusDependencies.statusReports.get(reportKey).file;
|
||||
|
||||
if (thisReport.lastFile === undefined) {
|
||||
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
|
||||
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
|
||||
|
||||
let customOK = {
|
||||
result: global.CUSTOM_OK_RESPONSE.result,
|
||||
message: "Dependency does not exist."
|
||||
}
|
||||
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
|
||||
callBackFunction(customOK);
|
||||
return;
|
||||
}
|
||||
|
||||
contextVariables.dateBeginOfMarket = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + thisReport.lastFile.hours + ":" + thisReport.lastFile.minutes + GMT_SECONDS);
|
||||
|
||||
}
|
||||
|
||||
if (processConfig.framework.endDate.fixedDate !== undefined) {
|
||||
|
||||
/* The ending date is fixed, we will end there. */
|
||||
|
||||
contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
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. ");
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusReport.status === "Status Report is corrupt.") {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
thisReport = statusDependencies.statusReports.get(reportKey).file;
|
||||
|
||||
if (thisReport.lastFile === undefined) {
|
||||
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
|
||||
|
||||
let customOK = {
|
||||
result: global.CUSTOM_OK_RESPONSE.result,
|
||||
message: "Dependency not ready."
|
||||
}
|
||||
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
|
||||
callBackFunction(customOK);
|
||||
return;
|
||||
}
|
||||
|
||||
contextVariables.dateEndOfMarket = new Date(thisReport.lastFile.valueOf());
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
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. ");
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusReport.status === "Status Report is corrupt.") {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
thisReport = statusDependencies.statusReports.get(reportKey).file;
|
||||
|
||||
if (thisReport.lastFile !== undefined) {
|
||||
|
||||
if (bot.hasTheBotJustStarted === true && processConfig.framework.startDate.resumeExecution === "false") {
|
||||
|
||||
startFromBegining();
|
||||
return;
|
||||
}
|
||||
|
||||
contextVariables.lastFile = new Date(thisReport.lastFile);
|
||||
interExecutionMemoryArray = thisReport.interExecutionMemoryArray;
|
||||
|
||||
processTimePeriods();
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
In the case when there is no status report, we assume like the last processed file is the one on the date of Begining of Market.
|
||||
*/
|
||||
|
||||
startFromBegining();
|
||||
return;
|
||||
}
|
||||
|
||||
function startFromBegining() {
|
||||
|
||||
contextVariables.lastFile = new Date(contextVariables.dateBeginOfMarket.getUTCFullYear() + "-" + (contextVariables.dateBeginOfMarket.getUTCMonth() + 1) + "-" + contextVariables.dateBeginOfMarket.getUTCDate() + " " + "00:00" + GMT_SECONDS);
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> startFromBegining -> thisReport.lastFile === undefined"); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> startFromBegining -> contextVariables.lastFile = " + contextVariables.lastFile); }
|
||||
|
||||
/*
|
||||
The first time the process is running is the right time to create the data structure that is going to be shared across different executions.
|
||||
This data structure has one object per each timePeriod.
|
||||
*/
|
||||
|
||||
interExecutionMemoryArray = [];
|
||||
|
||||
for (let i = 0; i < global.dailyFilePeriods.length; i++) {
|
||||
let emptyObject = {};
|
||||
interExecutionMemoryArray.push(emptyObject);
|
||||
}
|
||||
|
||||
processTimePeriods();
|
||||
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.message);
|
||||
if (err.message === "Cannot read property 'file' of undefined") {
|
||||
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
|
||||
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
|
||||
}
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function processTimePeriods() {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> Entering function."); }
|
||||
|
||||
try {
|
||||
|
||||
let n;
|
||||
let botNeverRan = true;
|
||||
|
||||
bot.processDatetime = new Date(contextVariables.lastFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop.
|
||||
|
||||
advanceTime();
|
||||
|
||||
function advanceTime() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> Entering function."); }
|
||||
|
||||
bot.processDatetime = new Date(bot.processDatetime.valueOf() + ONE_DAY_IN_MILISECONDS);
|
||||
previousDay = new Date(bot.processDatetime.valueOf() - ONE_DAY_IN_MILISECONDS);
|
||||
|
||||
logger.newInternalLoop(bot.codeName, bot.process);
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> bot.processDatetime = " + bot.processDatetime); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> previousDay = " + previousDay); }
|
||||
|
||||
/* Validation that we are not going past the head of the market. */
|
||||
|
||||
if (bot.processDatetime.valueOf() > contextVariables.dateEndOfMarket.valueOf()) {
|
||||
|
||||
const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + ".";
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> advanceTime -> " + logText); }
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
periodsLoop();
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> advanceTime -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function periodsLoop() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoop -> Entering function."); }
|
||||
|
||||
/*
|
||||
|
||||
We will iterate through all posible periods.
|
||||
|
||||
*/
|
||||
|
||||
n = 0 // loop Variable representing each possible period as defined at the periods array.
|
||||
|
||||
periodsLoopBody();
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoop -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function periodsLoopBody() {
|
||||
|
||||
try {
|
||||
|
||||
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];
|
||||
|
||||
let dependencyIndex = 0;
|
||||
dataFiles = [];
|
||||
|
||||
dependencyLoopBody();
|
||||
|
||||
function dependencyLoopBody() {
|
||||
|
||||
try {
|
||||
|
||||
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 previousFile;
|
||||
let currentFile;
|
||||
|
||||
if (bot.processDatetime.valueOf() > contextVariables.dateBeginOfMarket.valueOf()) {
|
||||
getPreviousFile();
|
||||
} else {
|
||||
previousFile = [];
|
||||
getCurrentFile()
|
||||
}
|
||||
|
||||
function getPreviousFile() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> Entering function."); }
|
||||
|
||||
let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2);
|
||||
let filePath = dependency.product + '/' + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath;
|
||||
let fileName = market.assetA + '_' + market.assetB + ".json";
|
||||
|
||||
storage.getTextFile(filePath, fileName, onFileReceived, true);
|
||||
|
||||
function onFileReceived(err, text) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> Entering function."); }
|
||||
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> text = " + text); }
|
||||
|
||||
if (err.message === "File does not exist." && botNeverRan === true) {
|
||||
|
||||
/*
|
||||
Sometimes one of the dependencies of an indicator for some reasons are not calculated from the begining of the market.
|
||||
When that happens we can not find those files. What we do in this situation is to move the time fordward until we can find
|
||||
all the dependencies and the first run of the bot is successful.
|
||||
|
||||
After that, we will not accept more missing files on any of the dependencies, and if any is missing we will abort the processing.
|
||||
*/
|
||||
logger.write(MODULE_NAME, "[WARN] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> Skipping day because file " + filePath + "/" + fileName + " was not found.");
|
||||
|
||||
advanceTime();
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.result === "Fail Because" && err.message === "File does not exist.") {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.message);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> filePath = " + filePath);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> fileName = " + fileName);
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = " + err.message);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> filePath = " + filePath);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> fileName = " + fileName);
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
|
||||
previousFile = JSON.parse(text);
|
||||
|
||||
getCurrentFile();
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getPreviousFile -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentFile() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> Entering function."); }
|
||||
|
||||
let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2);
|
||||
let filePath = dependency.product + '/' + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath;
|
||||
let fileName = market.assetA + '_' + market.assetB + ".json";
|
||||
|
||||
storage.getTextFile(filePath, fileName, onFileReceived, true);
|
||||
|
||||
function onFileReceived(err, text) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> Entering function."); }
|
||||
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> text = " + text); }
|
||||
|
||||
if (err.result === "Fail Because" && err.message === "File does not exist.") {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.message);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> filePath = " + filePath);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> fileName = " + fileName);
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.message);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> filePath = " + filePath);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> fileName = " + fileName);
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
|
||||
currentFile = JSON.parse(text);
|
||||
|
||||
let dataFile = previousFile.concat(currentFile);
|
||||
|
||||
dataFiles.push(dataFile);
|
||||
dependencyControlLoop();
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getCurrentFile -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoop -> dependencyLoopBody -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function dependencyControlLoop() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> dependencyControlLoop -> Entering function."); }
|
||||
|
||||
dependencyIndex++;
|
||||
|
||||
if (dependencyIndex < dataDependencies.config.length) {
|
||||
|
||||
dependencyLoopBody();
|
||||
|
||||
} else {
|
||||
|
||||
callTheBot();
|
||||
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> dependencyControlLoop -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function callTheBot() {
|
||||
|
||||
try {
|
||||
|
||||
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];
|
||||
|
||||
usertBot.start(
|
||||
dataFiles,
|
||||
timePeriod,
|
||||
outputPeriodLabel,
|
||||
bot.processDatetime,
|
||||
contextVariables.dateBeginOfMarket,
|
||||
contextVariables.dateEndOfMarket,
|
||||
interExecutionMemoryArray[n],
|
||||
onBotFinished);
|
||||
|
||||
function onBotFinished(err) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> onBotFinished -> Entering function."); }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
|
||||
botNeverRan = false;
|
||||
periodsControlLoop();
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> onBotFinished -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function periodsControlLoop() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsControlLoop -> Entering function."); }
|
||||
|
||||
n++;
|
||||
|
||||
if (n < global.dailyFilePeriods.length) {
|
||||
|
||||
periodsLoopBody();
|
||||
|
||||
} else {
|
||||
|
||||
n = 0;
|
||||
|
||||
writeDataRanges(onWritten);
|
||||
|
||||
function onWritten(err) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> controlLoop -> onWritten -> Entering function."); }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> controlLoop -> onWritten -> err = " + err.message);
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
|
||||
writeStatusReport(bot.processDatetime, advanceTime);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> controlLoop -> onWritten -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsControlLoop -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function writeDataRanges(callBack) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
|
||||
|
||||
let productIndex = 0;
|
||||
productLoopBody();
|
||||
|
||||
function productLoopBody() {
|
||||
|
||||
let folderName = bot.products[productIndex].codeName;
|
||||
|
||||
writeDataRange(contextVariables.dateBeginOfMarket, bot.processDatetime, folderName, controlLoop);
|
||||
}
|
||||
|
||||
function controlLoop() {
|
||||
|
||||
productIndex++;
|
||||
|
||||
if (productIndex < bot.products.length) {
|
||||
productLoopBody();
|
||||
} else {
|
||||
callBack(global.DEFAULT_OK_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); }
|
||||
|
||||
let dataRange = {
|
||||
begin: pBegin.valueOf(),
|
||||
end: pEnd.valueOf()
|
||||
};
|
||||
|
||||
let fileContent = JSON.stringify(dataRange);
|
||||
|
||||
let fileName = 'Data.Range.' + market.assetA + '_' + market.assetB + '.json';
|
||||
let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process;
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> fileName = " + fileName); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> filePath = " + filePath); }
|
||||
|
||||
currentBotStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated);
|
||||
|
||||
function onFileCreated(err) {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.message);
|
||||
callBack(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (LOG_FILE_CONTENT === true) {
|
||||
logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent);
|
||||
}
|
||||
|
||||
callBack(global.DEFAULT_OK_RESPONSE);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.message);
|
||||
callBack(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function writeStatusReport(lastFileDate, callBack) {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); }
|
||||
|
||||
try {
|
||||
|
||||
let reportKey = bot.devTeam + "-" + bot.codeName + "-" + "Multi-Period-Daily" + "-" + "dataSet.V1";
|
||||
let thisReport = statusDependencies.statusReports.get(reportKey);
|
||||
|
||||
thisReport.file.lastExecution = bot.currentDaytime;
|
||||
thisReport.file.lastFile = lastFileDate;
|
||||
thisReport.file.interExecutionMemoryArray = interExecutionMemoryArray;
|
||||
thisReport.save(callBack);
|
||||
|
||||
bot.hasTheBotJustStarted = false;
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,340 @@
|
||||
exports.newMultiPeriodMarket = function newMultiPeriodMarket(bot, logger, COMMONS, UTILITIES, BLOB_STORAGE, USER_BOT_MODULE, COMMONS_MODULE) {
|
||||
|
||||
const FULL_LOG = true;
|
||||
const LOG_FILE_CONTENT = false;
|
||||
const MODULE_NAME = "Multi Period Market";
|
||||
|
||||
const commons = COMMONS.newCommons(bot, logger, UTILITIES);
|
||||
|
||||
thisObject = {
|
||||
initialize: initialize,
|
||||
start: start
|
||||
};
|
||||
|
||||
let utilities = UTILITIES.newCloudUtilities(bot, logger);
|
||||
|
||||
let statusDependencies;
|
||||
let dataDependencies;
|
||||
let storages = [];
|
||||
let dataFiles = [];
|
||||
|
||||
let usertBot;
|
||||
|
||||
let processConfig;
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
logger.fileName = MODULE_NAME;
|
||||
logger.initialize();
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
statusDependencies = pStatusDependencies;
|
||||
dataDependencies = pDataDependencies;
|
||||
processConfig = pProcessConfig;
|
||||
|
||||
for (let i = 0; i < dataDependencies.config.length; i++) {
|
||||
|
||||
let key;
|
||||
let storage;
|
||||
let dependency = dataDependencies.config[i];
|
||||
|
||||
key = dependency.devTeam + "-" +
|
||||
dependency.bot + "-" +
|
||||
dependency.product + "-" +
|
||||
dependency.dataSet + "-" +
|
||||
dependency.dataSetVersion
|
||||
|
||||
storage = dataDependencies.dataSets.get(key);
|
||||
|
||||
storages.push(storage);
|
||||
|
||||
}
|
||||
|
||||
usertBot = USER_BOT_MODULE.newUserBot(bot, logger, COMMONS_MODULE, UTILITIES, BLOB_STORAGE);
|
||||
usertBot.initialize(dataDependencies, callBackFunction);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function start(callBackFunction) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
|
||||
|
||||
let market = global.MARKET;
|
||||
|
||||
/* Context Variables */
|
||||
|
||||
let contextVariables = {
|
||||
dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history.
|
||||
dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process.
|
||||
};
|
||||
|
||||
if (processConfig.framework.startDate.fixedDate !== undefined) {
|
||||
|
||||
/* The starting date is fixed, we will start from there. */
|
||||
|
||||
contextVariables.dateBeginOfMarket = new Date(processConfig.framework.startDate.fixedDate);
|
||||
|
||||
}
|
||||
|
||||
if (processConfig.framework.endDate.fixedDate !== undefined) {
|
||||
|
||||
/* The ending date is fixed, we will end there. */
|
||||
|
||||
contextVariables.dateEndOfMarket = new Date(processConfig.framework.endDate.fixedDate);
|
||||
|
||||
}
|
||||
|
||||
processTimePeriods();
|
||||
|
||||
function processTimePeriods() {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> Entering function."); }
|
||||
|
||||
try {
|
||||
|
||||
let n;
|
||||
|
||||
periodsLoop();
|
||||
|
||||
function periodsLoop() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoop -> Entering function."); }
|
||||
|
||||
/*
|
||||
|
||||
We will iterate through all posible periods.
|
||||
|
||||
*/
|
||||
|
||||
n = 0 // loop Variable representing each possible period as defined at the periods array.
|
||||
|
||||
periodsLoopBody();
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoop -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function periodsLoopBody() {
|
||||
|
||||
try {
|
||||
|
||||
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];
|
||||
|
||||
let dependencyIndex = 0;
|
||||
dataFiles = [];
|
||||
|
||||
dependencyLoopBody();
|
||||
|
||||
function dependencyLoopBody() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> Entering function."); }
|
||||
|
||||
let dependency = dataDependencies.config[dependencyIndex];
|
||||
let storage = storages[dependencyIndex];
|
||||
|
||||
getFile();
|
||||
|
||||
function getFile() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> Entering function."); }
|
||||
|
||||
let fileName = market.assetA + '_' + market.assetB + ".json";
|
||||
|
||||
let filePath = dependency.product + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
|
||||
|
||||
storage.getTextFile(filePath, fileName, onFileReceived, true);
|
||||
|
||||
function onFileReceived(err, text) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> Entering function."); }
|
||||
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> text = " + text); }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = " + err.message);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> filePath = " + filePath);
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> fileName = " + fileName);
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let dataFile = JSON.parse(text);
|
||||
dataFiles.push(dataFile);
|
||||
|
||||
dependencyControlLoop();
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> dependencyLoopBody -> getFile -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoop -> dependencyLoopBody -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function dependencyControlLoop() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> dependencyControlLoop -> Entering function."); }
|
||||
|
||||
dependencyIndex++;
|
||||
|
||||
if (dependencyIndex < dataDependencies.config.length) {
|
||||
|
||||
dependencyLoopBody();
|
||||
|
||||
} else {
|
||||
|
||||
callTheBot();
|
||||
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> dependencyControlLoop -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function callTheBot() {
|
||||
|
||||
try {
|
||||
|
||||
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];
|
||||
|
||||
usertBot.start(
|
||||
dataFiles,
|
||||
timePeriod,
|
||||
outputPeriodLabel,
|
||||
contextVariables.dateBeginOfMarket,
|
||||
contextVariables.dateEndOfMarket,
|
||||
onBotFinished);
|
||||
|
||||
function onBotFinished(err) {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> onBotFinished -> Entering function."); }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
|
||||
periodsControlLoop();
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> onBotFinished -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err){
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> callTheBot -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsLoopBody -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function periodsControlLoop() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> processTimePeriods -> periodsControlLoop -> Entering function."); }
|
||||
|
||||
n++;
|
||||
|
||||
if (n < global.marketFilesPeriods.length) {
|
||||
|
||||
periodsLoopBody();
|
||||
|
||||
} else {
|
||||
|
||||
writeStatusReport(callBackFunction);
|
||||
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> periodsControlLoop -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> processTimePeriods -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function writeStatusReport(callBack) {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
|
||||
|
||||
try {
|
||||
|
||||
let reportKey = bot.devTeam + "-" + bot.codeName + "-" + "Multi-Period-Market" + "-" + "dataSet.V1";
|
||||
let thisReport = statusDependencies.statusReports.get(reportKey);
|
||||
|
||||
thisReport.file.lastExecution = bot.processDatetime;
|
||||
thisReport.save(callBack);
|
||||
|
||||
}
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.message);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
};
|
||||
+9
-3
@@ -33,6 +33,13 @@
|
||||
<Compile Include="CloudRequire.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="exchange\wrappers\coss.js" />
|
||||
<Compile Include="MultiPeriodDaily.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MultiPeriodMarket.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="utils\auth.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@@ -46,7 +53,6 @@
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="exchange\exchangeUtils.js" />
|
||||
<Compile Include="exchange\wrappers\beautifier.js" />
|
||||
<Compile Include="exchange\wrappers\binance.js" />
|
||||
<Compile Include="exchange\wrappers\binanceClient.js" />
|
||||
<Compile Include="exchange\wrappers\poloniex.js" />
|
||||
@@ -60,7 +66,7 @@
|
||||
<Compile Include="StatusDependencies.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ExtractionBotProcessMainLoop.js">
|
||||
<Compile Include="SensorBotProcessMainLoop.js">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Context.js">
|
||||
@@ -91,7 +97,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Exchange\" />
|
||||
<Folder Include="Exchange\Wrappers\" />
|
||||
<Folder Include="Exchange\wrappers\" />
|
||||
<Folder Include="Integrations\" />
|
||||
<Folder Include="utils\" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# AACloud
|
||||
# Clone Executor
|
||||
|
||||
This repository holds the code of the bits of the AA Platform that run in the cloud.
|
||||
Whithin the Superalgos project, we have the concept of Financial Beings, which are autonomous agents that have a financial life. These FBs entities have a master (imagine a class in programming) and clones (imagine an instance of a class running). The master is the one who aquires reputation after competitions. Every time we need to do a backtest, live trading or participating in a competition, a clone is extracted from the master and is put to run, either in backtest mode, live trading mode or competition mode. The clone lifespan is contrained by the purpose it was created. For instance, a clone created for a competition will live only during the period of such competition.
|
||||
|
||||
## About the AA Project
|
||||
The software needed to execute these clones is the Clone Executor.
|
||||
|
||||
The AA Platform is built and maintained by Advanced Algos, the start up behind the AA Project.
|
||||
## About the Superalgos Project
|
||||
|
||||
Superalgos is built and maintained by the Superalgos Foundation and its Community.
|
||||
|
||||
Our mission is to enable the fast-paced evolution of crypto trading bots within the context of trading bots competitions.
|
||||
|
||||
@@ -24,7 +26,7 @@ If you are interested in coding your own crypto trading bots and eventually part
|
||||
|
||||
## Copyright and License Notice
|
||||
|
||||
© Copyright 2018 Advanced Algos. Please see the [AACloud License](/LICENSE) for information on how you can use this software.
|
||||
© Copyright 2018 Advanced Algos. Please see the [Clone Executor License](/LICENSE) for information on how you can use this software.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
require('dotenv').config()
|
||||
|
||||
global.CURRENT_ENVIRONMENT = "Develop";
|
||||
global.CURRENT_EXECUTION_AT = "Cloud";
|
||||
global.CURRENT_EXECUTION_AT = "Node";
|
||||
global.SHALL_BOT_STOP = false;
|
||||
global.AT_BREAKPOINT = false; // This is used only when running at the browser.
|
||||
global.AT_BREAKPOINT = false; // This is used only when running at the browser.
|
||||
global.RUN_AS_TEAM = false;
|
||||
|
||||
/* Default parameters can be changed by the execution configuration */
|
||||
global.EXCHANGE_NAME = "Poloniex";
|
||||
global.EXCHANGE_NAME = process.env.EXCHANGE_NAME;
|
||||
global.MARKET = {
|
||||
assetA: "USDT",
|
||||
assetB: "BTC"
|
||||
@@ -16,25 +16,17 @@ global.MARKET = {
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.log('[INFO] Run -> uncaughtException -> err.message = ' + err.message);
|
||||
console.log('[INFO] Run -> uncaughtException -> err.stack = ', err.stack);
|
||||
return;
|
||||
process.exit(1)
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('[INFO] Run -> unhandledRejection -> reason = ' + reason);
|
||||
console.log('[INFO] Run -> unhandledRejection -> reason = ' + JSON.stringify(reason));
|
||||
console.log('[INFO] Run -> unhandledRejection -> p = ' + JSON.stringify(p));
|
||||
return;
|
||||
process.exit(1)
|
||||
});
|
||||
|
||||
process.on('exit', function (code) {
|
||||
try {
|
||||
console.log('[INFO] Run -> process.on.exit -> About to exit -> code = ' + code);
|
||||
return;
|
||||
}
|
||||
catch (err) {
|
||||
console.log("[ERROR] Run -> process.on.exit -> Error Logging Error Code.");
|
||||
console.log("[ERROR] Run -> process.on.exit -> err.message = " + err.message);
|
||||
return;
|
||||
}
|
||||
console.log('[INFO] Run -> process.on.exit -> About to exit -> code = ' + code);
|
||||
});
|
||||
|
||||
readExecutionConfiguration();
|
||||
@@ -80,7 +72,7 @@ function readExecutionConfiguration() {
|
||||
backtest: backtest,
|
||||
competition: competition
|
||||
}
|
||||
} else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Extractor') {
|
||||
} else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor') {
|
||||
let allMonths = {
|
||||
run: "false",
|
||||
minYear: process.env.MIN_YEAR,
|
||||
@@ -92,7 +84,9 @@ function readExecutionConfiguration() {
|
||||
month: process.env.MONTH
|
||||
}
|
||||
let noTime = {
|
||||
run: "false"
|
||||
run: "false",
|
||||
beginDatetime: process.env.BEGIN_DATE_TIME,
|
||||
resumeExecution: process.env.RESUME_EXECUTION
|
||||
}
|
||||
let fixedInterval = {
|
||||
run: "false",
|
||||
@@ -112,17 +106,20 @@ function readExecutionConfiguration() {
|
||||
|
||||
startMode[process.env.START_MODE].run = "true"
|
||||
|
||||
let executionList = [{
|
||||
let cloneToExecute = {
|
||||
enabled: "true",
|
||||
devTeam: process.env.DEV_TEAM,
|
||||
bot: process.env.BOT,
|
||||
process: process.env.PROCESS,
|
||||
repo: global.CURRENT_BOT_REPO
|
||||
}]
|
||||
}
|
||||
|
||||
global.EXECUTION_CONFIG = {
|
||||
executionList: executionList,
|
||||
startMode: startMode
|
||||
cloneToExecute: cloneToExecute,
|
||||
startMode: startMode,
|
||||
timePeriod: getTimePeriod(process.env.TIME_PERIOD),
|
||||
timePeriodFileStorage: process.env.TIME_PERIOD,
|
||||
dataSet: process.env.DATA_SET
|
||||
};
|
||||
|
||||
readStoragePermissions();
|
||||
@@ -130,13 +127,44 @@ function readExecutionConfiguration() {
|
||||
|
||||
catch (err) {
|
||||
console.log("[ERROR] readExecutionConfiguration -> err = " + err.message);
|
||||
console.log("[ERROR] readExecutionConfiguration -> Please verify that the Start Mode for the type of Bot configured applies to that type.");
|
||||
console.log("[ERROR] readExecutionConfiguration -> err = " + err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
function readStoragePermissions() {
|
||||
let filePath;
|
||||
function getTimePeriod(timePeriod){
|
||||
if(timePeriod !== undefined){
|
||||
try {
|
||||
let timePeriodMap = new Map()
|
||||
timePeriodMap.set("24-hs", 86400000)
|
||||
timePeriodMap.set("12-hs", 43200000)
|
||||
timePeriodMap.set("08-hs", 28800000)
|
||||
timePeriodMap.set("06-hs", 21600000)
|
||||
timePeriodMap.set("04-hs", 14400000)
|
||||
timePeriodMap.set("03-hs", 10800000)
|
||||
timePeriodMap.set("02-hs", 7200000)
|
||||
timePeriodMap.set("01-hs", 3600000)
|
||||
timePeriodMap.set("45-min", 2700000)
|
||||
timePeriodMap.set("40-min", 2400000)
|
||||
timePeriodMap.set("30-min", 1800000)
|
||||
timePeriodMap.set("20-min", 1200000)
|
||||
timePeriodMap.set("15-min", 900000)
|
||||
timePeriodMap.set("10-min", 600000)
|
||||
timePeriodMap.set("05-min", 300000)
|
||||
timePeriodMap.set("04-min", 240000)
|
||||
timePeriodMap.set("03-min", 180000)
|
||||
timePeriodMap.set("02-min", 120000)
|
||||
timePeriodMap.set("01-min", 60000)
|
||||
return timePeriodMap.get(timePeriod)
|
||||
} catch (error) {
|
||||
console.log( "[WARN] Run -> readExecutionConfiguration -> getTimePeriod -> Error: ", error);
|
||||
}
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function readStoragePermissions() {
|
||||
try {
|
||||
console.log( "[INFO] Run -> readStoragePermissions -> Entering function. ");
|
||||
|
||||
@@ -186,10 +214,8 @@ function readStoragePermissions() {
|
||||
startRoot();
|
||||
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
console.log("[ERROR] Run -> readStoragePermissions -> err = " + err.message);
|
||||
console.log("[HINT] Run -> readStoragePermissions -> You need to have a file at this path -> " + filePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
exports.newExtractionBotProcessMainLoop = function newExtractionBotProcessMainLoop(bot, parentLogger) {
|
||||
exports.newSensorBotProcessMainLoop = function newSensorBotProcessMainLoop(bot, parentLogger) {
|
||||
|
||||
const ROOT_DIR = './';
|
||||
|
||||
const MODULE_NAME = "Extraction Bot Process Main Loop";
|
||||
const MODULE_NAME = "Sensor Bot Process Main Loop";
|
||||
const FULL_LOG = true;
|
||||
|
||||
let USER_BOT_MODULE;
|
||||
@@ -13,7 +13,7 @@
|
||||
const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog');
|
||||
let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble.
|
||||
|
||||
let nextLoopTimeoutHandle;
|
||||
let nextLoopTimeoutHandle;
|
||||
let checkLoopHealthHandle;
|
||||
|
||||
let thisObject = {
|
||||
@@ -22,7 +22,7 @@
|
||||
};
|
||||
|
||||
let processConfig;
|
||||
let UI_COMMANDS;
|
||||
let UI_COMMANDS;
|
||||
|
||||
return thisObject;
|
||||
|
||||
@@ -48,24 +48,10 @@
|
||||
|
||||
let filePath;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) { // This is what determines if the bot is loaded from the devTeam or an endUser copy.
|
||||
case "Cloud": {
|
||||
if (global.RUN_AS_TEAM) {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name; // DevTeams bots only are run at the cloud.
|
||||
} else {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return; }
|
||||
if (global.RUN_AS_TEAM) {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name; // DevTeams bots only are run at the cloud.
|
||||
} else {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
}
|
||||
|
||||
const CLOUD_REQUIRE = require(ROOT_DIR + 'CloudRequire');
|
||||
@@ -84,22 +70,7 @@
|
||||
|
||||
USER_BOT_MODULE = pMODULE;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) {
|
||||
case "Cloud": {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo;
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo;
|
||||
|
||||
cloudRequire.downloadCommons(cloudStorage, filePath, onCommonsDownloaded);
|
||||
|
||||
@@ -134,7 +105,7 @@
|
||||
try {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] run -> Entering function."); }
|
||||
|
||||
bot.enableCheckLoopHealth = true;
|
||||
bot.enableCheckLoopHealth = true;
|
||||
|
||||
let fixedTimeLoopIntervalHandle;
|
||||
|
||||
@@ -173,7 +144,7 @@
|
||||
|
||||
const UTILITIES = require(ROOT_DIR + 'CloudUtilities');
|
||||
const STATUS_REPORT = require(ROOT_DIR + 'StatusReport');
|
||||
const EXCHANGE_API = require(ROOT_DIR + 'Exchange/ExchangeAPI');
|
||||
const EXCHANGE_API = require('@superalgos/exchangegateway');
|
||||
const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies');
|
||||
|
||||
/* 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. */
|
||||
@@ -182,8 +153,8 @@
|
||||
|
||||
/* High level log entry */
|
||||
|
||||
console.log(bot.processDatetime.toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + " " + pad(pMonth, 2) + "/" + pad(pYear, 4)
|
||||
+ " Entered into Main Loop # " + pad(Number(bot.loopCounter), 8));
|
||||
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. */
|
||||
|
||||
@@ -275,7 +246,7 @@
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> Entering function."); }
|
||||
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(bot, logger);
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, global.EXCHANGE_NAME);
|
||||
|
||||
exchangeAPI.initialize(onInizialized);
|
||||
|
||||
@@ -610,7 +581,7 @@
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onContinue -> Entering function."); }
|
||||
|
||||
/* Extraction bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */
|
||||
/* Sensor bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */
|
||||
|
||||
switch (nextWaitTime) {
|
||||
case 'Normal': {
|
||||
@@ -620,7 +591,7 @@
|
||||
logger.persist();
|
||||
return;
|
||||
} else {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.normalWaitTime / 1000) + " seconds."); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.normalWaitTime / 1000) + " seconds."); }
|
||||
checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.normalWaitTime * 5, bot.loopCounter);
|
||||
nextLoopTimeoutHandle = setTimeout(loop, processConfig.normalWaitTime);
|
||||
logger.persist();
|
||||
@@ -664,7 +635,7 @@
|
||||
|
||||
function checkLoopHealth(pLastLoop) {
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT !== "Cloud") {
|
||||
if (global.CURRENT_EXECUTION_AT !== "Node") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -714,33 +685,7 @@
|
||||
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
let filePath;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) { // This is what determines if the bot is loaded from the devTeam or an endUser copy.
|
||||
case "Cloud": {
|
||||
filePath = global.DEV_TEAM + "/" + "AACloud"; // DevTeams bots only are run at the cloud.
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
if (global.SHALL_BOT_STOP === false) {
|
||||
continueCallBack();
|
||||
} else {
|
||||
stopCallBack();
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> onInizialized -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> onInizialized -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let filePath = global.DEV_TEAM + "/" + "AACloud"; // DevTeams bots only are run at the cloud.
|
||||
|
||||
let fileName = "this.config.json";
|
||||
|
||||
@@ -750,7 +695,7 @@
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
/*
|
||||
/*
|
||||
If for any reason this config file cannot be read, we are not going to abort the loop for that. Instead we are going to assume
|
||||
that there are no instructions to stop and we will keep the show running.
|
||||
*/
|
||||
@@ -6,6 +6,7 @@
|
||||
const MODULE_NAME = "Status Dependencies";
|
||||
|
||||
let thisObject = {
|
||||
config: undefined,
|
||||
statusReports: new Map(),
|
||||
initialize: initialize,
|
||||
keys: []
|
||||
@@ -22,6 +23,8 @@
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
thisObject.config = pStatusDependenciesConfig;
|
||||
|
||||
/*
|
||||
|
||||
For each dependency declared at the bot config, we will initialize the status report, and load it as part of this initialization process.
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
let USER_BOT_MODULE;
|
||||
let COMMONS_MODULE;
|
||||
|
||||
const BLOB_STORAGE = require(ROOT_DIR + 'BlobStorage');
|
||||
const operationsIntegration = require('./Integrations/OperationsModuleIntegration')
|
||||
|
||||
const BLOB_STORAGE = require(ROOT_DIR + 'BlobStorage');
|
||||
const DEBUG_MODULE = require(ROOT_DIR + 'DebugLog');
|
||||
let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble.
|
||||
|
||||
let nextLoopTimeoutHandle;
|
||||
let nextLoopTimeoutHandle;
|
||||
let checkLoopHealthHandle;
|
||||
|
||||
let thisObject = {
|
||||
@@ -24,7 +25,7 @@
|
||||
bot["botCache"] = new Map();
|
||||
let processConfig;
|
||||
let cloudStorage;
|
||||
let UI_COMMANDS;
|
||||
let UI_COMMANDS;
|
||||
|
||||
return thisObject;
|
||||
|
||||
@@ -51,17 +52,13 @@
|
||||
let filePath;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) { // This is what determines if the bot is loaded from the devTeam or an endUser copy.
|
||||
case "Cloud": {
|
||||
case "Node": {
|
||||
if (global.RUN_AS_TEAM) {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name; // DevTeams bots only are run at the cloud.
|
||||
} else {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO + "/" + pProcessConfig.name; // DevTeam Members bots only are run at the browser.
|
||||
|
||||
break;
|
||||
}
|
||||
case "IDE": {
|
||||
@@ -69,7 +66,7 @@
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> CURRENT_EXECUTION_AT must be either 'Node' or 'IDE'.");
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
@@ -92,22 +89,7 @@
|
||||
|
||||
USER_BOT_MODULE = pMODULE;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) {
|
||||
case "Cloud": {
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo;
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
filePath = global.DEV_TEAM + "/" + "members" + "/" + global.USER_LOGGED_IN + "/" + global.CURRENT_BOT_REPO;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onInizialized -> onBotDownloaded -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo;
|
||||
|
||||
cloudRequire.downloadCommons(cloudStorage, filePath, onCommonsDownloaded);
|
||||
|
||||
@@ -144,7 +126,7 @@
|
||||
try {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] run -> Entering function."); }
|
||||
|
||||
bot.enableCheckLoopHealth = true;
|
||||
bot.enableCheckLoopHealth = true;
|
||||
|
||||
let fixedTimeLoopIntervalHandle;
|
||||
|
||||
@@ -181,7 +163,7 @@
|
||||
/* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */
|
||||
|
||||
const UTILITIES = require(ROOT_DIR + 'CloudUtilities');
|
||||
const EXCHANGE_API = require(ROOT_DIR + 'Exchange/ExchangeAPI');
|
||||
const EXCHANGE_API = require('@superalgos/exchangegateway');
|
||||
const CONTEXT = require(ROOT_DIR + 'Context');
|
||||
const ASSISTANT = require(ROOT_DIR + 'Assistant');
|
||||
const STATUS_REPORT = require(ROOT_DIR + 'StatusReport');
|
||||
@@ -247,7 +229,7 @@
|
||||
|
||||
} else {
|
||||
|
||||
bot.processDatetime = new Date(bot.backtest.beginDatetime); // Set the starting time as the configured beginDatetime.
|
||||
bot.processDatetime = new Date(bot.backtest.beginDatetime); // Set the starting time as the configured beginDatetime.
|
||||
|
||||
}
|
||||
|
||||
@@ -262,7 +244,7 @@
|
||||
|
||||
} else {
|
||||
|
||||
bot.processDatetime = new Date(bot.processDatetime.valueOf() + timePeriod); // We advance time here.
|
||||
bot.processDatetime = new Date(bot.processDatetime.valueOf() + timePeriod); // We advance time here.
|
||||
|
||||
|
||||
if (UI_COMMANDS.eventHandler !== undefined) {
|
||||
@@ -275,15 +257,15 @@
|
||||
|
||||
if (UI_COMMANDS.endDatetime !== undefined) {
|
||||
|
||||
endDatetime = new Date(UI_COMMANDS.endDatetime.valueOf());
|
||||
endDatetime = new Date(UI_COMMANDS.endDatetime.valueOf());
|
||||
|
||||
} else {
|
||||
|
||||
endDatetime = new Date(bot.backtest.endDatetime);
|
||||
endDatetime = new Date(bot.backtest.endDatetime);
|
||||
|
||||
}
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT === "Cloud") {
|
||||
if (global.CURRENT_EXECUTION_AT === "Node") {
|
||||
|
||||
if (bot.processDatetime.valueOf() > endDatetime.valueOf()) {
|
||||
|
||||
@@ -294,7 +276,7 @@
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, context);
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -322,7 +304,7 @@
|
||||
|
||||
}
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT === "Cloud") {
|
||||
if (global.CURRENT_EXECUTION_AT === "Node") {
|
||||
|
||||
let beginDatetime = new Date(bot.competition.beginDatetime);
|
||||
|
||||
@@ -346,7 +328,7 @@
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, context);
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -380,7 +362,7 @@
|
||||
+ " " + bot.startMode + " 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. */
|
||||
|
||||
|
||||
let exchangeAPI;
|
||||
let assistant;
|
||||
let userBot;
|
||||
@@ -536,7 +518,7 @@
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initializeContext() {
|
||||
|
||||
try {
|
||||
@@ -616,7 +598,7 @@
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> Entering function."); }
|
||||
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(bot, logger);
|
||||
exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, global.EXCHANGE_NAME);
|
||||
|
||||
exchangeAPI.initialize(onInizialized);
|
||||
|
||||
@@ -864,7 +846,6 @@
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, emailList = global.EMAIL_CONFIG.adminList);
|
||||
callBackFunction(err);
|
||||
return;
|
||||
}
|
||||
@@ -877,7 +858,6 @@
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, emailList = global.EMAIL_CONFIG.adminList);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
@@ -890,7 +870,6 @@
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, emailList = global.EMAIL_CONFIG.adminList);
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
@@ -902,7 +881,6 @@
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, emailList = global.EMAIL_CONFIG.adminList);
|
||||
callBackFunction(err);
|
||||
}
|
||||
}
|
||||
@@ -925,7 +903,7 @@
|
||||
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);
|
||||
updateClonesModule();
|
||||
return;
|
||||
}
|
||||
case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
|
||||
@@ -984,6 +962,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function updateClonesModule() {
|
||||
|
||||
try {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> updateClonesModule -> Entering function."); }
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT === "Node") {
|
||||
|
||||
// Update clones module with latest context informations
|
||||
|
||||
let lastExecution = context.newHistoryRecord
|
||||
let date = lastExecution.date / 1000 | 0
|
||||
let buyAvgRate = lastExecution.buyAvgRate
|
||||
let sellAvgRate = lastExecution.sellAvgRate
|
||||
let marketRate = lastExecution.marketRate
|
||||
let combinedProfitsA = lastExecution.combinedProfitsA
|
||||
let combinedProfitsB = lastExecution.combinedProfitsB
|
||||
|
||||
await operationsIntegration.updateExecutionResults(date, buyAvgRate, sellAvgRate,
|
||||
marketRate, combinedProfitsA, combinedProfitsB)
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> updateClonesModule -> Clones Module Updated."); }
|
||||
}
|
||||
|
||||
nextWaitTime = 'Normal';
|
||||
loopControl(nextWaitTime);
|
||||
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> updateClonesModule -> err = " + err.message);
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> updateClonesModule -> Execution will continue anyways. ");
|
||||
nextWaitTime = 'Normal';
|
||||
loopControl(nextWaitTime);
|
||||
}
|
||||
}
|
||||
|
||||
function loopControl(nextWaitTime) {
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Entering function."); }
|
||||
@@ -1003,7 +1016,7 @@
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, context);
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
||||
return;
|
||||
|
||||
}
|
||||
@@ -1047,7 +1060,7 @@
|
||||
|
||||
function checkLoopHealth(pLastLoop) {
|
||||
|
||||
if (global.CURRENT_EXECUTION_AT !== "Cloud") {
|
||||
if (global.CURRENT_EXECUTION_AT !== "Node") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1099,33 +1112,7 @@
|
||||
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
let filePath;
|
||||
|
||||
switch (global.CURRENT_EXECUTION_AT) { // This is what determines if the bot is loaded from the devTeam or an endUser copy.
|
||||
case "Cloud": {
|
||||
filePath = global.DEV_TEAM + "/" + "AACloud"; // DevTeams bots only are run at the cloud.
|
||||
break;
|
||||
}
|
||||
case "Browser": {
|
||||
if (global.SHALL_BOT_STOP === false) {
|
||||
continueCallBack();
|
||||
} else {
|
||||
stopCallBack();
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> onInizialized -> CURRENT_EXECUTION_AT must be either 'Cloud' or 'Browser'.");
|
||||
logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> onInizialized -> global.CURRENT_EXECUTION_AT = " + global.CURRENT_EXECUTION_AT);
|
||||
logger.persist();
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
bot.enableCheckLoopHealth = false;
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let filePath = global.DEV_TEAM + "/" + "AACloud"; // DevTeams bots only are run at the cloud.
|
||||
|
||||
let fileName = "this.config.json";
|
||||
|
||||
@@ -1135,7 +1122,7 @@
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
|
||||
/*
|
||||
/*
|
||||
If for any reason this config file cannot be read, we are not going to abort the loop for that. Instead we are going to assume
|
||||
that there are no instructions to stop and we will keep the show running.
|
||||
*/
|
||||
@@ -1185,7 +1172,7 @@
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = " + err.message);
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = " + err.stack);
|
||||
clearInterval(fixedTimeLoopIntervalHandle);
|
||||
clearTimeout(nextLoopTimeoutHandle);
|
||||
clearTimeout(checkLoopHealthHandle);
|
||||
|
||||
Generated
+1944
-41
File diff suppressed because it is too large.
Load diff
+4
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "AACloud",
|
||||
"name": "@superalgos/clone-executor",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "Run.js",
|
||||
@@ -9,20 +9,21 @@
|
||||
"author": "Luis-Fernando-Molina",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@superalgos/exchangegateway": "0.0.1",
|
||||
"@superalgos/mqservice": "0.0.12",
|
||||
"apollo-boost": "^0.1.16",
|
||||
"app-root-path": "^2.1.0",
|
||||
"assert": "^1.4.1",
|
||||
"axios": "^0.18.0",
|
||||
"azure-storage": "^2.8.2",
|
||||
"dotenv": "^6.0.0",
|
||||
"graphql": "^14.0.2",
|
||||
"graphql-client": "^2.0.1",
|
||||
"jsonwebtoken": "^8.4.0",
|
||||
"lodash": "^4.17.11",
|
||||
"nodemailer": "^4.6.8",
|
||||
"nonce": "^1.0.4",
|
||||
"querystring": "^0.2.0",
|
||||
"retry": "^0.12.0",
|
||||
"superagent": "^5.0.2",
|
||||
"underscore": "^1.9.1",
|
||||
"winston": "^3.1.0"
|
||||
}
|
||||
|
||||
+2
-46
@@ -1,54 +1,10 @@
|
||||
{
|
||||
"codeName": "AACloud",
|
||||
"codeName": "clone-executor",
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 1,
|
||||
"patch": 0
|
||||
},
|
||||
"executionList": [
|
||||
{
|
||||
"enabled": "false",
|
||||
"devTeam": "AAMasters",
|
||||
"bot": "AAMariam",
|
||||
"process": "Trading-Process",
|
||||
"repo": "AAMariam-Trading-Bot"
|
||||
},
|
||||
{
|
||||
"enabled": "false",
|
||||
"devTeam": "AAMasters",
|
||||
"bot": "AACharly",
|
||||
"process": "Poloniex-Live-Trades",
|
||||
"repo": "AACharly-Extraction-Bot"
|
||||
},
|
||||
{
|
||||
"enabled": "false",
|
||||
"devTeam": "AAMasters",
|
||||
"bot": "AACharly",
|
||||
"process": "Poloniex-Hole-Fixing",
|
||||
"repo": "AACharly-Extraction-Bot"
|
||||
},
|
||||
{
|
||||
"enabled": "false",
|
||||
"devTeam": "AAMasters",
|
||||
"bot": "AABruce",
|
||||
"process": "One-Min-Daily-Candles-Volumes",
|
||||
"repo": "AABruce-Indicator-Bot"
|
||||
},
|
||||
{
|
||||
"enabled": "false",
|
||||
"devTeam": "AAMasters",
|
||||
"bot": "AAOlivia",
|
||||
"process": "Multi-Period-Daily",
|
||||
"repo": "AAOlivia-Indicator-Bot"
|
||||
},
|
||||
{
|
||||
"enabled": "false",
|
||||
"devTeam": "AAMasters",
|
||||
"bot": "AAOlivia",
|
||||
"process": "Multi-Period-Market",
|
||||
"repo": "AAOlivia-Indicator-Bot"
|
||||
}
|
||||
],
|
||||
"stopGracefully": false,
|
||||
"marketRateProvider": {
|
||||
"devTeam": "AAMasters",
|
||||
@@ -61,4 +17,4 @@
|
||||
"dataSet": "One-Min",
|
||||
"dataSetVersion": "dataSet.V1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
const axios = require('axios')
|
||||
const appRoot = require('app-root-path')
|
||||
const { logger } = require(`${appRoot}/utils/logger`)
|
||||
|
||||
exports.authenticate = async function () {
|
||||
try {
|
||||
|
||||
if (!global.ACCESS_TOKEN) {
|
||||
logger.debug('authenticate -> Entering Function.')
|
||||
|
||||
const authBody = '{"client_id":"' + process.env.AUTH_CLIENT_ID
|
||||
+ '","client_secret": "' + process.env.AUTH_CLIENT_SECRET
|
||||
@@ -33,7 +30,6 @@ exports.authenticate = async function () {
|
||||
return global.ACCESS_TOKEN
|
||||
|
||||
} catch (error) {
|
||||
logger.error('authenticate -> Error: ' + error.stack)
|
||||
throw new Error('There has been an error on the authentication: ' + error)
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -1,11 +1,10 @@
|
||||
const winston = require('winston')
|
||||
const appRoot = require('app-root-path')
|
||||
const util = require('util')
|
||||
|
||||
const options = {
|
||||
file: {
|
||||
level: 'info',
|
||||
filename: `${appRoot}/logs/app.log`,
|
||||
filename: '../logs/app.log',
|
||||
handleExceptions: true,
|
||||
json: true,
|
||||
maxsize: 5242880, // 5MB
|
||||
|
||||
Reference in new issue
Block a user