diff --git a/Assistant.js b/Assistant.js deleted file mode 100644 index 9a98b1cbd..000000000 --- a/Assistant.js +++ /dev/null @@ -1,1127 +0,0 @@ -exports.newAssistant = function newAssistant(BOT, logger, UTILITIES) { - - /* - This module allows trading bots to execute actions on the exchange. - */ - - const MODULE_NAME = "Assistant"; - - let bot = BOT; - - let thisObject = { - dataDependencies: undefined, - initialize: initialize, - createOrder: createOrder, - getPositions: getPositions, - getBalance: getBalance, - getAvailableBalance: getAvailableBalance, - getInitialBalance: getInitialBalance, - getProfits: getProfits, - getCombinedProfits: getCombinedProfits, - getROI: getROI, - getMarketRate: getMarketRate, - getTicker: getTicker, - sendMessage: sendMessage, - rememberThis: rememberThis, - remindMeOf: remindMeOf, - truncDecimals: truncDecimals, - sendEmail: sendEmail, - addExtraData: addExtraData - }; - - let utilities = UTILITIES.newCloudUtilities(logger); - - let openOrders = []; // 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 context; - let exchangeAPI; - - let marketRate; - let ticker; - - return thisObject; - - function initialize(pContext, pExchangeAPI, pDataDependencies, callBackFunction) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - - /* Save local values. */ - - context = pContext; - exchangeAPI = pExchangeAPI; - thisObject.dataDependencies = pDataDependencies; - - switch (bot.startMode) { - - case 'Live': { - getMarketRateFromExchange(); - break; - } - - case 'Backtest': { - validateExchangeSyncronicity(); - break; - } - - default: { - logger.write(MODULE_NAME, "[ERROR] initialize -> Unexpected bot.startMode."); - logger.write(MODULE_NAME, "[ERROR] initialize -> bot.startMode = " + bot.startMode); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function getMarketRateFromExchange() { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> Entering function."); } - - exchangeAPI.getTicker(bot.market, onTicker); - - return; - - function onTicker(err, pTicker) { - - try { - - 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 (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromExchange -> onTicker -> We could not get the Market Price now."); } - - callBackFunction(global.DEFAULT_RETRY_RESPONSE); - return; - } - - ticker = pTicker; - marketRate = pTicker.last; - - context.newHistoryRecord.marketRate = marketRate; - - validateExchangeSyncronicity(); - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromExchange -> onTicker -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromExchange -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function validateExchangeSyncronicity() { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> validateExchangeSyncronicity -> Entering function."); } - - /* Procedure to validate we are in sync with the exchange. */ - - getPositionsAtExchange(onDone); - - function onDone(err) { - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> validateExchangeSyncronicity -> onDone -> Entering function."); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> validateExchangeSyncronicity -> onDone -> Execution finished well."); } - calculateProfits(); - return; - } - break; - 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] initialize -> validateExchangeSyncronicity -> onDone -> Retry Later. Requesting Execution Retry."); - callBackFunction(err); - return; - } - break; - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] initialize -> validateExchangeSyncronicity -> onDone -> Operation Failed. Aborting the process."); - callBackFunction(err); - return; - } - break; - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> validateExchangeSyncronicity -> onDone -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function calculateProfits() { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> calculateProfits -> Entering function."); } - - /* Calculate Profits */ - - if (context.executionContext.initialBalance.baseAsset > 0) { - - context.executionContext.profits.baseAsset = (thisObject.truncDecimals(context.executionContext.balance.baseAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset); - context.executionContext.profits.baseAsset = thisObject.truncDecimals(context.executionContext.profits.baseAsset); - } - - if (context.executionContext.initialBalance.quotedAsset > 0) { - - context.executionContext.profits.quotedAsset = (thisObject.truncDecimals(context.executionContext.balance.quotedAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset); - context.executionContext.profits.quotedAsset = thisObject.truncDecimals(context.executionContext.profits.quotedAsset); - } - - context.newHistoryRecord.profitsBaseAsset = context.executionContext.profits.baseAsset; - context.newHistoryRecord.profitsQuotedAsset = context.executionContext.profits.quotedAsset; - - /* Calculate Combined Profits */ - - if (context.executionContext.initialBalance.baseAsset > 0) { - - let convertedAssetsB = (thisObject.truncDecimals(context.executionContext.balance.quotedAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset)) * marketRate; - convertedAssetsB = thisObject.truncDecimals(convertedAssetsB); - - context.executionContext.combinedProfits.baseAsset = (thisObject.truncDecimals(context.executionContext.balance.baseAsset) + convertedAssetsB - thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset) * 100; - context.executionContext.combinedProfits.baseAsset = thisObject.truncDecimals(context.executionContext.combinedProfits.baseAsset); - } - - if (context.executionContext.initialBalance.quotedAsset > 0) { - - let convertedAssetsA = (thisObject.truncDecimals(context.executionContext.balance.baseAsset) - thisObject.truncDecimals(context.executionContext.initialBalance.baseAsset)) / marketRate; - convertedAssetsA = thisObject.truncDecimals(convertedAssetsA); - - context.executionContext.combinedProfits.quotedAsset = (thisObject.truncDecimals(context.executionContext.balance.quotedAsset) + convertedAssetsA - thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset)) / thisObject.truncDecimals(context.executionContext.initialBalance.quotedAsset) * 100; - context.executionContext.combinedProfits.quotedAsset = thisObject.truncDecimals(context.executionContext.combinedProfits.quotedAsset); - } - - context.newHistoryRecord.combinedProfitsA = context.executionContext.combinedProfits.baseAsset; - context.newHistoryRecord.combinedProfitsB = context.executionContext.combinedProfits.quotedAsset; - - callBackFunction(global.DEFAULT_OK_RESPONSE); - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> calculateProfits -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getPositionsAtExchange(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Entering function."); } - - /* - - Here we grab all the positions at the exchange for the account we are using for trading. We will not asume all the positions - were made by this bot, but we need them all to later check if all the ones made by the bot are still there, were executed or - manually cancelled by the exchange account owner. - - */ - - switch (bot.startMode) { - - case "Live": { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Live Mode Detected."); } - exchangeAPI.getOpenOrders(bot.market, onResponse); - break; - } - - case "Backtest": { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Backtest Mode Detected."); } - let openOrders = []; // We simulate all positions were executed. - onResponse(global.DEFAULT_OK_RESPONSE, openOrders); - break; - } - - default: { - logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> Unexpected bot.startMode."); - logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> bot.startMode = " + bot.startMode); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function onResponse(err, pOpenOrders) { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> pOpenOrders = " + JSON.stringify(pOpenOrders)); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> Execution finished well."); } - openOrders = pOpenOrders; - ordersExecutionCheck(callBack); - return; - } - break; - 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] getPositionsAtExchange -> onResponse -> Retry Later. Requesting Execution Retry."); - callBack(err); - return; - } - break; - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> onResponse -> Operation Failed. Aborting the process."); - callBack(err); - return; - } - break; - } - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] getPositionsAtExchange -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function ordersExecutionCheck(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> Entering function."); } - - /* - - Here we check that all the positions we know are still valid 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. - 2. The exchange for some reason closed the positions. In some exchanges positions have an expiration time. - 3. The orders were executed. - - Situations 1 and 2 are similar unexpected and we will stop the bot execution when we detect them. Number 3 is an - expected behaviour and we will take appropiate action. - - Also, the position might still be there but it might have been partially executed. To detect that we need to compare the - position amounts we have on file to the one we are receiving from the exchange. - - Before we begin, we have to remove all the orders that have been already closed at the last execution of the bot. - - */ - - 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++) { - - if (context.executionContext.positions[a].status === "open") { - - openPositions.push(context.executionContext.positions[a]); - - } - } - - /* - - 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 before 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; - - controlLoop(); - - function loopBody() { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> Entering function."); } - - let position = context.executionContext.positions[i]; - let exchangePosition; - - for (let j = 0; j < openOrders.length; j++) { - - if (position.id === openOrders[j].id) { - - exchangePosition = openOrders[j]; - positionFound(); - return; - } - } - - positionNotFound(); - return; - - function positionFound() { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> positionFound -> Entering function."); } - - /* - We update the final amountA based on what we got from the exchange. - This is needed since the values could be different from what we calculate. - */ - position.amountA = exchangePosition.amountA; - - /* - We need to know if the order was partially executed. To know that, we compare the amounts on file with the ones - received from the exchange. If they are the same, then the order is intact. Otherwise, to confirm a partial execution, - we will request the associated trades from the exchange. - */ - - if (position.amountB === exchangePosition.amountB) { - - /* Position is still there, untouched. Nothing to do here. */ - - next(); - return; - - } else { - - getPositionTradesAtExchange(position.id, confirmOrderWasPartiallyExecuted); - return; - - } - } - - function positionNotFound() { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> positionNotFound -> Entering function."); } - - /* Position not found: we need to know if the order was executed. */ - - getPositionTradesAtExchange(position.id, confirmOrderWasExecuted); - - } - - function getPositionTradesAtExchange(pOrderId, innerCallBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> pOrderId = " + pOrderId); } - - /* - - Given one position, we request all the associated trades to it. - - */ - - switch (bot.startMode) { - - case "Live": { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Live Mode Detected."); } - exchangeAPI.getOrder(pOrderId, bot.market, onResponse); - return; - - } - - case "Backtest": { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Backtest Mode Detected."); } - - let trades = []; - - /* We look for the position at the executionContext */ - - for (let i = 0; i < context.executionContext.positions.length; i++) { - - let thisOrder = context.executionContext.positions[i]; - - if (thisOrder.id === pOrderId) { - - let feeRate = 0.002; // Default backtesting fee simulation - - let trade = { - id: Math.trunc(Math.random(1) * 1000000), - type: thisOrder.type, - rate: thisOrder.rate.toString(), - amountA: thisObject.truncDecimals(thisOrder.amountA).toString(), - amountB: thisObject.truncDecimals(thisOrder.amountB).toString(), - fee: thisObject.truncDecimals(feeRate).toString(), - date: (new Date()).valueOf() - } - - trades.push(trade); - - let order = { - trades: trades - } - onResponse(global.DEFAULT_OK_RESPONSE, order); - - return; - } - } - - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Position not found at Executioin Context."); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - - default: { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Unexpected bot.startMode."); - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> bot.startMode = " + bot.startMode); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function onResponse(err, order) { - - try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Entering function."); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Execution finished well."); } - innerCallBack(order); - } - break; - 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] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Retry Later. Requesting Execution Retry."); - callBack(global.DEFAULT_RETRY_RESPONSE); - return; - } - break; - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Operation Failed. Aborting the process."); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - break; - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function confirmOrderWasExecuted(order) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> trades = " + JSON.stringify(trades)); } - - let trades = order.trades - /* - - 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 sumBaseAsset = 0; - let sumQuotedAsset = 0; - let sumRate = 0; - - for (let k = 0; k < trades.length; k++) { - let trade = trades[k]; - sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(trade.amountA); - sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(trade.amountB); - sumRate = sumRate + thisObject.truncDecimals(trade.rate); - } - - sumBaseAsset = thisObject.truncDecimals(sumBaseAsset); - sumQuotedAsset = thisObject.truncDecimals(sumQuotedAsset); - - if (position.amountB !== sumQuotedAsset) { - logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> position.amountB = " + position.amountB); - logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> sumQuotedAsset = " + sumQuotedAsset); - - logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Cannot be confirmed that the order was executed. It must be manually cancelled by the user or cancelled by the exchange itself."); - logger.write(MODULE_NAME, "[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> If the process was abruptally cancelled and then restarted, it is possible that now is not sincronized with the exchange."); - logger.write(MODULE_NAME, "[HINT] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> In any case, to continue, you must manually delete the orders at the exchange."); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - - /* - - Confirmed that order was executed. Next thing to do is to remember the trades and change its status. - - */ - - position.status = "executed"; - - if (position.type === "sell") { - context.newHistoryRecord.sellExecRate = sumRate / trades.length; - } else { - context.newHistoryRecord.buyExecRate = sumRate / trades.length; - } - - applyTradesToContext(trades); - - let newTransaction = { - type: position.type + " executed", - position: position - }; - - context.executionContext.transactions.push(newTransaction); - - /* All done. */ - - next(); - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function confirmOrderWasPartiallyExecuted(trades) { - - try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Entering function."); } - - let trades = order.trades - - /* - - 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 sumBaseAsset = 0; - let sumQuotedAsset = 0; - - for (let k = 0; k < trades.length; k++) { - let trade = trades[k]; - sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(trade.amountA); - sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(trade.amountB); - } - - /* To this we add the current position amounts. */ - - sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(exchangePosition.amountA); - sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(exchangePosition.amountB); - - sumBaseAsset = thisObject.truncDecimals(sumBaseAsset); - sumQuotedAsset = thisObject.truncDecimals(sumQuotedAsset); - - /* - * Next let's get the max decimal positions for the pair being trade returned by the exchange and validate that at least - * one decimal position less than the max matches. The reason for this is because there could be some - * rounding on the exchange when there are partial executed orders that will interfiere with the exact validation. - * - * IE: For Poloniex USDT/BTC the max returned decimal positions is 8, it means we will validate that the diference is less than 0.0000001 - */ - - let exchangeParam = exchangeAPI.getMaxDecimalPositions(); - let minValue = '0.' + (1).toPrecision(exchangeParam - 1).split('.').reverse().join(''); - let exchangePrecision = parseFloat(parseFloat(minValue).toFixed(exchangeParam)); - - if (Math.abs(position.amountA - sumBaseAsset) > exchangePrecision || Math.abs(position.amountB - sumQuotedAsset) > exchangePrecision) { - - logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountA = " + position.amountA); - logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumBaseAsset = " + sumBaseAsset); - logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> position.amountB = " + position.amountB); - logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> sumQuotedAsset = " + sumQuotedAsset); - - 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; - position.amountB = exchangePosition.amountB; - position.date = (new Date(exchangePosition.date)).valueOf(); - - applyTradesToContext(trades); - - let newTransaction = { - type: position.type + " partially executed", - position: position - }; - - context.executionContext.transactions.push(newTransaction); - - /* All done. */ - - next(); - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function applyTradesToContext(trades) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> applyTradesToContext -> Entering function."); } - - /* Here we apply the trades that already happened at the exchange to the balance and available balance of the bot. We also calculate its profits. */ - - for (k = 0; k < trades.length; k++) { - - let trade = trades[k]; - - position.trades.push(trade); - - context.newHistoryRecord.newTrades++; - - /* Calculate Balances */ - - let baseAsset = 0; - let quotedAsset = 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. - - */ - - let feeAmount = thisObject.truncDecimals(Number(trade.fee) * Number(trade.amountB)); - - baseAsset = Number(trade.amountA); - quotedAsset = Number(trade.amountB) - feeAmount; - - context.executionContext.balance.baseAsset = thisObject.truncDecimals(context.executionContext.balance.baseAsset - baseAsset); - context.executionContext.balance.quotedAsset = thisObject.truncDecimals(context.executionContext.balance.quotedAsset + quotedAsset); - - context.executionContext.availableBalance.quotedAsset = thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset + quotedAsset); - - /* Not the available balance for asset A is not affected since it was already reduced when the order was placed. */ - - } - - 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. - - */ - - let feeAmount = thisObject.truncDecimals(Number(trade.fee) * Number(trade.amountA)); - - baseAsset = Number(trade.amountA) - feeAmount; - quotedAsset = Number(trade.amountB); - - context.executionContext.balance.baseAsset = thisObject.truncDecimals(context.executionContext.balance.baseAsset + baseAsset); - context.executionContext.balance.quotedAsset = thisObject.truncDecimals(context.executionContext.balance.quotedAsset - quotedAsset); - - context.executionContext.availableBalance.baseAsset = thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset + baseAsset); - - /* Not the available balance for asset B is not affected since it was already reduced when the order was placed. */ - - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> applyTradesToContext -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function next() { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> next -> Entering function."); } - - i++; - controlLoop(); - - } - - function controlLoop() { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> controlLoop -> Entering function."); } - - if (i < context.executionContext.positions.length) { - - loopBody(); - - } else { - - final(); - - } - } - - function final() { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> final -> Entering function."); } - - callBack(global.DEFAULT_OK_RESPONSE); - - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> err = " + err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function createOrder(pType, pRate, pAmountA, pAmountB, callBackFunction) { - - try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> Entering function."); } - - /* Removing extra decimals. */ - - pRate = thisObject.truncDecimals(pRate); - pAmountA = thisObject.truncDecimals(pAmountA); - pAmountB = thisObject.truncDecimals(pAmountB); - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pType = " + pType); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pRate = " + pRate); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pAmountA = " + pAmountA); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pAmountB = " + pAmountB); } - - /* Validations that the limits are not surpassed. */ - - if (pType === 'buy') { - - if (pAmountA > thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset)) { - - logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountA is grater than the Available Balance."); - logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountA = " + pAmountA); - logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset)); - - let err = { - result: global.DEFAULT_FAIL_RESPONSE.result, - message: 'Not Enough Available Balance to Buy.' - }; - - callBackFunction(err); - return; - } - } - - if (pType === 'sell') { - - if (pAmountB > thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset)) { - - logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountB is grater than the Available Balance."); - logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountB = " + pAmountB); - logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset)); - - let err = { - result: global.DEFAULT_FAIL_RESPONSE.result, - message: 'Not Enough Available Balance to Sell.' - }; - - callBackFunction(err); - return; - } - } - - /* All validations passed, we proceed. */ - - switch (bot.startMode) { - - case "Live": { - - exchangeAPI.createOrder(bot.market, pType, pRate, pAmountA, pAmountB, onResponse); - return; - } - - case "Backtest": { - - if (pRate !== marketRate) { - - logger.write(MODULE_NAME, "[WARNING] createOrder -> Input Validations -> createOrder Rate can is different to marketRate while in Backtesting Mode. "); - //onResponse(global.DEFAULT_FAIL_RESPONSE, positionId); - } - - let positionId = Math.trunc(Math.random(1) * 1000000); - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> Simulating Exchange Response -> orderId = " + positionId); } - onResponse(global.DEFAULT_OK_RESPONSE, positionId); - return; - } - - default: { - logger.write(MODULE_NAME, "[ERROR] createOrder -> Unexpected bot.startMode."); - logger.write(MODULE_NAME, "[ERROR] createOrder -> bot.startMode = " + bot.startMode); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function onResponse(err, order) { - - try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> order.id = " + order.id); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> Execution finished well."); } - - let position = { - id: order.id, - type: pType, - rate: pRate, - amountA: pAmountA, - amountB: pAmountB, - date: (bot.processDatetime.valueOf()), - status: "open", - trades: [] - }; - - context.executionContext.positions.push(position); - - let newTransaction = { - type: "newPosition", - position: position - }; - - context.executionContext.transactions.push(newTransaction); - - context.newHistoryRecord.newPositions++; - - recalculateRateAverages(); - - /* Recalculate Available Balances */ - - if (position.type === 'buy') { - - context.executionContext.availableBalance.baseAsset = thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset - pAmountA); - context.newHistoryRecord.lastBuyRate = pRate; - } - - if (position.type === 'sell') { - - context.executionContext.availableBalance.quotedAsset = thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset - pAmountB); - context.newHistoryRecord.lastSellRate = pRate; - } - - callBackFunction(global.DEFAULT_OK_RESPONSE, position); - return; - } - break; - 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] createOrder -> onResponse -> Retry Later. Requesting Execution Retry."); - callBackFunction(global.DEFAULT_RETRY_RESPONSE); - return; - } - break; - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> Operation Failed. Aborting the process."); - callBackFunction(err); - return; - } - break; - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] createOrder -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getPublicTradeHistory(baseAsset, quotedAsset, startTime, endTime, callback) { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getTradeHistory -> Entering function."); } - - exchangeAPI.getPublicTradeHistory(baseAsset, quotedAsset, startTime, endTime, callback); - } - - function recalculateRateAverages() { - - /* This function calculates the weighted rates of buy and sell positions. This is a way to summarize with only one rate, where the positions are. */ - - let sumBuyWeightedRates = 0; - let sumSellWeightedRates = 0; - - let sumBuyWeights = 0; - let sumSellWeights = 0; - - for (let i = 0; i < context.executionContext.positions.length; i++) { - - let position = context.executionContext.positions[i]; - - if (position.type === 'buy') { - - sumBuyWeightedRates = sumBuyWeightedRates + position.rate * position.amountB - sumBuyWeights = sumBuyWeights + position.amountB; - - } - - if (position.type === 'sell') { - - sumSellWeightedRates = sumSellWeightedRates + position.rate * position.amountB - sumSellWeights = sumSellWeights + position.amountB; - - } - - } - - context.newHistoryRecord.buyAvgRate = (sumBuyWeightedRates / sumBuyWeights || 0); - context.newHistoryRecord.sellAvgRate = (sumSellWeightedRates / sumSellWeights || 0); - } - - 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.balance)); } - - 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 getInitialBalance() { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getInitialBalance -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getInitialBalance -> JSON.stringify(context.executionContext.initialBalance) = " + JSON.stringify(context.executionContext.initialBalance)); } - - return JSON.parse(JSON.stringify(context.executionContext.initialBalance)); - } - - 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 = { - baseAsset: context.newHistoryRecord.combinedProfitsA, - quotedAsset: 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 = { - baseAsset: (context.executionContext.balance.baseAsset - context.executionContext.initialBalance.baseAsset) / context.executionContext.initialBalance.baseAsset * 100, - quotedAsset: (context.executionContext.balance.quotedAsset - context.executionContext.initialBalance.quotedAsset) / context.executionContext.initialBalance.quotedAsset * 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)); - } - - function getMarketRate() { - return marketRate; - } - - function getTicker() { - return ticker; - } - - function sendMessage(pRelevance, pTitle, pBody) { - let runIndex = context.statusReport.runs.length - 1; - - context.newHistoryRecord.messageRelevance = pRelevance; - context.newHistoryRecord.messageTitle = bot.startMode + "." + runIndex + ": " + pTitle; - context.newHistoryRecord.messageBody = pBody; - } - - function rememberThis(pKey, pValue) { - if (context.executionContext.remember === undefined) { - context.executionContext.remember = {}; - } - context.executionContext.remember[pKey] = pValue; - } - - function remindMeOf(pKey) { - if (context.executionContext.remember === undefined) { - context.executionContext.remember = {}; - } - return context.executionContext.remember[pKey]; - } - - function sendEmail(pTitle, pBody, pTo) { - logger.write(MODULE_NAME, "[WARN] sendEmail -> Send emails is currently disabled."); - return; - } - - function addExtraData(pExtraDataArray) { - context.extraData.push(pExtraDataArray); - } - - function truncDecimals(pFloatValue, pDecimals) { - if (!pDecimals) pDecimals = 8; // Default value - return parseFloat(parseFloat(pFloatValue).toFixed(pDecimals)); - } -}; diff --git a/Context.js b/Context.js deleted file mode 100644 index bc347977b..000000000 --- a/Context.js +++ /dev/null @@ -1,551 +0,0 @@ -exports.newContext = function newContext(BOT, logger, UTILITIES) { - - let bot = BOT; - - const MODULE_NAME = "Context"; - - /* - - This module deals with keeping the context between different executions of the bot. Conceptually this context is divided in 3 main parts: - - 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. - - 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. - - */ - - let thisObject = { - statusReport: undefined, // Here is the information that defines which was the last sucessfull execution and some other details. - executionHistory: [], // This is the record of bot execution. - executionContext: undefined, // Here is the business information of the last execution of this bot process. - newHistoryRecord : { - date: undefined, - buyAvgRate: 0, - sellAvgRate: 0, - lastSellRate: 0, - sellExecRate: 0, - lastBuyRate: 0, - buyExecRate: 0, - marketRate: 0, - newPositions: 0, - newTrades: 0, - movedPositions: 0, - profitsBaseAsset: 0, - profitsQuotedAsset: 0, - combinedProfitsA: 0, - combinedProfitsB: 0, - messageRelevance: 0, - messageTitle: "", - messageBody: "" - }, - initialize: initialize, - saveThemAll: saveThemAll, - extraData: [] - }; - - /* - - During the process we will create a new History Record. This will go to the Context History file which essentially mantains an - index of all the bots executions. This file will later be plotted by the bot s plotter on the timeline, allowing end users to - know where there is information related to the actions taken by the bot. - - */ - - let statusReportModule; - - /* Utilities needed. */ - - let utilities = UTILITIES.newCloudUtilities(logger); - - /* Storage account to be used here. */ - - const FILE_STORAGE = require('./FileStorage.js'); - let fileStorage = FILE_STORAGE.newFileStorage(logger); - - let statusDependencies; - - let runIndex; // This is the index for this run and depends on the startMode. - let sessionPath = '' - - return thisObject; - - function initialize(pStatusDependencies, callBackFunction) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - - statusDependencies = pStatusDependencies; - - if (bot.SESSION !== undefined) { - sessionPath = bot.SESSION.folderName + "/" - } - - /* - Here we get the positions the bot did and that are recorded at the bot storage account. We will use them through out the rest - of the process. - */ - thisObject.newHistoryRecord.date = bot.processDatetime; - - getStatusReport(onDone); - - function onDone(err) { - try { - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> onDone -> Execution finished well."); } - - callBackFunction(global.DEFAULT_OK_RESPONSE); - return; - } - case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> Retry Later. Requesting Execution Retry."); - callBackFunction(err); - return; - } - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> Operation Failed. Aborting the process."); - callBackFunction(err); - return; - } - default: { - logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> Operation Failed with custom response."); - logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = " + JSON.stringify(err)); - callBackFunction(err); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = "+ err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getStatusReport(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getStatusReport -> Entering function."); } - - let key = bot.dataMine + "-" + bot.codeName + "-" + "Context" + "-" + bot.dataSetVersion; - - statusReportModule = statusDependencies.statusReports.get(key); - thisObject.statusReport = statusReportModule.file; - - /* The context is created in 2 situations: 1) when at the bot config is set not to continue using the same context after a new execution. 2) when the status report does not exist. */ - - if (bot.hasTheBotJustStarted === true || thisObject.statusReport.runs === undefined) { - - createConext(callBack); - - } else { - - runIndex = thisObject.statusReport.runs.length - 1; - - getExecutionHistory(callBack); - - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getStatusReport -> err = "+ err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getExecutionHistory(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionHistory -> Entering function."); } - - let fileName = "Execution.History." + bot.startMode + "." + runIndex + ".json"; - let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + fileName; - - fileStorage.getTextFile(filePath, onFileReceived); - - function onFileReceived(err, text) { - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> onFileReceived -> err = "+ JSON.stringify(err)); - callBack(err); - return; - } - - if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { - logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionHistory -> onFileReceived -> Content received = " + text); - } - - try { - - thisObject.executionHistory = JSON.parse(text); - - /* Move some values to the new record, in case there are no transactions that re-calculate them. */ - - thisObject.newHistoryRecord.buyAvgRate = thisObject.executionHistory[thisObject.executionHistory.length-1][1]; - thisObject.newHistoryRecord.sellAvgRate = thisObject.executionHistory[thisObject.executionHistory.length - 1][2]; - - getExecutionContext(callBack); - - } catch (err) { - - /* - - It might happen that the file content is corrupt or it does not exist. The bot can not run without an Execution Hitory, - since it is risky to ignore its own history unless it is its first execution in this mode ever. - - */ - - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> onFileReceived -> Bot cannot execute without the Execution History. -> Err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionHistory -> err = "+ err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function getExecutionContext(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionContext -> Entering function."); } - - let date; - - date = new Date(thisObject.statusReport.runs[runIndex].lastExecution); - thisObject.statusReport.runs[runIndex].lastExecution = bot.processDatetime; - thisObject.statusReport.runs[runIndex].endDatetime = bot.processDatetime; - - let fileName = "/Execution.Context." + bot.startMode + "." + runIndex + ".json"; - let dateForPath = date.getUTCFullYear() + '/' + utilities.pad(date.getUTCMonth() + 1, 2) + '/' + utilities.pad(date.getUTCDate(), 2) + '/' + utilities.pad(date.getUTCHours(), 2) + '/' + utilities.pad(date.getUTCMinutes(), 2); - let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + dateForPath + fileName; - - fileStorage.getTextFile(filePath, onFileReceived); - - function onFileReceived(err, text) { - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> onFileReceived -> err = "+ err.stack); - callBack(err); - return; - } - - if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { - logger.write(MODULE_NAME, "[INFO] initialize -> getExecutionContext -> onFileReceived -> Content received = " + text); - } - - try { - - thisObject.executionContext = JSON.parse(text); - thisObject.executionContext.transactions = []; // We record here the transactions that happened duting this execution. - - /* Update the new History Record, in case there are no trades during this execution that updates it. */ - - thisObject.newHistoryRecord.profitsBaseAsset = thisObject.executionContext.profits.baseAsset; - thisObject.newHistoryRecord.profitsQuotedAsset = thisObject.executionContext.profits.quotedAsset; - - thisObject.newHistoryRecord.combinedProfitsA = thisObject.executionContext.combinedProfits.baseAsset; - thisObject.newHistoryRecord.combinedProfitsB = thisObject.executionContext.combinedProfits.quotedAsset; - - callBack(global.DEFAULT_OK_RESPONSE); - - } catch (err) { - - /* - - It might happen that the file content is corrupt or it does not exist. The bot can not run without a Status Report, - since it is risky to ignore its own execution history. Except when the bot is running for the fist time in this mode, - an execution context file with the right format is needed. - - */ - - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> onFileReceived -> Response from storage. -> Text = " + text); - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> onFileReceived -> Bot cannot execute without the Execution Context. -> Err = " + err.message); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getExecutionContext -> err = "+ err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - function createConext(callBack) { - - try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> createConext -> Entering function."); } - /* - - When the bot is executed for the very first time, there are a few files that do not exist and need to be created, and that - content is what we are going to set up now. - - */ - - if (thisObject.statusReport.runs === undefined) { // This means that the Status Report does not exist. - - thisObject.statusReport.runs = []; - } - - let runContent = { - beginDatetime: bot.processDatetime, - endDatetime: bot.processDatetime, - lastExecution: bot.processDatetime - }; - - thisObject.statusReport.runs.push(runContent); - runIndex = thisObject.statusReport.runs.length - 1; - - thisObject.executionHistory = []; - - if (bot.VALUES_TO_USE.initialBalanceA === undefined || bot.VALUES_TO_USE.initialBalanceB === undefined) { throw new Error("Global Variables INITIAL_BALANCE_A and INITIAL_BALANCE_B cannot be undefined.") } - - const INITIAL_BALANCE_A = Number(bot.VALUES_TO_USE.initialBalanceB); // NOTE THAT THIS IS INVERTED BECAUSE OF POLONIEX THAT IS THE ONLY EXCHANGE SUPPORTED RIGHT NOW - const INITIAL_BALANCE_B = Number(bot.VALUES_TO_USE.initialBalanceA); - - - thisObject.executionContext = { - initialBalance: { // This is used to calculate profits. - baseAsset: INITIAL_BALANCE_A, - quotedAsset: INITIAL_BALANCE_B - }, - balance: { // This is the total balance that includes positions at the order book + funds available to be traded. - baseAsset: INITIAL_BALANCE_A, - quotedAsset: INITIAL_BALANCE_B // It starts with the initial initialBalance. - }, - availableBalance: { // This is the balance the bot has at any moment in time available to be traded (not in positions at the order book). - baseAsset: INITIAL_BALANCE_A, - quotedAsset: INITIAL_BALANCE_B // It starts with the initial initialBalance. - }, - profits: { - baseAsset: 0, - quotedAsset: 0 - }, - combinedProfits: { - baseAsset: 0, - quotedAsset: 0 - }, - positions: [], - transactions: [] - }; - - callBack(global.DEFAULT_OK_RESPONSE); - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> createConext -> err = "+ err.stack); - callBack(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> err = "+ err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function saveThemAll(callBackFunction) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> Entering function."); } - - writeExecutionContext(onDone); - - function onDone(err) { - try { - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> onDone -> Execution finished well."); } - - bot.hasTheBotJustStarted = false; - - callBackFunction(global.DEFAULT_OK_RESPONSE); - return; - } - break; - 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] saveThemAll -> onDone -> Retry Later. Requesting Execution Retry."); - callBackFunction(err); - return; - } - break; - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> onDone -> Operation Failed. Aborting the process."); - callBackFunction(err); - return; - } - break; - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> onDone -> err = "+ err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeExecutionContext(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExecutionContext -> Entering function."); } - - let fileName = "/Execution.Context." + bot.startMode + "." + runIndex +".json"; - let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCHours(), 2) + '/' + utilities.pad(bot.processDatetime.getUTCMinutes(), 2); - let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + dateForPath + fileName; - let fileContent = JSON.stringify(thisObject.executionContext); - - if(fileContent === undefined){ - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExecutionContext -> executionContext is undefined."); - callBackFunction(global.DEFAULT_ERROR_RESPONSE); - } - - if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExecutionContext -> fileContent = " + fileContent); } - - fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExecutionContext -> onFileCreated -> Entering function."); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExecutionContext -> onFileCreated -> err = "+ err.stack); - callBack(err); - return; - } - - writeExucutionHistory(callBack); - return; - } - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExecutionContext -> err = "+ err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeExucutionHistory(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> Entering function."); } - - let fileName = "Execution.History." + bot.startMode + "." + runIndex + ".json"; - let filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + fileName; - - let newRecord = [ - thisObject.newHistoryRecord.date.valueOf(), - thisObject.newHistoryRecord.buyAvgRate, - thisObject.newHistoryRecord.sellAvgRate, - - thisObject.newHistoryRecord.lastSellRate, - thisObject.newHistoryRecord.sellExecRate, - thisObject.newHistoryRecord.lastBuyRate, - thisObject.newHistoryRecord.buyExecRate, - - thisObject.newHistoryRecord.marketRate, - thisObject.newHistoryRecord.newPositions, - thisObject.newHistoryRecord.newTrades, - thisObject.newHistoryRecord.movedPositions, - thisObject.newHistoryRecord.profitsBaseAsset, - thisObject.newHistoryRecord.profitsQuotedAsset, - thisObject.newHistoryRecord.combinedProfitsA, - thisObject.newHistoryRecord.combinedProfitsB, - - thisObject.newHistoryRecord.messageRelevance, - thisObject.newHistoryRecord.messageTitle, - thisObject.newHistoryRecord.messageBody, - thisObject.extraData - ]; - - thisObject.executionHistory.push(newRecord); - - let fileContent = JSON.stringify(thisObject.executionHistory); - - fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); - - function onFileCreated(err) { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> onFileCreated -> Entering function."); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExucutionHistory -> onFileCreated -> err = "+ err.stack); - callBack(err); - return; - } - - if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { - logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> onFileCreated -> Content written = " + fileContent); - } - - /* Here we will write the file containing the max sequence number. */ - - fileContent = runIndex; - fileName = "Execution.History." + bot.startMode + "." + "Sequence" + ".json"; - filePath = bot.filePathRoot + "/Output/" + sessionPath + "Trading-Process/" + fileName; - - fileStorage.createTextFile(filePath, fileContent + '\n', onSequenceFileCreated); - - function onSequenceFileCreated(err) { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeExucutionHistory -> onFileCreated -> onSequenceFileCreated -> Entering function."); } - - if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExucutionHistory -> onFileCreated -> onSequenceFileCreated -> err = "+ err.stack); - callBack(err); - return; - } - - writeStatusReport(callBack); - return; - } - } - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeExucutionHistory -> err = "+ err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function writeStatusReport(callBack) { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] saveThemAll -> writeStatusReport -> Entering function."); } - - statusReportModule.file = thisObject.statusReport; - statusReportModule.save(callBack); - - } - catch (err) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> writeStatusReport -> err = "+ err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] saveThemAll -> Error = " + err.message); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function truncDecimals(pFloatValue, pDecimals) { - - if (!pDecimals) pDecimals = 8; // Default value - - return parseFloat(parseFloat(pFloatValue).toFixed(pDecimals)); - - } - -}; diff --git a/MultiPeriod.js b/MultiPeriod.js index bf46a4b85..c414630a3 100644 --- a/MultiPeriod.js +++ b/MultiPeriod.js @@ -41,7 +41,7 @@ dataDependenciesModule = pDataDependencies; processConfig = pProcessConfig; - let USER_BOT_MODULE = require("./TradingBot") + let USER_BOT_MODULE = require("./Trading/TradingBot") botInstance = USER_BOT_MODULE.newTradingBot(bot, logger, UTILITIES, FILE_STORAGE); botInstance.initialize(exchangeAPI, callBackFunction); diff --git a/Session.js b/Session.js index feb0e7421..3e84808ed 100644 --- a/Session.js +++ b/Session.js @@ -53,12 +53,21 @@ if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { return } // This happens when the UI is reloaded, the session was running and tries to run it again. /* We are going to run the Definition comming at the event. */ + bot.APP_SCHEMA_ARRAY = JSON.parse(message.event.appSchema) bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem) bot.TRADING_ENGINE = JSON.parse(message.event.tradingEngine) bot.SESSION = JSON.parse(message.event.session) bot.DEPENDENCY_FILTER = JSON.parse(message.event.dependencyFilter) bot.RESUME = message.event.resume + /* Setup the APP_SCHEMA_MAP based on the APP_SCHEMA_ARRAY */ + bot.APP_SCHEMA_MAP = new Map() + for (let i = 0; i < bot.APP_SCHEMA_ARRAY.length; i++) { + let nodeDefinition = bot.APP_SCHEMA_ARRAY[i] + let key = nodeDefinition.type + bot.APP_SCHEMA_MAP.set(key, nodeDefinition) + } + /* Set the folderName for logging, reports, context and data output */ let config if (bot.SESSION.config !== undefined) { diff --git a/TradingBot.js b/Trading/TradingBot.js similarity index 99% rename from TradingBot.js rename to Trading/TradingBot.js index 699ccf512..37c791fc4 100644 --- a/TradingBot.js +++ b/Trading/TradingBot.js @@ -14,7 +14,7 @@ exports.newTradingBot = function newTradingBot(bot, logger, UTILITIES, FILE_STOR let utilities = UTILITIES.newCloudUtilities(logger) let fileStorage = FILE_STORAGE.newFileStorage(logger) - const COMMONS = require('./Commons.js') + const COMMONS = require('../Commons.js') let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE) let exchangeAPI diff --git a/Trading/TradingEngine.js b/Trading/TradingEngine.js new file mode 100644 index 000000000..30207da99 --- /dev/null +++ b/Trading/TradingEngine.js @@ -0,0 +1,69 @@ +exports.newTradingEngine = function newTradingEngine(bot, logger) { + + const MODULE_NAME = 'Trading Engine' + + let thisObject = { + initialize: initialize, + finalize: finalize + } + + return thisObject + + function initialize() { + /* Here we will go through all the nodes in the Trading Engine hiriarchy and apply the initial value to the value property when needed */ + + if (bot.TRADING_ENGINE.isInitialized !== true || bot.RESUME === false) { + bot.TRADING_ENGINE.isInitialized = true + initializeNode(bot.TRADING_ENGINE) + } + } + + function finalize() { + + } + + function initializeNode(node) { + if (node === undefined) { return } + + /* Here we initialize the node value */ + if (node.config !== undefined) { + if (node.config.initialValue !== undefined) { + node.value = node.config.initialValue + } + } + + /* Now we go down through all this node children */ + let nodeDefinition = bot.APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition === undefined) { return } + + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + initializeNode(node[property.name]) + } + previousPropertyName = property.name + } + break + } + case 'array': { + if (node[property.name] !== undefined) { + let nodePropertyArray = node[property.name] + object[property.name] = [] + for (let m = 0; m < nodePropertyArray.length; m++) { + initializeNode(nodePropertyArray[m]) + } + } + break + } + } + } + } + } +} + diff --git a/TradingSimulation.js b/Trading/TradingSimulation.js similarity index 98% rename from TradingSimulation.js rename to Trading/TradingSimulation.js index 313b2e1e7..0f78d4c25 100644 --- a/TradingSimulation.js +++ b/Trading/TradingSimulation.js @@ -52,9 +52,11 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT } let tradingSystem = bot.TRADING_SYSTEM + let tradingEngine = bot.TRADING_ENGINE + let sessionParameters = bot.SESSION.parameters - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.initialDatetime = ' + bot.VALUES_TO_USE.timeRange.initialDatetime) } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> bot.VALUES_TO_USE.timeRange.finalDatetime = ' + bot.VALUES_TO_USE.timeRange.finalDatetime) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> sessionParameters.timeRange.config.initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> sessionParameters.timeRange.config.finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime) } let timerToCloseStage = 0 @@ -78,6 +80,10 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1 } + const TRADING_ENGINE_MODULE = require('./TradingEngine.js') + let tradingEngineModule = TRADING_ENGINE_MODULE.newTradingEngine(bot, logger) + tradingEngineModule.initialize() + if (variable.isInitialized !== true || bot.RESUME === false) { variable.isInitialized = true @@ -239,7 +245,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT } else { /* Estimate Initial Candle based on the timeRage configured for the session. */ let firstEnd = candles[0].end - let targetEnd = bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() + let targetEnd = sessionParameters.timeRange.config.initialDatetime.valueOf() let diff = targetEnd - firstEnd let amount = diff / variable.episode.parameters.timeFrame @@ -247,7 +253,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT if (currentCandleIndex < 0) { currentCandleIndex = 0 } if (currentCandleIndex > candles.length - 1) { /* - This will happen when the bot.VALUES_TO_USE.timeRange.initialDatetime is beyond the last candle available, + This will happen when the sessionParameters.timeRange.config.initialDatetime is beyond the last candle available, meaning that the dataSet needs to be updated with more up-to-date data. */ currentCandleIndex = candles.length - 1 @@ -274,13 +280,13 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT let candle = candles[currentCandleIndex] /* Not processing while out of user-defined time range */ - if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle before the bot.VALUES_TO_USE.timeRange.initialDatetime.') } + if (candle.end < sessionParameters.timeRange.config.initialDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle before the sessionParameters.timeRange.config.initialDatetime.') } controlLoop() return } - if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle after the bot.VALUES_TO_USE.timeRange.finalDatetime.') } + if (candle.begin > sessionParameters.timeRange.config.finalDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle after the sessionParameters.timeRange.config.finalDatetime.') } afterLoop() return } @@ -367,8 +373,8 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Simulation ' + bot.sessionKey + ' Loop # ' + currentCandleIndex + ' @ ' + processingDate) } /* Telling the world we are alive and doing well */ - let fromDate = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) - let lastDate = new Date(bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) + let fromDate = new Date(sessionParameters.timeRange.config.initialDatetime.valueOf()) + let lastDate = new Date(sessionParameters.timeRange.config.finalDatetime.valueOf()) let currentDateString = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2) let currentDate = new Date(loopingDay) @@ -1713,19 +1719,19 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT function putOpeningOrder() { if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function.') } - /* We wont take a position unless we are withing the bot.VALUES_TO_USE.timeRange.initialDatetime and the bot.VALUES_TO_USE.timeRange.finalDatetime range */ - if (bot.VALUES_TO_USE.timeRange.initialDatetime !== undefined) { - if (candle.end < bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> bot.VALUES_TO_USE.timeRange.initialDatetime = ' + bot.VALUES_TO_USE.timeRange.initialDatetime) } + /* We wont take a position unless we are withing the sessionParameters.timeRange.config.initialDatetime and the sessionParameters.timeRange.config.finalDatetime range */ + if (sessionParameters.timeRange.config.initialDatetime !== undefined) { + if (candle.end < sessionParameters.timeRange.config.initialDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> sessionParameters.timeRange.config.initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime) } takePositionAtSimulation() return } } /* We wont take a position if we are past the final datetime */ - if (bot.VALUES_TO_USE.timeRange.finalDatetime !== undefined) { - if (candle.begin > bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf()) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> bot.VALUES_TO_USE.timeRange.finalDatetime = ' + bot.VALUES_TO_USE.timeRange.finalDatetime) } + if (sessionParameters.timeRange.config.finalDatetime !== undefined) { + if (candle.begin > sessionParameters.timeRange.config.finalDatetime.valueOf()) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> sessionParameters.timeRange.config.finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime) } takePositionAtSimulation() return }