diff --git a/Trading-Process/User.Bot.js b/Trading-Process/User.Bot.js index 06bee8e02..af13ee57d 100644 --- a/Trading-Process/User.Bot.js +++ b/Trading-Process/User.Bot.js @@ -44,6 +44,13 @@ let stopLossEnabled = true; let stopLossPorcentage = 0.02; + let lastBuyRate = assistant.remindMeOf(LAST_BUY_RATE_KEY); + if (lastBuyRate === undefined) { + let currentRate = assistant.getMarketRate(); + lastBuyRate = currentRate; + assistant.rememberThis(LAST_BUY_RATE_KEY, lastBuyRate); + } + /* This trading bot will use an strategy based on the interpretation of the Linear Regression Curve Channel. */ @@ -63,7 +70,7 @@ callBackFunction(global.DEFAULT_RETRY_RESPONSE); return; } - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. logger.write(MODULE_NAME, "[ERROR] start -> onDone -> Operation Failed. Aborting the process. err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return; @@ -77,29 +84,56 @@ function businessLogic(callBack) { if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> Entering function."); } + let firstChannelTilt = 0; - getChannelTilt(botDecision); + getChannelTilt(firstTiltCheck); - function botDecision(err, channelTilt) { + function firstTiltCheck(err, channelTilt) { try { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> botDecision -> Entering Function."); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> firstTiltCheck -> Entering Function."); } if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> botDecision -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> firstTiltCheck -> err = " + err.message); callBack(global.DEFAULT_FAIL_RESPONSE); } else { if (channelTilt == 1) { - createBuyPosition(callBack); + firstChannelTilt = 1; + getSecondChannelTilt(secondTiltCheck); } else if (channelTilt == -1) { - createSellPosition(callBack); + firstChannelTilt = -1; + getSecondChannelTilt(secondTiltCheck); } else { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> botDecision -> Nothing to do, there isn't a sell or buy oportunity."); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> firstTiltCheck -> Nothing to do, there isn't a buy or sell oportunity."); } callBack(global.DEFAULT_OK_RESPONSE); } } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> botDecision -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> firstTiltCheck -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function secondTiltCheck(err, secondChannelTilt) { + try { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> secondTiltCheck -> Entering Function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> secondTiltCheck -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } else { + if (firstChannelTilt == 1 && secondChannelTilt == 1) { + createBuyPosition(callBack); + }else if (firstChannelTilt == -1 && secondChannelTilt == -1) { + createSellPosition(callBack); + } else { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> secondTiltCheck -> Nothing to do, there isn't a buy or sell oportunity."); } + + callBack(global.DEFAULT_OK_RESPONSE); + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> secondTiltCheck -> err = " + err.message); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } @@ -154,7 +188,7 @@ } } - logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> getChannelTilt -> onLRCPointsFileReceived. The expected LRC Point was not found."); + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> getChannelTilt -> onLRCPointsFileReceived. The expected LRC Point was not found: "+bot.processDatetime.valueOf()); callBack(global.DEFAULT_FAIL_RESPONSE); } else { // Running live we will process last available LRCPoint only if it's delayed 25 minutes top @@ -231,114 +265,54 @@ if (lrcPoint.max < lrcPoint.mid && lrcPoint.min > lrcPoint.mid && lrcPoint.mid < lrcPoint.min) { if (lrcPoint.min > previousLRCPoint.min && lrcPoint.mid > previousLRCPoint.mid && lrcPoint.max > previousLRCPoint.max) { - channelTilt = CHANNEL_UP; // The channel points UP + // The channel points UP + channelTilt = CHANNEL_UP; ruleApplied += "Rule_1."; } } if (lrcPoint.min < lrcPoint.mid && lrcPoint.max > lrcPoint.mid && lrcPoint.mid < lrcPoint.max) { if (lrcPoint.min < previousLRCPoint.min && lrcPoint.mid < previousLRCPoint.mid && lrcPoint.max < previousLRCPoint.max) { - channelTilt = CHANNEL_DOWN; // The channel points DOWN + // The channel points DOWN + channelTilt = CHANNEL_DOWN; ruleApplied += "Rule_2."; } } if (lrcPoint.min < previousLRCPoint.min && lrcPoint.mid <= previousLRCPoint.mid) { // 15 AND 30 changed direction from up to down - if (checkToSellWithStopLoss()) { - channelTilt = CHANNEL_DOWN; - ruleApplied += "Rule_3a."; - } + if(checkToSellWithStopLoss()) { + channelTilt = CHANNEL_DOWN; + ruleApplied += "Rule_3a."; + } } if (lrcPoint.min > previousLRCPoint.min && lrcPoint.mid >= previousLRCPoint.mid) { // 15 AND 30 changed direction from down to up - getLastCandleFromOlivia(onLastCandleFromOliviaReceived); - - function onLastCandleFromOliviaReceived(candle) { - if (candle !== undefined && candle.close >= candle.open) { - //We will only fast buy if the current candle close, is greater than the open - channelTilt = CHANNEL_UP; - ruleApplied += "Rule_3b."; - } - - let logMessage = bot.processDatetime.toISOString() + ". Rule: " + ruleApplied + "Tilt: " + channelTilt + " (" + Number(lrcPoint.min).toLocaleString() + "|" + Number(lrcPoint.mid).toLocaleString() + "|" + Number(lrcPoint.max).toLocaleString() + ")"; - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> Results: " + logMessage); } - - callBack(global.DEFAULT_OK_RESPONSE, channelTilt); - } - } else { - - let logMessage = bot.processDatetime.toISOString() + ". Rule: " + ruleApplied + "Tilt: " + channelTilt + " (" + Number(lrcPoint.min).toLocaleString() + "|" + Number(lrcPoint.mid).toLocaleString() + "|" + Number(lrcPoint.max).toLocaleString() + ")"; - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> Results: " + logMessage); } - - callBack(global.DEFAULT_OK_RESPONSE, channelTilt); + channelTilt = CHANNEL_UP; + ruleApplied += "Rule_3b."; } + + let logMessage = bot.processDatetime.toISOString() + ". First Rule: " + ruleApplied + ". Tilt: " + channelTilt + " (" + Number(lrcPoint.min).toLocaleString() + "|" + Number(lrcPoint.mid).toLocaleString() + "|" + Number(lrcPoint.max).toLocaleString() + ")"; + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> Results: " + logMessage); } + + callBack(global.DEFAULT_OK_RESPONSE, channelTilt); + } - - function getLastCandleFromOlivia(callback) { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> getLastCandleFromOlivia -> Entering Function."); } - - let key = "AAMasters-AAOlivia-Candles-Multi-Period-Daily-dataSet.V1"; - let oliviaStorage = assistant.dataDependencies.dataSets.get(key); - - let dateTime = new Date(lrcPoint.begin); - let datePath = dateTime.getUTCFullYear() + "/" + pad(dateTime.getUTCMonth() + 1, 2) + "/" + pad(dateTime.getUTCDate(), 2); - let filePath = "Candles/Multi-Period-Daily/" + periodName + "/" + datePath; - let fileName = market.assetA + '_' + market.assetB + ".json" - - oliviaStorage.getTextFile(filePath, fileName, onFileReceived); - - function onFileReceived(err, text) { - try { - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] getLastCandleFromOlivia -> onFileReceived-> Entering Function."); } - - let candleFile = JSON.parse(text); - let lastCandleOlivia; - for (let i = 0; i < candleFile.length; i++) { - if (candleFile[i][4] == lrcPoint.begin) { - lastCandleOlivia = { - open: candleFile[i][2], - close: candleFile[i][3], - min: candleFile[i][0], - max: candleFile[i][1], - begin: candleFile[i][4], - end: candleFile[i][5] - }; - break; - } - } - - callback(lastCandleOlivia); - } else { - logger.write(MODULE_NAME, "[WARN] getLastCandleFromOlivia -> onFileReceived -> Failed to get the file."); - callback(undefined); - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] getLastCandleFromOlivia -> onFileReceived -> err = " + err.message); - callBackFunction(global.DEFAULT_RETRY_RESPONSE); - } - } - } - - function checkToSellWithStopLoss() { + + function checkToSellWithStopLoss() { + let allowSell = true; let assetBBalance = assistant.getAvailableBalance().assetB; let currentRate = assistant.getMarketRate(); let amountB = assistant.getAvailableBalance().assetB; let amountA = amountB * currentRate; - let lastBuyRate = assistant.remindMeOf(LAST_BUY_RATE_KEY); - if (stopLossEnabled && assetBBalance > 0) { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> Stop Loss Enabled, checking to sell."); } + if (stopLossEnabled) { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> Stop Loss Enabled, checking to sell."); } // Get the current investment value let investment = 0; if (assistant.getAvailableBalance() !== undefined) { - //Get our last buy, if any - if (lastBuyRate === undefined) - lastBuyRate = currentRate; // We assume we just bought - investment = assistant.getAvailableBalance().assetB * lastBuyRate; } @@ -346,11 +320,12 @@ let currentLoss = amountA - investment; let stopLossValue = investment * stopLossPorcentage; - if (currentLoss >= 0) { - return true; // If we are winning sell. - } else if (currentLoss < 0) { + if (currentLoss > 0) { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> No loss, we can sell."); } + + } else if (assetBBalance > 0) { if (Math.abs(currentLoss) > stopLossValue) { - let message = "Selling to avoid loss more than " + Number(stopLossPorcentage * 100).toLocaleString() + "% of our initial investment. "; + let message = "Sell to avoid loss more than " + Number(stopLossPorcentage * 100).toLocaleString() + "% of our initial investment. "; message += "Combined ROI on current execution: "; if (assistant.getCombinedProfits().assetA > 0) { @@ -359,28 +334,184 @@ message += Number(assistant.getCombinedProfits().assetB).toLocaleString() + "%. "; } - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> Stop loss: " + message); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> Stop loss, sell: " + message); } assistant.sendMessage(3, "Stop Loss", message); - return true; - } else { let currentRisk = currentLoss / investment; - let message = "Not selling now. Risking " + Number(currentRisk * 100).toLocaleString() + "% of our initial investment"; + let message = "Not sell now. Risking " + Number(currentRisk * 100).toLocaleString() + "% of our initial investment"; message += ". Last Buy: $" + Number(lastBuyRate).toLocaleString(); message += ". Not selling at: $" + Number(currentRate).toLocaleString(); - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> Stop loss: " + message); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> Stop loss, not sell: " + message); } assistant.sendMessage(3, "Stop Loss", message); - return false; + allowSell = false; } } - } else { - return true; } + + return allowSell; + } + + } + + function getSecondChannelTilt(callBack) { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> getSecondChannelTilt -> Entering function."); } + + const CHANNEL_DOWN = -1; + const CHANNEL_UP = 1; + const NO_CHANNEL = 0; + let lrcPoint, previousLRCPoint; + + getLRCPointsMarketFile(onLRCPointsFileReceived); + + function onLRCPointsFileReceived(err, lrcPointsFile) { + try { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> getSecondChannelTilt -> onLRCPointsFileReceived."); } + + let lastIndexLrcPointsFile = lrcPointsFile.length - 1; + let lastAvailableDateTime = lrcPointsFile[lastIndexLrcPointsFile][0]; + + if (bot.processDatetime.valueOf() <= lastAvailableDateTime || !isExecutionToday()) { + for (let i = lrcPointsFile.length-1; i >= 0; i--) { + if (bot.processDatetime.valueOf() >= lrcPointsFile[i][0] && bot.processDatetime.valueOf() < lrcPointsFile[i][1]) { + lrcPoint = { + begin: lrcPointsFile[i][0], + end: lrcPointsFile[i][1], + min: lrcPointsFile[i][2], + mid: lrcPointsFile[i][3], + max: lrcPointsFile[i][4] + }; + + if (i >= 1) { + let previous = i - 1; + previousLRCPoint = { + begin: lrcPointsFile[previous][0], + end: lrcPointsFile[previous][1], + min: lrcPointsFile[previous][2], + mid: lrcPointsFile[previous][3], + max: lrcPointsFile[previous][4] + }; + + applyBotRules(); + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> getSecondChannelTilt -> onLRCPointsFileReceived. The expected previous LRC Point was not found."); + } + return; + } + } + + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> getSecondChannelTilt -> onLRCPointsFileReceived. The expected LRC Point was not found." + bot.processDatetime.valueOf() +". "+JSON.stringify(lrcPointsFile)); + callBack(global.DEFAULT_FAIL_RESPONSE); + } else { + // Running live we will process last available LRCPoint only if it's delayed 25 minutes top + let maxTolerance = 25 * 60 * 1000; + if (bot.processDatetime.valueOf() <= (lastAvailableDateTime + maxTolerance)) { + lrcPoint = { + begin: lrcPointsFile[lastIndexLrcPointsFile][0], + end: lrcPointsFile[lastIndexLrcPointsFile][1], + min: lrcPointsFile[lastIndexLrcPointsFile][2], + mid: lrcPointsFile[lastIndexLrcPointsFile][3], + max: lrcPointsFile[lastIndexLrcPointsFile][4] + }; + + if (lastIndexLrcPointsFile >= 1) { + let previous = lastIndexLrcPointsFile - 1; + previousLRCPoint = { + begin: lrcPointsFile[previous][0], + end: lrcPointsFile[previous][1], + min: lrcPointsFile[previous][2], + mid: lrcPointsFile[previous][3], + max: lrcPointsFile[previous][4] + }; + + applyBotRules(); + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> getSecondChannelTilt -> onLRCPointsFileReceived. The expected previous LRC Point was not found."); + } + } else { + if (LOG_INFO === true) logger.write(MODULE_NAME, "[WARN] start -> getSecondChannelTilt -> onLRCPointsFileReceived. Available candle older than 5 minutes. Skeeping execution."); + + callBack(global.DEFAULT_OK_RESPONSE, NO_CHANNEL); // TODO CUSTOM_OK_RESPONSE + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getSecondChannelTilt -> onLRCPointsFileReceived -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function isExecutionToday() { + let localDate = new Date(); + let today = new Date(Date.UTC( + localDate.getUTCFullYear(), + localDate.getUTCMonth(), + localDate.getUTCDate())); + + return (today.getUTCFullYear() === bot.processDatetime.getUTCFullYear() + && today.getUTCMonth() === bot.processDatetime.getUTCMonth() + && today.getUTCDate() === bot.processDatetime.getUTCDate()); + } + + function onPreviousLRCPointsFileReceived(err, lrcPointsFile) { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getSecondChannelTilt -> onLRCPointsFileReceived."); } + + let previous = lrcPointsFile.length - 1; + previousLRCPoint = { + begin: lrcPointsFile[previous][0], + end: lrcPointsFile[previous][1], + min: lrcPointsFile[previous][2], + mid: lrcPointsFile[previous][3], + max: lrcPointsFile[previous][4] + }; + + applyBotRules(); + } + + function applyBotRules() { + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getSecondChannelTilt -> applyBotRules -> LRC Point Found."); } + + let channelTilt = NO_CHANNEL; + let ruleApplied = ""; + + if (lrcPoint.max < lrcPoint.mid && lrcPoint.min > lrcPoint.mid && lrcPoint.mid < lrcPoint.min) { + if (lrcPoint.min > previousLRCPoint.min && lrcPoint.mid > previousLRCPoint.mid && lrcPoint.max > previousLRCPoint.max) { + // The channel points UP + channelTilt = CHANNEL_UP; + ruleApplied += "Rule_1."; + } + } + + if (lrcPoint.min < lrcPoint.mid && lrcPoint.max > lrcPoint.mid && lrcPoint.mid < lrcPoint.max) { + if (lrcPoint.min < previousLRCPoint.min && lrcPoint.mid < previousLRCPoint.mid && lrcPoint.max < previousLRCPoint.max) { + // The channel points DOWN + channelTilt = CHANNEL_DOWN; + ruleApplied += "Rule_2."; + } + } + + if (lrcPoint.min < previousLRCPoint.min && lrcPoint.mid <= previousLRCPoint.mid) { + // 15 AND 30 changed direction from up to down + channelTilt = CHANNEL_DOWN; + ruleApplied += "Rule_3a."; + } + + if (lrcPoint.min > previousLRCPoint.min && lrcPoint.mid >= previousLRCPoint.mid) { + // 15 AND 30 changed direction from down to up + channelTilt = CHANNEL_UP; + ruleApplied += "Rule_3b."; + } + + let logMessage = bot.processDatetime.toISOString() + ". Second Rule: " + ruleApplied + ". Tilt: " + channelTilt + " (" + Number(lrcPoint.min).toLocaleString() + "|" + Number(lrcPoint.mid).toLocaleString() + "|" + Number(lrcPoint.max).toLocaleString() + ")"; + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getSecondChannelTilt -> applyBotRules -> Results: " + logMessage); } + + callBack(global.DEFAULT_OK_RESPONSE, channelTilt); + } } @@ -391,14 +522,22 @@ let filePath = "LRC-Points/Multi-Period-Daily/" + periodName + "/" + datePath; let fileName = market.assetA + '_' + market.assetB + ".json" - gaussStorage.getTextFile(filePath, fileName, onFileReceived); + if (bot.startMode === "Backtest" && bot.botCache.has(filePath)) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> getLRCPointsFile -> Getting the file from local cache."); } + + cleanUpCache(bot.botCache, "LRC-Points/Multi-Period-Daily/" + periodName , dateTime); + callback(global.DEFAULT_OK_RESPONSE, bot.botCache.get(filePath)); + + } else { + gaussStorage.getTextFile(filePath, fileName, onFileReceived); + } function onFileReceived(err, text) { if (err.result === global.DEFAULT_OK_RESPONSE.result) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> getLRCPointsFile -> onFileReceived > Entering Function."); } let lrcPointsFile = JSON.parse(text); - + bot.botCache.set(filePath, lrcPointsFile); callback(global.DEFAULT_OK_RESPONSE, lrcPointsFile); } else { logger.write(MODULE_NAME, "[ERROR] start -> getChannelTilt -> getLRCPointsFile -> onFileReceived -> Failed to get the file. Will abort the process and request a retry."); @@ -406,6 +545,50 @@ return; } } + + function cleanUpCache(fileCache, path, dateTimeCacheFile) { + let date = new Date(dateTimeCacheFile); + date.setDate(date.getDate() - 2); + let datePath = date.getUTCFullYear() + "/" + pad(date.getUTCMonth() + 1, 2) + "/" + pad(date.getUTCDate(), 2); + let cachePosition = path + '/' + datePath; + + // Remove and go back one day + if (fileCache.has(cachePosition)) { + fileCache.delete(cachePosition); + date.setDate(date.getDate() - 1); + cleanUpCache(fileCache, date); + } + } + } + + function getLRCPointsMarketFile(callback) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] getLRCPointsMarketFile -> Entering function."); } + + let filePath = "LRC-Points/Multi-Period-Market/01-hs"; + let fileName = market.assetA + '_' + market.assetB + ".json" + + if (bot.startMode === "Backtest" && bot.botCache.has(filePath)) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] getLRCPointsMarketFile -> Getting the file from local cache."); } + + callback(global.DEFAULT_OK_RESPONSE, bot.botCache.get(filePath)); + + } else { + + gaussStorage.getTextFile(filePath, fileName, onFileReceived); + } + function onFileReceived(err, text) { + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] getLRCPointsMarketFile -> onFileReceived > Entering Function."); } + + let lrcPointsFile = JSON.parse(text); + bot.botCache.set(filePath, lrcPointsFile); + callback(global.DEFAULT_OK_RESPONSE, lrcPointsFile); + } else { + logger.write(MODULE_NAME, "[ERROR]getLRCPointsMarketFile -> onFileReceived -> Failed to get the file. Will abort the process and request a retry."); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return; + } + } } function createBuyPosition(callBack) { @@ -415,23 +598,24 @@ let assetABalance = assistant.getAvailableBalance().assetA; let currentRate = assistant.getMarketRate(); let amountA = assistant.getAvailableBalance().assetA; - let amountB = amountA / currentRate; + let amountB = Number((amountA / currentRate).toFixed(8)); - if (positions.length > 0 && positions[0].type === "buy" && positions[0].status !== "executed" && (bot.processDatetime.valueOf() - positions[0].date) > (60000 * 15)) { + if (positions.length > 0 && positions[0].type === "buy" && positions[0].status !== "executed" && (bot.processDatetime.valueOf() - positions[0].date) > (60000 * 10)) { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Moving an existing position to a new price: $" + Number(currentRate).toLocaleString()); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Moving an existing BUY position to a new price: $" + Number(currentRate).toLocaleString()); } let message = "Moving an existing buy position to a new price: $" + Number(currentRate).toLocaleString(); assistant.sendMessage(6, "Moving Position", message); + message = bot.processDatetime.toISOString() + " - " + message; + assistant.sendEmail("Alerts", message, bot.emailSubscriptions); assistant.rememberThis(LAST_BUY_RATE_KEY, currentRate); assistant.movePosition(positions[0], currentRate, callBack); - } else if (assetABalance > 0) { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createSellPosition -> Artuditu put a new BUY position at price: $" + Number(currentRate).toLocaleString()); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Artuditu put a new BUY position at price: $" + Number(currentRate).toLocaleString()); } let message = "Creating a new buy position. Price: $" + Number(currentRate).toLocaleString(); if (assistant.getCombinedProfits() !== undefined) { @@ -444,12 +628,13 @@ } assistant.sendMessage(7, "Buying", message); + message = bot.processDatetime.toISOString() + " - " + message; + assistant.sendEmail("Alerts", message, bot.emailSubscriptions); assistant.rememberThis(LAST_BUY_RATE_KEY, currentRate); assistant.putPosition("buy", currentRate, amountA, amountB, callBack); - } else { if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Not enough available balance to buy."); } @@ -466,17 +651,18 @@ let amountB = assistant.getAvailableBalance().assetB; let amountA = amountB * currentRate; - if (positions.length > 0 && positions[0].type === "sell" && positions[0].status !== "executed" && (bot.processDatetime.valueOf() - positions[0].date) > (60000 * 15)) { + if (positions.length > 0 && positions[0].type === "sell" && positions[0].status !== "executed" && (bot.processDatetime.valueOf() - positions[0].date) > (60000 * 10)) { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Artuditu is moving an existing position to a new rate: " + Number(currentRate).toLocaleString()); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createSellPosition -> Artuditu is moving an existing SELL position to a new price: $" + Number(currentRate).toLocaleString()); } let message = "Moving an existing sell position to a new price: $" + Number(currentRate).toLocaleString(); assistant.sendMessage(6, "Moving Position", message); + message = bot.processDatetime.toISOString() + " - " + message; + assistant.sendEmail("Alerts", message, bot.emailSubscriptions); assistant.movePosition(positions[0], currentRate, callBack); } else if (assetBBalance > 0) { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createSellPosition -> Artuditu put a new SELL position at price: $" + Number(currentRate).toLocaleString()); } let message = "Creating a new sell position. Price: $" + Number(currentRate).toLocaleString(); @@ -489,11 +675,13 @@ } } assistant.sendMessage(7, "Selling", message); + message = bot.processDatetime.toISOString() + " - " + message; + assistant.sendEmail("Alerts", message, bot.emailSubscriptions); assistant.putPosition("sell", currentRate, amountA, amountB, callBack); } else { - if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> There is not enough available balance to sell. Available balance: " + assetBBalance); } + if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createSellPosition -> There is not enough available balance to sell."); } callBack(global.DEFAULT_OK_RESPONSE); } diff --git a/this.bot.config.json b/this.bot.config.json index db4c43fea..c89b6f6a4 100644 --- a/this.bot.config.json +++ b/this.bot.config.json @@ -16,26 +16,27 @@ "description": "Simple trading strategy to be used as a template.", "startMode": { "live": { - "run": "true", + "run": "false", "resumeExecution": "false" }, "backtest": { - "run": "false", - "beginDatetime": "2018-06-24T18:00:00.000Z", - "endDatetime": "2018-06-28T11:00:00.000Z", - "waitTime": 500 + "run": "true", + "beginDatetime": "2018-04-11T00:00:00.000Z", + "endDatetime": "2018-06-17T23:59:59.000Z", + "waitTime": 1 }, "competition": { "run": "false", - "beginDatetime": "2018-06-30T00:00:00.000Z", - "endDatetime": "2018-07-01T23:59:59.000Z", - "resumeExecution": "false" + "beginDatetime": "2018-07-04T00:00:00.000Z", + "endDatetime": "2018-07-06T23:59:59.000Z", + "resumeExecution": "true" } }, "normalWaitTime": 60000, "retryWaitTime": 10000, "sleepWaitTime": 3600000, "comaWaitTime": 86400000, + "timePeriod": 1800000, "statusDependencies": [ { "devTeam": "AAVikings", @@ -47,6 +48,16 @@ "process": "Multi-Period-Daily", "dataSetVersion": "dataSet.V1" }, + { + "devTeam": "AAVikings", + "bot": "AAGauss", + "botVersion": { + "major": 1, + "minor": 0 + }, + "process": "Multi-Period-Market", + "dataSetVersion": "dataSet.V1" + }, { "devTeam": "AAVikings", "bot": "AAArtudito", @@ -70,6 +81,17 @@ "dataSetVersion": "dataSet.V1", "dataSet": "Multi-Period-Daily" }, + { + "devTeam": "AAVikings", + "bot": "AAGauss", + "botVersion": { + "major": 1, + "minor": 0 + }, + "product": "LRC-Points", + "dataSetVersion": "dataSet.V1", + "dataSet": "Multi-Period-Market" + }, { "devTeam": "AAMasters", "bot": "AABruce", @@ -174,5 +196,6 @@ "moduleName": "History" } } - ] + ], + "emailSubscriptions": "mati108@gmail.com" } \ No newline at end of file