From 49877c148761f49868996ba6ec76ea391d461f6f Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Thu, 5 Jul 2018 15:25:36 -0300 Subject: [PATCH 01/14] Adding fix for a new dns error when trying to connect to Azure The execution will wait for a moment and then retry when there is a connection issue. --- BlobStorage.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BlobStorage.js b/BlobStorage.js index ba1c857c8..da720ae37 100644 --- a/BlobStorage.js +++ b/BlobStorage.js @@ -156,7 +156,8 @@ exports.newBlobStorage = function newBlobStorage(BOT, logger) { err.code === 'EADDRINUSE' || err.code === 'AuthenticationFailed' || err.code === 'OperationTimedOut' || - err.code === 'ServerBusy' + err.code === 'ServerBusy' || + err.code === 'EAI_AGAIN' ) { @@ -277,7 +278,8 @@ exports.newBlobStorage = function newBlobStorage(BOT, logger) { err.code === 'EADDRINUSE' || err.code === 'AuthenticationFailed' || err.code === 'OperationTimedOut' || - err.code === 'ServerBusy' + err.code === 'ServerBusy' || + err.code === 'EAI_AGAIN' ) { From 32afb3a1a64894a9ce40daf47a43e34ad555656d Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Thu, 5 Jul 2018 15:28:07 -0300 Subject: [PATCH 02/14] Adding fix for the move and put position. We need to ensure the format of the values received as parameters on movePosition and putPosition. Rounding issues are fixed at validating the exchange positions on only 6 positions. Parsing additional values is not necesary. --- Assistant.js | 53 ++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/Assistant.js b/Assistant.js index f725356e7..7c14a0f58 100644 --- a/Assistant.js +++ b/Assistant.js @@ -181,7 +181,7 @@ if (bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) { marketRate = (candle.open + candle.close) / 2; - marketRate = Number(parseFloat(marketRate).toFixed(8)); + marketRate = Number(marketRate.toFixed(8)); context.newHistoryRecord.marketRate = marketRate; ticker = { @@ -543,9 +543,9 @@ id: Math.trunc(Math.random(1) * 1000000), type: thisPosition.type, rate: thisPosition.rate.toString(), - amountA: Number(parseFloat(thisPosition.amountA).toFixed(8)).toString(), - amountB: Number(parseFloat(thisPosition.amountB).toFixed(8)).toString(), - fee: Number(parseFloat(feeRate).toFixed(8)).toString(), + amountA: Number(thisPosition.amountA.toFixed(8)).toString(), + amountB: Number(thisPosition.amountB.toFixed(8)).toString(), + fee: Number(feeRate.toFixed(8)).toString(), date: (new Date()).valueOf() } @@ -636,8 +636,8 @@ sumAssetB = sumAssetB + Number(trade.amountB); } - sumAssetA = Number(parseFloat(sumAssetA).toFixed(8)); - sumAssetB = Number(parseFloat(sumAssetB).toFixed(8)); + sumAssetA = Number(sumAssetA.toFixed(8)); + sumAssetB = Number(sumAssetB.toFixed(8)); if (position.amountB !== sumAssetB) { logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> position.amountB = " + position.amountB); @@ -711,10 +711,10 @@ sumAssetA = sumAssetA + Number(exchangePosition.amountA); sumAssetB = sumAssetB + Number(exchangePosition.amountB); - sumAssetA = Number(parseFloat(sumAssetA).toFixed(6)); // Fixed to 6 positions to avoid rounding issues - sumAssetB = Number(parseFloat(sumAssetB).toFixed(8)); + sumAssetA = Number(sumAssetA.toFixed(6)); // Fixed to 6 positions to avoid rounding issues + sumAssetB = Number(sumAssetB.toFixed(8)); - if (parseFloat(position.amountA).toFixed(6) !== sumAssetA || position.amountB !== sumAssetB ) { + if (position.amountA.toFixed(6) !== sumAssetA || position.amountB !== sumAssetB ) { logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountA = " + position.amountA); logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumAssetA = " + sumAssetA); logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountB = " + position.amountB); @@ -779,28 +779,28 @@ if (trade.type === 'buy') { let fee = Number(trade.fee) * trade.amountB; - let fixedFee = Number(parseFloat(fee).toFixed(8)); + let fixedFee = Number(fee.toFixed(8)); assetA = context.executionContext.balance.assetA - Number(trade.amountA); assetB = context.executionContext.balance.assetB + Number(trade.amountB) - fixedFee; let available = context.executionContext.availableBalance.assetB + Number(trade.amountB) - fixedFee; - context.executionContext.availableBalance.assetB = Number(parseFloat(available).toFixed(8)); + context.executionContext.availableBalance.assetB = Number(available.toFixed(8)); } if (trade.type === 'sell') { let fee = Number(trade.fee) * trade.amountA; - let fixedFee = Number(parseFloat(fee).toFixed(8)); + let fixedFee = Number(fee.toFixed(8)); assetA = context.executionContext.balance.assetA + Number(trade.amountA) - fixedFee; assetB = context.executionContext.balance.assetB - Number(trade.amountB); let available = context.executionContext.availableBalance.assetA + Number(trade.amountA) - fixedFee; - context.executionContext.availableBalance.assetA = Number(parseFloat(available).toFixed(8)); + context.executionContext.availableBalance.assetA = Number(available.toFixed(8)); } - context.executionContext.balance.assetA = Number(parseFloat(assetA).toFixed(8)); - context.executionContext.balance.assetB = Number(parseFloat(assetB).toFixed(8)); + context.executionContext.balance.assetA = Number(assetA.toFixed(8)); + context.executionContext.balance.assetB = Number(assetB.toFixed(8)); } } catch (err) { @@ -859,10 +859,6 @@ 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 -> pType = " + pType); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pRate = " + pRate); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + pAmountA); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + pAmountB); } /* Removing extra decimals. */ @@ -870,6 +866,11 @@ pAmountA = Number(parseFloat(pAmountA).toFixed(8)); pAmountB = Number(parseFloat(pAmountB).toFixed(8)); + 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 = " + pRate); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + pAmountA); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + pAmountB); } + /* Validations that the limits are not surpassed. */ if (pType === 'buy') { @@ -889,7 +890,7 @@ return; } - let aRate = Number(parseFloat(pAmountA / pRate).toFixed(8)); + let aRate = Number((pAmountA / pRate).toFixed(8)); if (aRate !== pAmountB) { @@ -925,7 +926,7 @@ return; } - let bRate = Number(parseFloat(pAmountB * pRate).toFixed(8)); + let bRate = Number((pAmountB * pRate).toFixed(8)); if (bRate !== pAmountA) { @@ -1022,14 +1023,14 @@ if (position.type === 'buy') { context.executionContext.availableBalance.assetA = context.executionContext.availableBalance.assetA - pAmountA; - context.executionContext.availableBalance.assetA = Number(parseFloat(context.executionContext.availableBalance.assetA).toFixed(8)); + context.executionContext.availableBalance.assetA = Number(context.executionContext.availableBalance.assetA.toFixed(8)); context.newHistoryRecord.lastBuyRate = pRate; } if (position.type === 'sell') { context.executionContext.availableBalance.assetB = context.executionContext.availableBalance.assetB - pAmountB; - context.executionContext.availableBalance.assetB = Number(parseFloat(context.executionContext.availableBalance.assetB).toFixed(8)); + context.executionContext.availableBalance.assetB = Number(context.executionContext.availableBalance.assetB.toFixed(8)); context.newHistoryRecord.lastSellRate = pRate; } @@ -1068,9 +1069,12 @@ 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 = " + pNewRate); } + /* Removing extra decimals. */ + pNewRate = Number(parseFloat(pNewRate).toFixed(8)); + let newAmountB; if (pPosition.type === "buy") { - newAmountB = parseFloat(pPosition.amountA / pNewRate).toFixed(7); // If it was fixed to 8 positions there could be some rounding up and there will be not enough AssetA for buying + newAmountB = Number((pPosition.amountA / pNewRate).toFixed(8)); } else { newAmountB = pPosition.amountB; } @@ -1293,4 +1297,5 @@ return context.executionContext.remember[pKey]; } + }; \ No newline at end of file From ef748165341cd1ad01f1102d0ce67165a7c4a07a Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Thu, 5 Jul 2018 15:47:37 -0300 Subject: [PATCH 03/14] Adding email function --- Assistant.js | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Assistant.js b/Assistant.js index 7c14a0f58..c840434e0 100644 --- a/Assistant.js +++ b/Assistant.js @@ -7,6 +7,8 @@ */ const MODULE_NAME = "Assistant"; + + var nodemailer = require('nodemailer'); let bot = BOT; @@ -26,7 +28,8 @@ getTicker: getTicker, sendMessage: sendMessage, rememberThis: rememberThis, - remindMeOf: remindMeOf + remindMeOf: remindMeOf, + sendEmail: sendEmail }; let utilities = UTILITIES.newCloudUtilities(bot, logger); @@ -1271,6 +1274,10 @@ context.newHistoryRecord.messageRelevance = pRelevance; context.newHistoryRecord.messageTitle = pTitle; context.newHistoryRecord.messageBody = pBody; + + if(pRelevance > 6){ + sendEmail(pTitle, pBody); + } } @@ -1298,4 +1305,34 @@ } + function sendEmail(pTitle, pBody) { + try { + + let transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: '', + pass: '' + } + }); + + let mailOptions = { + from: '', + to: '', + subject: bot.starMode + ' - ' + pTitle, + text: pBody + }; + + transporter.sendMail(mailOptions, function (error, info) { + if (error) { + logger.write(MODULE_NAME, "[ERROR] sendEmail -> err = " + error); + } else { + logger.write(MODULE_NAME, "[INFO] sendEmail -> Email sent = " + info.response); + } + }); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] sendEmail -> err = " + err.message); + } + } }; \ No newline at end of file From 99dded14fdb50c387c713a7a63abea9ec3ba3d87 Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Thu, 5 Jul 2018 16:57:21 -0300 Subject: [PATCH 04/14] Fixing method name --- Assistant.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assistant.js b/Assistant.js index c840434e0..ccc5b0d90 100644 --- a/Assistant.js +++ b/Assistant.js @@ -1319,7 +1319,7 @@ let mailOptions = { from: '', to: '', - subject: bot.starMode + ' - ' + pTitle, + subject: bot.startMode + ' - ' + pTitle, text: pBody }; From 1059106fc2cd2b545abea33e1b679a9933a3361e Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 10:49:11 -0300 Subject: [PATCH 05/14] Updating blob storage and assistant --- Assistant.js | 110 +++++++++++++++++++++++++++++++++++-------------- BlobStorage.js | 24 +++++------ 2 files changed, 90 insertions(+), 44 deletions(-) diff --git a/Assistant.js b/Assistant.js index ccc5b0d90..70cb8acdc 100644 --- a/Assistant.js +++ b/Assistant.js @@ -184,7 +184,7 @@ if (bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) { marketRate = (candle.open + candle.close) / 2; - marketRate = Number(marketRate.toFixed(8)); + marketRate = Number(parseFloat(marketRate).toFixed(8)); context.newHistoryRecord.marketRate = marketRate; ticker = { @@ -405,7 +405,7 @@ /* - Here we check that all the positions we know we have are still at the exchange. If they are not, we will try to take appropiate + Here we check that all the positions we know we still have at the exchange. If they are not, we will try to take appropiate actions. Reasons why the positions might not be there are: 1. The user / account owner closed the positions manually. @@ -422,6 +422,8 @@ */ + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> JSON.stringify(context.executionContext.positions) = " + JSON.stringify(context.executionContext.positions)); } + let openPositions = []; for (let a = 0; a < context.executionContext.positions.length; a++) { @@ -433,8 +435,19 @@ } } + /* + + This removes all orders leaving only the not executed position. Consider that we do want executed positions to be recored on the file so as to + have a record of their execution. But at next execution these records must be deleted bedore the new Execution Context file is created and + this is what we do here. + + */ + context.executionContext.positions = openPositions; + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> Removing executed positions."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> JSON.stringify(context.executionContext.positions) = " + JSON.stringify(context.executionContext.positions)); } + /* Now we can start checking what happened at the exchange. */ let i = 0; @@ -546,9 +559,9 @@ id: Math.trunc(Math.random(1) * 1000000), type: thisPosition.type, rate: thisPosition.rate.toString(), - amountA: Number(thisPosition.amountA.toFixed(8)).toString(), - amountB: Number(thisPosition.amountB.toFixed(8)).toString(), - fee: Number(feeRate.toFixed(8)).toString(), + amountA: Number(parseFloat(thisPosition.amountA).toFixed(8)).toString(), + amountB: Number(parseFloat(thisPosition.amountB).toFixed(8)).toString(), + fee: Number(parseFloat(feeRate).toFixed(8)).toString(), date: (new Date()).valueOf() } @@ -639,8 +652,8 @@ sumAssetB = sumAssetB + Number(trade.amountB); } - sumAssetA = Number(sumAssetA.toFixed(8)); - sumAssetB = Number(sumAssetB.toFixed(8)); + sumAssetA = Number(parseFloat(sumAssetA).toFixed(8)); + sumAssetB = Number(parseFloat(sumAssetB).toFixed(8)); if (position.amountB !== sumAssetB) { logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> position.amountB = " + position.amountB); @@ -714,10 +727,10 @@ sumAssetA = sumAssetA + Number(exchangePosition.amountA); sumAssetB = sumAssetB + Number(exchangePosition.amountB); - sumAssetA = Number(sumAssetA.toFixed(6)); // Fixed to 6 positions to avoid rounding issues - sumAssetB = Number(sumAssetB.toFixed(8)); + sumAssetA = Number(parseFloat(sumAssetA).toFixed(6)); // Fixed to 6 positions to avoid rounding issues + sumAssetB = Number(parseFloat(sumAssetB).toFixed(8)); - if (position.amountA.toFixed(6) !== sumAssetA || position.amountB !== sumAssetB ) { + if (parseFloat(position.amountA).toFixed(6) !== sumAssetA || position.amountB !== sumAssetB ) { logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountA = " + position.amountA); logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumAssetA = " + sumAssetA); logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountB = " + position.amountB); @@ -782,28 +795,28 @@ if (trade.type === 'buy') { let fee = Number(trade.fee) * trade.amountB; - let fixedFee = Number(fee.toFixed(8)); + let fixedFee = Number(parseFloat(fee).toFixed(8)); assetA = context.executionContext.balance.assetA - Number(trade.amountA); assetB = context.executionContext.balance.assetB + Number(trade.amountB) - fixedFee; let available = context.executionContext.availableBalance.assetB + Number(trade.amountB) - fixedFee; - context.executionContext.availableBalance.assetB = Number(available.toFixed(8)); + context.executionContext.availableBalance.assetB = Number(parseFloat(available).toFixed(8)); } if (trade.type === 'sell') { let fee = Number(trade.fee) * trade.amountA; - let fixedFee = Number(fee.toFixed(8)); + let fixedFee = Number(parseFloat(fee).toFixed(8)); assetA = context.executionContext.balance.assetA + Number(trade.amountA) - fixedFee; assetB = context.executionContext.balance.assetB - Number(trade.amountB); let available = context.executionContext.availableBalance.assetA + Number(trade.amountA) - fixedFee; - context.executionContext.availableBalance.assetA = Number(available.toFixed(8)); + context.executionContext.availableBalance.assetA = Number(parseFloat(available).toFixed(8)); } - context.executionContext.balance.assetA = Number(assetA.toFixed(8)); - context.executionContext.balance.assetB = Number(assetB.toFixed(8)); + context.executionContext.balance.assetA = Number(parseFloat(assetA).toFixed(8)); + context.executionContext.balance.assetB = Number(parseFloat(assetB).toFixed(8)); } } catch (err) { @@ -862,6 +875,10 @@ 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 -> pType = " + pType); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pRate = " + pRate); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + pAmountA); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + pAmountB); } /* Removing extra decimals. */ @@ -869,11 +886,6 @@ pAmountA = Number(parseFloat(pAmountA).toFixed(8)); pAmountB = Number(parseFloat(pAmountB).toFixed(8)); - 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 = " + pRate); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + pAmountA); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + pAmountB); } - /* Validations that the limits are not surpassed. */ if (pType === 'buy') { @@ -893,7 +905,7 @@ return; } - let aRate = Number((pAmountA / pRate).toFixed(8)); + let aRate = Number(parseFloat(pAmountA / pRate).toFixed(8)); if (aRate !== pAmountB) { @@ -929,7 +941,7 @@ return; } - let bRate = Number((pAmountB * pRate).toFixed(8)); + let bRate = Number(parseFloat(pAmountB * pRate).toFixed(8)); if (bRate !== pAmountA) { @@ -1026,14 +1038,14 @@ if (position.type === 'buy') { context.executionContext.availableBalance.assetA = context.executionContext.availableBalance.assetA - pAmountA; - context.executionContext.availableBalance.assetA = Number(context.executionContext.availableBalance.assetA.toFixed(8)); + context.executionContext.availableBalance.assetA = Number(parseFloat(context.executionContext.availableBalance.assetA).toFixed(8)); context.newHistoryRecord.lastBuyRate = pRate; } if (position.type === 'sell') { context.executionContext.availableBalance.assetB = context.executionContext.availableBalance.assetB - pAmountB; - context.executionContext.availableBalance.assetB = Number(context.executionContext.availableBalance.assetB.toFixed(8)); + context.executionContext.availableBalance.assetB = Number(parseFloat(context.executionContext.availableBalance.assetB).toFixed(8)); context.newHistoryRecord.lastSellRate = pRate; } @@ -1224,40 +1236,70 @@ } function getPositions() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositions -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositions -> JSON.stringify(context.executionContext.positions) = " + JSON.stringify(context.executionContext.positions)); } + return JSON.parse(JSON.stringify(context.executionContext.positions)); } function getBalance() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getBalance -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getBalance -> JSON.stringify(context.executionContext.balance) = " + JSON.stringify(context.executionContext.positions)); } + return JSON.parse(JSON.stringify(context.executionContext.balance)); } function getAvailableBalance() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getAvailableBalance -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getAvailableBalance -> JSON.stringify(context.executionContext.availableBalance) = " + JSON.stringify(context.executionContext.availableBalance)); } + return JSON.parse(JSON.stringify(context.executionContext.availableBalance)); } function getInvestment() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getInvestment -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getInvestment -> JSON.stringify(context.executionContext.investment) = " + JSON.stringify(context.executionContext.investment)); } + return JSON.parse(JSON.stringify(context.executionContext.investment)); } function getProfits() { + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getProfits -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getProfits -> JSON.stringify(context.executionContext.profits) = " + JSON.stringify(context.executionContext.profits)); } + return JSON.parse(JSON.stringify(context.executionContext.profits)); } function getCombinedProfits() { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getCombinedProfits -> Entering function."); } + let combinedProfits = { assetA: context.newHistoryRecord.combinedProfitsA, assetB: context.newHistoryRecord.combinedProfitsB } + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getCombinedProfits -> JSON.stringify(combinedProfits) = " + JSON.stringify(combinedProfits)); } + return JSON.parse(JSON.stringify(combinedProfits)); } function getROI() { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getROI -> Entering function."); } + let ROI = { assetA: (context.executionContext.balance.assetA - context.executionContext.investment.assetA) / context.executionContext.investment.assetA * 100, assetB: (context.executionContext.balance.assetB - context.executionContext.investment.assetB) / context.executionContext.investment.assetB * 100 } + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getROI -> JSON.stringify(ROI) = " + JSON.stringify(ROI)); } + return JSON.parse(JSON.stringify(ROI)); } @@ -1276,7 +1318,7 @@ context.newHistoryRecord.messageBody = pBody; if(pRelevance > 6){ - sendEmail(pTitle, pBody); + sendEmail(pTitle, pBody, false); } } @@ -1305,20 +1347,24 @@ } - function sendEmail(pTitle, pBody) { + function sendEmail(pTitle, pBody, pSendToAdmins) { try { + let emailList = global.EMAIL_CONFIG.distributionList; + if (pSendToAdmins) { + emailList = global.EMAIL_CONFIG.adminList; + } let transporter = nodemailer.createTransport({ - service: 'gmail', + service: global.EMAIL_CONFIG.service, auth: { - user: '', - pass: '' + user: global.EMAIL_CONFIG.user, + pass: global.EMAIL_CONFIG.pass } }); let mailOptions = { - from: '', - to: '', + from: global.EMAIL_CONFIG.from, + bcc: emailList, subject: bot.startMode + ' - ' + pTitle, text: pBody }; diff --git a/BlobStorage.js b/BlobStorage.js index da720ae37..f05bb4918 100644 --- a/BlobStorage.js +++ b/BlobStorage.js @@ -148,16 +148,16 @@ exports.newBlobStorage = function newBlobStorage(BOT, logger) { } if ( - err.code === 'ECONNRESET' || - err.code === 'ENOTFOUND' || - err.code === 'ESOCKETTIMEDOUT' || - err.code === 'ETIMEDOUT' || - err.code === 'ECONNREFUSED' || - err.code === 'EADDRINUSE' || + err.code === 'ECONNRESET' || + err.code === 'ENOTFOUND' || + err.code === 'ESOCKETTIMEDOUT' || + err.code === 'ETIMEDOUT' || + err.code === 'ECONNREFUSED' || + err.code === 'EADDRINUSE' || + err.code === 'EAI_AGAIN' || err.code === 'AuthenticationFailed' || - err.code === 'OperationTimedOut' || - err.code === 'ServerBusy' || - err.code === 'EAI_AGAIN' + err.code === 'OperationTimedOut' || + err.code === 'ServerBusy' ) { @@ -276,12 +276,12 @@ exports.newBlobStorage = function newBlobStorage(BOT, logger) { err.code === 'ETIMEDOUT' || err.code === 'ECONNREFUSED' || err.code === 'EADDRINUSE' || + err.code === 'EAI_AGAIN' || err.code === 'AuthenticationFailed' || err.code === 'OperationTimedOut' || - err.code === 'ServerBusy' || - err.code === 'EAI_AGAIN' + err.code === 'ServerBusy' ) - + { setTimeout(secondTry, SECOND_TRY_WAIT_TIME); From b225123d1f4f8564b83fe5e8b32ee74032cf7dca Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 10:51:43 -0300 Subject: [PATCH 06/14] Adding email config and notifications for errors --- Run.js | 20 +++++++++++++++++++- TradingBotProcessMainLoop.js | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Run.js b/Run.js index b33b6cb63..fbfb498a1 100644 --- a/Run.js +++ b/Run.js @@ -30,6 +30,7 @@ process.on('exit', function (code) { }); global.USER_PROFILE = {}; +global.EMAIL_CONFIG = {}; readStoragePermissions(); @@ -57,7 +58,7 @@ function readStoragePermissions() { global.USER_PROFILE.storagePermissions = permissionsMap; - readExchangeAPIKey(); + readEmailConfiguration(); } catch (err) { console.log("[ERROR] readStoragePermissions -> err = " + err.message); @@ -65,6 +66,23 @@ function readStoragePermissions() { } } +function readEmailConfiguration() { + + let filePath; + + try { + let fs = require('fs'); + filePath = '../' + 'Email-Config' + '/' + 'Email.Config.json'; + global.EMAIL_CONFIG = JSON.parse(fs.readFileSync(filePath, 'utf8')); + + readExchangeAPIKey(); + } + catch (err) { + console.log("[ERROR] readEmailConfiguration -> err = " + err.message); + console.log("[HINT] You need to have a file at this path -> " + filePath); + } +} + function readExchangeAPIKey() { try { diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index 768e97a9a..1e155f837 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -851,6 +851,7 @@ clearTimeout(nextLoopTimeoutHandle); clearTimeout(checkLoopHealthHandle); bot.enableCheckLoopHealth = false; + assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, true); callBackFunction(err); return; } @@ -863,6 +864,7 @@ clearTimeout(nextLoopTimeoutHandle); clearTimeout(checkLoopHealthHandle); bot.enableCheckLoopHealth = false; + assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, true); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return; } @@ -875,6 +877,7 @@ clearTimeout(nextLoopTimeoutHandle); clearTimeout(checkLoopHealthHandle); bot.enableCheckLoopHealth = false; + assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, true); callBackFunction(err); } } @@ -886,6 +889,7 @@ clearTimeout(nextLoopTimeoutHandle); clearTimeout(checkLoopHealthHandle); bot.enableCheckLoopHealth = false; + assistant.sendEmail("FAIL", "Operation Failed. Aborting the process. err = " + err.message, true); callBackFunction(err); } } From f516e3e1daeb2f248a011139e7017a9b4b8f66c1 Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 10:52:24 -0300 Subject: [PATCH 07/14] Adding fix for debuglog --- DebugLog.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DebugLog.js b/DebugLog.js index 2cc9376cb..70adf7be0 100644 --- a/DebugLog.js +++ b/DebugLog.js @@ -147,7 +147,11 @@ try { - /* console.log("AACloud" + spacePad(pModule, 50) + " : " + pMessage); */ + if (global.CURRENT_EXECUTION_AT === "IDE") { + + console.log("AACloud" + spacePad(pModule, 50) + " : " + pMessage); + + } if (disableLogging === true) { return; } From 08a48ec6b1f2fe3ec3ca59255c1afe42b8a55415 Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 11:24:58 -0300 Subject: [PATCH 08/14] Fixing movePosition new amount --- Assistant.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assistant.js b/Assistant.js index 70cb8acdc..5681e0447 100644 --- a/Assistant.js +++ b/Assistant.js @@ -1089,7 +1089,7 @@ let newAmountB; if (pPosition.type === "buy") { - newAmountB = Number((pPosition.amountA / pNewRate).toFixed(8)); + newAmountB = Number(parseFloat(pPosition.amountA / pNewRate).toFixed(8)); } else { newAmountB = pPosition.amountB; } From f71f4f59b577571ada72466a81ff62742abcfd2f Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 11:31:02 -0300 Subject: [PATCH 09/14] Merging Cloud Content --- Context.js | 29 ++++++++--------------------- Root.js | 2 +- TradingBotProcessMainLoop.js | 4 ++++ 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/Context.js b/Context.js index 08f829955..9fceb3106 100644 --- a/Context.js +++ b/Context.js @@ -6,30 +6,17 @@ /* - This module allows deals with keeping the context between different executions of the bot. - It deals with these 3 different files that helps the bot process remember where it is standing: + This module deals with keeping the context between different executions of the bot. Conceptually this context is divided in 3 main parts: - 1. The Status Report. - 2. The Execution History. - 3. The Execution Context. + I. The non-business information which helps the mechanisms of giving continuety to the bots execution over time. This information goes in a file + called the Status Report, and there is a module, child of this one called Status Report that deals with loading and saving the contents of that file. - */ + II. The business information around the bot execution, including its balance, positions at exchanges, trades executed and many other things. These + are platform data mantained on behalf of the executing bot. All these is kept in one file per execution called the Execution Context. The collection + of all these files can later reconstruct the vital business information the bot had at each moment in time. - - - /* - - Here we will keep the last status report, to be available during the whole process. The Status Report is a file the bot process - reads and saves again after each execution. Its main purpose is to know when the last execution was in order to locate the execution - context. When the bot runs for the first time it takes some vital parameters from there and it checks them through its lifecycle to see - if they changed. The Status Report file can eventually be manipulated by the bot operators / developers in order to change those parameters - or to point the last execution to a different date. Humans are not supose to manipulate the Execution Histroy or the execution Context files. - - The Execution History is basically an index with dates of all the executions the bot did across its history. It allows the bot plotter - to know which datetimes have informacion about the bots execution in order to display it. - - The Execution Context file records all the context information of the bot at the moment of execution and the final state of all of its - positions on the market. + II. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files + with all the business informatino the bot had each time it executed. We call this index the Execution History and there is one file to store it. */ diff --git a/Root.js b/Root.js index cf2dd5839..1fe0135d1 100644 --- a/Root.js +++ b/Root.js @@ -132,7 +132,7 @@ exports.newRoot = function newRoot() { logInfo: true, logWarnings: false, logErrors: true, - logContent: false, + logContent: true, intensiveLogging: false } }; diff --git a/TradingBotProcessMainLoop.js b/TradingBotProcessMainLoop.js index 1e155f837..4dd3897be 100644 --- a/TradingBotProcessMainLoop.js +++ b/TradingBotProcessMainLoop.js @@ -58,6 +58,10 @@ 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": { + filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo + "/" + pProcessConfig.name; // This works like Cloud except of a few things. + 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); From 7dd3053ddd61f33f55ade8e01b1c3b1920677422 Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 11:52:12 -0300 Subject: [PATCH 10/14] Setting log content of context to false --- Root.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Root.js b/Root.js index 1fe0135d1..cf2dd5839 100644 --- a/Root.js +++ b/Root.js @@ -132,7 +132,7 @@ exports.newRoot = function newRoot() { logInfo: true, logWarnings: false, logErrors: true, - logContent: true, + logContent: false, intensiveLogging: false } }; From 36608ba79ec9642910e67bdfd3f06d8d2a6b5972 Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 11:53:22 -0300 Subject: [PATCH 11/14] Fixing Typo --- Context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Context.js b/Context.js index 9fceb3106..5eabb049d 100644 --- a/Context.js +++ b/Context.js @@ -15,7 +15,7 @@ are platform data mantained on behalf of the executing bot. All these is kept in one file per execution called the Execution Context. The collection of all these files can later reconstruct the vital business information the bot had at each moment in time. - II. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files + III. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files with all the business informatino the bot had each time it executed. We call this index the Execution History and there is one file to store it. */ From f50470d3b257233bd76c5e4c3cfd27c8a8beb6ac Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 11:54:38 -0300 Subject: [PATCH 12/14] roll back --- Context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Context.js b/Context.js index 5eabb049d..9fceb3106 100644 --- a/Context.js +++ b/Context.js @@ -15,7 +15,7 @@ are platform data mantained on behalf of the executing bot. All these is kept in one file per execution called the Execution Context. The collection of all these files can later reconstruct the vital business information the bot had at each moment in time. - III. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files + II. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files with all the business informatino the bot had each time it executed. We call this index the Execution History and there is one file to store it. */ From 13d50773bab1728a70063fdaeae5fdd84fcf1110 Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 11:55:47 -0300 Subject: [PATCH 13/14] git merge --- Context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Context.js b/Context.js index 9fceb3106..5eabb049d 100644 --- a/Context.js +++ b/Context.js @@ -15,7 +15,7 @@ are platform data mantained on behalf of the executing bot. All these is kept in one file per execution called the Execution Context. The collection of all these files can later reconstruct the vital business information the bot had at each moment in time. - II. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files + III. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files with all the business informatino the bot had each time it executed. We call this index the Execution History and there is one file to store it. */ From c53a3f05670c619a97386ffeb2df9d1aacf94a72 Mon Sep 17 00:00:00 2001 From: Matias Benitez Date: Fri, 6 Jul 2018 11:56:34 -0300 Subject: [PATCH 14/14] causes a bad merge --- Context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Context.js b/Context.js index 5eabb049d..9fceb3106 100644 --- a/Context.js +++ b/Context.js @@ -15,7 +15,7 @@ are platform data mantained on behalf of the executing bot. All these is kept in one file per execution called the Execution Context. The collection of all these files can later reconstruct the vital business information the bot had at each moment in time. - III. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files + II. Execution of any bot cannot be guaranteed that will be on fixed interval of times. We need an index of all executions that points to the detailed files with all the business informatino the bot had each time it executed. We call this index the Execution History and there is one file to store it. */