Files
Superalgos/Trading-Process/User.Bot.js
T
2018-09-15 12:55:54 -03:00

695 lines
30 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
}
/*
This variable will be used during initialization to get the parameters
from the platform. At the moment it won't be used and we will only create
a single bot instance. In the future it will allow to create different
clones of the algonet.
*/
let genes
/*
The reference to the Traing Platform Advanced Algos Assistant that will
allow to put positions on the exchange
*/
let assistant
/*
This is a dependency example to other bots, in this case to the the
LRC Indicator.
*/
let gaussStorage
return thisObject
/*
The initialize function should be created as follows.
*/
function initialize (pAssistant, pGenes, 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)
checkGenes(pGenes, callBackFunction)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] initialize -> onDone -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function checkGenes (pGenes, callBackFunction) {
try {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] checkGenes -> Entering function.') }
function getGeneticRulesByName (pName) {
for (let i = 0; i < bot.genes.length; i++) {
let gene = bot.genes[i]
if (gene.name === pName) {
return gene
}
}
return undefined
}
let stopLoss = getGeneticRulesByName('stopLoss')
if (pGenes.stopLoss < stopLoss.lowerLimit || pGenes.stopLoss > stopLoss.upperLimit) {
logger.write(MODULE_NAME, '[ERROR] getGeneticRules -> Genes received are out of range.')
logger.write(MODULE_NAME, '[ERROR] getGeneticRules -> pGenes = ' + JSON.stringify(pGenes))
logger.write(MODULE_NAME, '[ERROR] getGeneticRules -> bot.genes = ' + JSON.stringify(bot.genes))
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
genes = pGenes
callBackFunction(global.DEFAULT_OK_RESPONSE)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] checkGenes -> err = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function start (callBackFunction) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> Entering function.') }
const market = global.MARKET
const LAST_SELL_RATE_KEY = 'AAVikings' + '-' + 'AAArtudito' + '-' + 'Trading-Process' + '-' + 'lastSellRate'
const LAST_BUY_RATE_KEY = 'AAVikings' + '-' + 'AAArtudito' + '-' + 'Trading-Process' + '-' + 'lastBuyRate'
const CHANNEL_DOWN = -1
const CHANNEL_UP = 1
const NO_CHANNEL = 0
// Parameters
let targetTradeEnabled = false
const TRADE_TARGET = 2.0
let stopLossEnabled = true
let stopLossPorcentage = genes.stopLoss / 100
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.
*/
let positions = assistant.getPositions()
let lastSellRate = assistant.remindMeOf(LAST_SELL_RATE_KEY)
if (targetTradeEnabled === true && assistant.getAvailableBalance().assetA > 1 && lastSellRate !== undefined) {
let currentROI = ((lastSellRate - assistant.getTicker().bid) / lastSellRate) * 100
if (currentROI > TRADE_TARGET) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> Current trade ROI is greater than default target. Buying...') }
createBuyPosition(onDone)
return
}
}
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.') }
let firstChannelTilt = 0
// Simulation for testing
if (Math.random(1) <= 0.5) {
createBuyPosition(callBack)
} else {
createSellPosition(callBack)
}
// getChannelTilt(firstTiltCheck);
function firstTiltCheck (err, channelTilt) {
try {
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 -> firstTiltCheck -> err = ' + err.message)
callBack(global.DEFAULT_FAIL_RESPONSE)
} else {
if (channelTilt == 1) {
createBuyPosition(callBack)
} else if (channelTilt == -1) {
firstChannelTilt = -1
getSecondChannelTilt(secondTiltCheck)
} else {
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 -> 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) {
createSellPosition(callBack)
} else {
if (LOG_INFO === true) { logger.write(MODULE_NAME, "[INFO] start -> businessLogic -> secondTiltCheck -> Nothing to do, there isn't a 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)
}
}
}
function getChannelTilt (callBack) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> getChannelTilt -> Entering function.') }
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(lrcPoint, previousLRCPoint, callBack)
} 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: ' + 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
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(lrcPoint, previousLRCPoint, callBack)
} 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 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(lrcPoint, previousLRCPoint, callBack)
}
}
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(lrcPoint, previousLRCPoint, callBack)
} 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(lrcPoint, previousLRCPoint, callBack)
} 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 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(lrcPoint, previousLRCPoint, callBack)
}
}
function applyBotRules (lrcPoint, previousLRCPoint, callBack) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> applyBotRules -> Entering Function.') }
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
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
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)
}
function checkToSellWithStopLoss () {
let allowSell = true
let assetBBalance = assistant.getAvailableBalance().assetB
let currentRate = assistant.getMarketRate()
let amountB = assistant.getAvailableBalance().assetB
let amountA = amountB * currentRate
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) {
investment = assistant.getAvailableBalance().assetB * lastBuyRate
}
// Calculate maximum loss setup
let currentLoss = amountA - investment
let stopLossValue = investment * stopLossPorcentage
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 = '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) {
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, sell: ' + message) }
assistant.sendMessage(3, 'Stop Loss', message)
} else {
let currentRisk = currentLoss / 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, not sell: ' + message) }
assistant.sendMessage(3, 'Stop Loss', message)
allowSell = false
}
}
}
return allowSell
}
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/30-min/' + datePath
let fileName = market.assetA + '_' + market.assetB + '.json'
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/30-min', 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.')
callBackFunction(global.DEFAULT_RETRY_RESPONSE)
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/02-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) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createBuyPosition -> Entering function.') }
let assetABalance = assistant.getAvailableBalance().assetA
let currentRate = assistant.getTicker().ask // *.9
let amountA = assistant.getAvailableBalance().assetA
let amountB = Number((amountA / currentRate).toFixed(8))
if (positions.length > 0 && positions[0].type === 'buy' && positions[0].status !== 'executed') {
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()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
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 -> createBuyPosition -> Artuditu put a new BUY position at price: $' + Number(currentRate).toLocaleString()) }
let message = 'Creating a new buy position. Price: $' + Number(currentRate).toLocaleString()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
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.') }
callBack(global.DEFAULT_OK_RESPONSE)
}
}
function createSellPosition (callBack) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] start -> businessLogic -> createSellPosition -> Entering function.') }
let assetBBalance = assistant.getAvailableBalance().assetB
let currentRate = assistant.getTicker().bid // *1.1
let amountB = assistant.getAvailableBalance().assetB
let amountA = amountB * currentRate
if (positions.length > 0 && positions[0].type === 'sell' && positions[0].status !== 'executed') {
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()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
assistant.sendMessage(6, 'Moving Position', message)
message = bot.processDatetime.toISOString() + ' - ' + message
assistant.sendEmail('Alerts', message, bot.emailSubscriptions)
assistant.rememberThis(LAST_SELL_RATE_KEY, currentRate)
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()
message += '. Combined ROI on current execution: ' + getCombinedProfit() + '%. '
assistant.sendMessage(7, 'Selling', message)
message = bot.processDatetime.toISOString() + ' - ' + message
assistant.sendEmail('Alerts', message, bot.emailSubscriptions)
assistant.rememberThis(LAST_SELL_RATE_KEY, currentRate)
assistant.putPosition('sell', currentRate, amountA, amountB, callBack)
} else {
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)
}
}
function pad (str, max) {
str = str.toString()
return str.length < max ? pad('0' + str, max) : str
}
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 getCombinedProfit () {
if (assistant.getCombinedProfits() !== undefined) {
if (assistant.getCombinedProfits().assetA > 0) {
return Number(assistant.getCombinedProfits().assetA).toLocaleString()
} else {
return Number(assistant.getCombinedProfits().assetB).toLocaleString()
}
}
}
}
}