508 lines
26 KiB
JavaScript
508 lines
26 KiB
JavaScript
exports.newUserBot = function newUserBot(bot, logger) {
|
|
|
|
const MODULE_NAME = "User Bot";
|
|
const FULL_LOG = true;
|
|
const LOG_INFO = true;
|
|
|
|
let thisObject = {
|
|
initialize: initialize,
|
|
start: start
|
|
};
|
|
|
|
let assistant; // The reference to the Traing Platform Advanced Algos Assistant.
|
|
let gaussStorage; // This is an example of dependency to other bots
|
|
|
|
return thisObject;
|
|
|
|
function initialize(pAssistant, callBackFunction) {
|
|
try {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
|
|
|
logger.fileName = MODULE_NAME;
|
|
|
|
assistant = pAssistant;
|
|
|
|
let key = "AAVikings-AAGauss-LRC-Points-Multi-Period-Daily-dataSet.V1";
|
|
|
|
gaussStorage = assistant.dataDependencies.dataSets.get(key);
|
|
|
|
callBackFunction(global.DEFAULT_OK_RESPONSE);
|
|
} catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] initialize -> onDone -> err = " + err.message);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function start(callBackFunction) {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
|
|
|
|
const LAST_BUY_RATE_KEY = "AAVikings" + "-" + "AAArtudito" + "-" + "Trading-Process" + "-" + "lastBuyRate";
|
|
|
|
let market = global.MARKET;
|
|
let periodName = "30-min";
|
|
|
|
let stopLossEnabled = true;
|
|
let stopLossPorcentage = 0.02;
|
|
|
|
/*
|
|
This trading bot will use an strategy based on the interpretation of the Linear Regression Curve Channel.
|
|
*/
|
|
|
|
businessLogic(onDone);
|
|
|
|
function onDone(err) {
|
|
try {
|
|
switch (err.result) {
|
|
case global.DEFAULT_OK_RESPONSE.result: {
|
|
logger.write(MODULE_NAME, "[INFO] start -> 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] start -> onDone -> Retry Later. Requesting Execution Retry.");
|
|
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.
|
|
logger.write(MODULE_NAME, "[ERROR] start -> onDone -> Operation Failed. Aborting the process. err = " + err.message);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
return;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> onDone -> err = " + err.message);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function businessLogic(callBack) {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> Entering function."); }
|
|
|
|
getChannelTilt(botDecision);
|
|
|
|
function botDecision(err, channelTilt) {
|
|
try {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> botDecision -> Entering Function."); }
|
|
|
|
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> botDecision -> err = " + err.message);
|
|
callBack(global.DEFAULT_FAIL_RESPONSE);
|
|
} else {
|
|
if (channelTilt == 1) {
|
|
createBuyPosition(callBack);
|
|
} else if (channelTilt == -1) {
|
|
createSellPosition(callBack);
|
|
} else {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> botDecision -> Nothing to do, there isn't a sell or buy oportunity."); }
|
|
|
|
callBack(global.DEFAULT_OK_RESPONSE);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> botDecision -> err = " + err.message);
|
|
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getChannelTilt(callBack) {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> getChannelTilt -> Entering function."); }
|
|
|
|
const CHANNEL_DOWN = -1;
|
|
const CHANNEL_UP = 1;
|
|
const NO_CHANNEL = 0;
|
|
let lrcPoint, previousLRCPoint;
|
|
|
|
let queryDate = new Date(bot.processDatetime);
|
|
getLRCPointsFile(queryDate, onLRCPointsFileReceived);
|
|
|
|
function onLRCPointsFileReceived(err, lrcPointsFile) {
|
|
try {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> getChannelTilt -> onLRCPointsFileReceived."); }
|
|
|
|
let lastIndexLrcPointsFile = lrcPointsFile.length - 1;
|
|
let lastAvailableDateTime = lrcPointsFile[lastIndexLrcPointsFile][0];
|
|
|
|
if (bot.processDatetime.valueOf() <= lastAvailableDateTime || !isExecutionToday()) {
|
|
for (let i = 0; i < lrcPointsFile.length; 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 {
|
|
queryDate.setDate(queryDate.getDate() - 1);
|
|
getLRCPointsFile(queryDate, onPreviousLRCPointsFileReceived);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
logger.write(MODULE_NAME, "[ERROR] start -> businessLogic -> getChannelTilt -> onLRCPointsFileReceived. The expected LRC Point was not found.");
|
|
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 {
|
|
queryDate.setDate(queryDate.getDate() - 1);
|
|
getLRCPointsFile(queryDate, onPreviousLRCPointsFileReceived);
|
|
}
|
|
} else {
|
|
if (LOG_INFO === true) logger.write(MODULE_NAME, "[WARN] start -> getChannelTilt -> 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 -> getChannelTilt -> 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 -> getChannelTilt -> 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 -> getChannelTilt -> 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) {
|
|
channelTilt = CHANNEL_UP; // The channel points 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
|
|
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 (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);
|
|
}
|
|
}
|
|
|
|
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() {
|
|
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."); }
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Calculate maximum loss setup
|
|
let currentLoss = amountA - investment;
|
|
let stopLossValue = investment * stopLossPorcentage;
|
|
|
|
if (currentLoss >= 0) {
|
|
return true; // If we are winning sell.
|
|
} else if (currentLoss < 0) {
|
|
if (Math.abs(currentLoss) > stopLossValue) {
|
|
let message = "Selling to avoid loss more than " + Number(stopLossPorcentage * 100).toLocaleString() + "% of our initial investment. ";
|
|
|
|
message += "Combined ROI on current execution: ";
|
|
if (assistant.getCombinedProfits().assetA > 0) {
|
|
message += Number(assistant.getCombinedProfits().assetA).toLocaleString() + "%. ";
|
|
} else {
|
|
message += Number(assistant.getCombinedProfits().assetB).toLocaleString() + "%. ";
|
|
}
|
|
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> getChannelTilt -> applyBotRules -> manageStopLossConfiguration -> Stop loss: " + 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";
|
|
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); }
|
|
|
|
assistant.sendMessage(3, "Stop Loss", message);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function getLRCPointsFile(dateTime, callback) {
|
|
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> getLRCPointsFile -> Entering function."); }
|
|
|
|
let datePath = dateTime.getUTCFullYear() + "/" + pad(dateTime.getUTCMonth() + 1, 2) + "/" + pad(dateTime.getUTCDate(), 2);
|
|
let filePath = "LRC-Points/Multi-Period-Daily/" + periodName + "/" + datePath;
|
|
let fileName = market.assetA + '_' + market.assetB + ".json"
|
|
|
|
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);
|
|
|
|
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.");
|
|
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function createBuyPosition(callBack) {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Entering function."); }
|
|
|
|
let positions = assistant.getPositions();
|
|
let assetABalance = assistant.getAvailableBalance().assetA;
|
|
let currentRate = assistant.getMarketRate();
|
|
let amountA = assistant.getAvailableBalance().assetA;
|
|
let amountB = amountA / currentRate;
|
|
|
|
if (positions.length > 0 && positions[0].type === "buy" && positions[0].status !== "executed" && (bot.processDatetime.valueOf() - positions[0].date) > (60000 * 15)) {
|
|
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Moving an existing 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);
|
|
|
|
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()); }
|
|
|
|
let message = "Creating a new buy position. Price: $" + Number(currentRate).toLocaleString();
|
|
if (assistant.getCombinedProfits() !== undefined) {
|
|
message += ". Combined ROI on current execution: ";
|
|
if (assistant.getCombinedProfits().assetA > 0) {
|
|
message += Number(assistant.getCombinedProfits().assetA).toLocaleString() + "%. ";
|
|
} else {
|
|
message += Number(assistant.getCombinedProfits().assetB).toLocaleString() + "%. ";
|
|
}
|
|
}
|
|
|
|
assistant.sendMessage(7, "Buying", message);
|
|
|
|
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."); }
|
|
|
|
callBack(global.DEFAULT_OK_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function createSellPosition(callBack) {
|
|
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createSellPosition -> Entering function."); }
|
|
|
|
let positions = assistant.getPositions();
|
|
let assetBBalance = assistant.getAvailableBalance().assetB;
|
|
let currentRate = assistant.getMarketRate();
|
|
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 (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> createBuyPosition -> Artuditu is moving an existing position to a new rate: " + Number(currentRate).toLocaleString()); }
|
|
|
|
let message = "Moving an existing sell position to a new price: $" + Number(currentRate).toLocaleString();
|
|
assistant.sendMessage(6, "Moving Position", message);
|
|
|
|
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();
|
|
if (assistant.getCombinedProfits() !== undefined) {
|
|
message += ". Combined ROI on current execution: ";
|
|
if (assistant.getCombinedProfits().assetA > 0) {
|
|
message += Number(assistant.getCombinedProfits().assetA).toLocaleString() + "%. ";
|
|
} else {
|
|
message += Number(assistant.getCombinedProfits().assetB).toLocaleString() + "%. ";
|
|
}
|
|
}
|
|
assistant.sendMessage(7, "Selling", message);
|
|
|
|
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); }
|
|
|
|
callBack(global.DEFAULT_OK_RESPONSE);
|
|
}
|
|
}
|
|
|
|
function pad(str, max) {
|
|
str = str.toString();
|
|
return str.length < max ? pad("0" + str, max) : str;
|
|
}
|
|
|
|
}
|
|
}; |