From afb5a352699e4be89d0462184261f653adc640b7 Mon Sep 17 00:00:00 2001 From: Matias Date: Fri, 5 Jul 2019 17:56:55 -0300 Subject: [PATCH 1/5] Fixing clone id name --- src/schema/mutations/AddBot.js | 2 +- src/schema/mutations/RemoveBot.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/schema/mutations/AddBot.js b/src/schema/mutations/AddBot.js index d269a1d5f..a3fba7964 100644 --- a/src/schema/mutations/AddBot.js +++ b/src/schema/mutations/AddBot.js @@ -29,7 +29,7 @@ const resolve = async (parent, { bot }, context) => { if (auxProduct.codeName === bot.productCodeName) { newBotClone = { displayName: auxBot.displayName, - codeName: auxBot.codeName + '-' + bot.cloneId, + codeName: auxBot.codeName, type: auxBot.type, profilePicture: auxBot.profilePicture, cloneId: bot.cloneId, diff --git a/src/schema/mutations/RemoveBot.js b/src/schema/mutations/RemoveBot.js index a09ce89f1..68e093e27 100644 --- a/src/schema/mutations/RemoveBot.js +++ b/src/schema/mutations/RemoveBot.js @@ -23,7 +23,7 @@ const resolve = async (parent, { bot }, context) => { if (auxTeam.codeName === bot.devTeam) { for (let j = 0; j < auxTeam.bots.length; j++) { const auxBot = auxTeam.bots[j] - if (auxBot.codeName + '-' + bot.cloneId === bot.codeName) { + if (auxBot.codeName === bot.codeName && auxBot.cloneId === bot.cloneId) { for (let k = 0; k < auxBot.products.length; k++) { const auxProduct = auxBot.products[k] if (auxProduct.codeName === bot.productCodeName) { From 4b519f25b62e57ebca65a7dc181ba819d8b293bd Mon Sep 17 00:00:00 2001 From: Matias Date: Sun, 7 Jul 2019 21:13:43 -0300 Subject: [PATCH 2/5] Adding read and write permissions to teams --- src/config/ecosystem.json | 8 +++---- src/models/ecosystem.js | 4 ++++ src/schema/mutations/AddTeam.js | 4 +++- src/schema/mutations/CreateFile.js | 13 +++++++++++- src/schema/queries/FileContent.js | 12 ++++++++++- src/schema/types/Host.js | 3 ++- src/utils/getDevTeamHost.js | 34 ++++++++++++++++++++++++++++++ 7 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/utils/getDevTeamHost.js diff --git a/src/config/ecosystem.json b/src/config/ecosystem.json index 3641c3fe7..20982e0fb 100644 --- a/src/config/ecosystem.json +++ b/src/config/ecosystem.json @@ -8,7 +8,7 @@ "url": "https://demo-api-dev.superalgos.org/", "storage": "localStorage", "container": "aamasters", - "accessKey": "" + "accessKey": "09e890b76767ede3e10f0526b5036600a6b8d3faac540336e12b493fdb2aa9ed46f86094c34652f99fb6322e5f09806412f17725807f85e4dc4169bd22dd59f0b23aba5251f5544478a1aabf5a2dea5260e6eb0c29af3a788208af85025a02f94950437d501b91fd55b9d97b3d6ac3af4e1161321f78b73ca0fda524dc3a5d03" }, "bots": [ { @@ -728,7 +728,7 @@ "url": "https://demo-api-dev.superalgos.org/", "container": "aavikings", "storage": "localStorage", - "accessKey": "" + "accessKey": "0f38fea438be5adfc5873228f503c55353fb334406128b16a958851b8e47c118a08f0105dd8985d5479758fc4cc3f56c2e2364c2d7c3d0fdffba8d7e9356056a34629e3b926b1ebb940122d973db30b04fc18fab542cbc1afdc032dccf64b0fe308ee6bbc74f82bd350aaebf723428a50d8a699fa2b4b96d58716986ed24069f" }, "bots": [ { @@ -834,8 +834,8 @@ "host": { "url": "https://demo-api-dev.superalgos.org/", "storage": "localStorage", - "container": "aaplatform", - "accessKey": "" + "container": "aamasters", + "accessKey": "09e890b76767ede3e10f0526b5036600a6b8d3faac540336e12b493fdb2aa9ed46f86094c34652f99fb6322e5f09806412f17725807f85e4dc4169bd22dd59f0b23aba5251f5544478a1aabf5a2dea5260e6eb0c29af3a788208af85025a02f94950437d501b91fd55b9d97b3d6ac3af4e1161321f78b73ca0fda524dc3a5d03" }, "competitions": [ { diff --git a/src/models/ecosystem.js b/src/models/ecosystem.js index bcaf36a46..e50759767 100644 --- a/src/models/ecosystem.js +++ b/src/models/ecosystem.js @@ -31,6 +31,10 @@ const ecosystemSchema = new Schema({ accessKey: { type: String, required: false + }, + ownerKey: { + type: String, + required: false } }, bots: [{ diff --git a/src/schema/mutations/AddTeam.js b/src/schema/mutations/AddTeam.js index 94f557eb6..995f87c6e 100644 --- a/src/schema/mutations/AddTeam.js +++ b/src/schema/mutations/AddTeam.js @@ -6,6 +6,7 @@ import { TeamInputType } from '../types/input' import { copyBot } from '../../storage/CopyBot' import { copySimulator } from '../../storage/CopySimulator' import { createContainer, writeFileContent } from '../../storage/providers/MinioStorage' +import crypto from 'crypto' export const args = { team: { type: TeamInputType } } @@ -51,7 +52,8 @@ const resolve = async (parent, { team }, context) => { url: process.env.NODE_ENDPOINT, storage: 'localStorage', container: team.codeName, - accessKey: '', + accessKey: crypto.randomBytes(128).toString('hex'), + ownerKey: crypto.randomBytes(128).toString('hex') } team.host = nodeEndpoint } diff --git a/src/schema/mutations/CreateFile.js b/src/schema/mutations/CreateFile.js index a1dcdcb6b..0911543cb 100644 --- a/src/schema/mutations/CreateFile.js +++ b/src/schema/mutations/CreateFile.js @@ -2,13 +2,24 @@ import logger from '../../utils/logger' import { FileInputType } from '../types/input' import { GraphQLString } from 'graphql' import { writeFileContent } from '../../storage/providers/MinioStorage' +import { AuthenticationError, WrongArgumentsError } from '../../errors' +import getDevTeamHost from '../../utils/getDevTeamHost' export const args = { file: { type: FileInputType } } const resolve = async (parent, { file }, context) => { logger.debug('createFile -> Entering Function: ' + file.container + '/' + file.filePath) try { - //TODO Add permissions + if (file.accessKey === undefined || file.accessKey === null) { + throw new WrongArgumentsError() + } + + let host = await getDevTeamHost(file.container, undefined, file.accessKey) + + if (host === undefined || host === null) { + throw new AuthenticationError() + } + await writeFileContent(file.container, file.filePath, file.fileContent) return 'File created.' } catch (err) { diff --git a/src/schema/queries/FileContent.js b/src/schema/queries/FileContent.js index 2b328b154..6856b6f93 100644 --- a/src/schema/queries/FileContent.js +++ b/src/schema/queries/FileContent.js @@ -3,6 +3,8 @@ import { FileInputType } from '../types/input' import { GraphQLString } from 'graphql' import { getFileContent } from '../../storage/providers/MinioStorage' import { getFileContentRemote } from '../../storage/providers/AzureStorage' +import { AuthenticationError, WrongArgumentsError } from '../../errors' +import getDevTeamHost from '../../utils/getDevTeamHost' export const args = { file: { type: FileInputType } } @@ -10,10 +12,18 @@ const resolve = async (parent, { file }, context) => { logger.debug('getFileContent -> Entering Function: ' + file.container + '/' + file.filePath) try { + if (file.accessKey === undefined || file.accessKey === null) { + throw new WrongArgumentsError() + } + + let host = await getDevTeamHost(file.container, file.accessKey) + + if (host === undefined || host === null) { + throw new AuthenticationError() + } let fileContent if (file.storage === 'localStorage') { - //TODO Add permissions fileContent = await getFileContent(file.container, file.filePath) } else { fileContent = await getFileContentRemote(file.container, file.filePath, file.storage, file.accessKey) diff --git a/src/schema/types/Host.js b/src/schema/types/Host.js index fbc45bcd1..0b36b8e00 100644 --- a/src/schema/types/Host.js +++ b/src/schema/types/Host.js @@ -9,7 +9,8 @@ const HostType = new GraphQLObjectType({ url: { type: GraphQLString }, storage: { type: GraphQLString }, container: { type: GraphQLString }, - accessKey: { type: GraphQLString } + accessKey: { type: GraphQLString }, + ownerKey: { type: GraphQLString } }), }); diff --git a/src/utils/getDevTeamHost.js b/src/utils/getDevTeamHost.js new file mode 100644 index 000000000..c244cf8e1 --- /dev/null +++ b/src/utils/getDevTeamHost.js @@ -0,0 +1,34 @@ +import { Ecosystem } from '../models' + +let teamCache = new Map() +export default async function getDevTeamHost(devTeamName, accessKey, ownerKey) { + let host + if (accessKey) { + host = teamCache.get(devTeamName + '.accessKey') + } else { + host = teamCache.get(devTeamName + '.ownerKey') + } + if (host) { + if (host.accessKey === accessKey || host.ownerKey === ownerKey) { + return host + } + } else { + let ecosystem = await Ecosystem.find() + + for (var i = 0; i < ecosystem.length; i++) { + for (var j = 0; j < ecosystem[i].devTeams.length; j++) { + if (ecosystem[i].devTeams[j].codeName.toLowerCase() === devTeamName.toLowerCase()) { + if (ecosystem[i].devTeams[j].host.accessKey === accessKey) { + teamCache.set(devTeamName + '.accessKey', ecosystem[i].devTeams[j].host) + return ecosystem[i].devTeams[j].host + } + if (ecosystem[i].devTeams[j].host.ownerKey === ownerKey) { + teamCache.set(devTeamName + '.ownerKey', ecosystem[i].devTeams[j].host) + return ecosystem[i].devTeams[j].host + } + } + } + } + + } +} From 9ac27a7fc58bbb7b044d38f1d7bd8b08e922bd14 Mon Sep 17 00:00:00 2001 From: Matias Date: Tue, 9 Jul 2019 22:49:37 -0300 Subject: [PATCH 3/5] New bot template --- src/storage/templates/TradingBot/User.Bot.js | 81 +++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/src/storage/templates/TradingBot/User.Bot.js b/src/storage/templates/TradingBot/User.Bot.js index 7cb73e57d..c8fc78f3a 100644 --- a/src/storage/templates/TradingBot/User.Bot.js +++ b/src/storage/templates/TradingBot/User.Bot.js @@ -1,4 +1,4 @@ -exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { +exports.newUserBot = function newUserBot(bot, logger) { // Variable used for logs, which will be passed to the logger instance const MODULE_NAME = 'User Bot' @@ -45,9 +45,12 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { let key = bot.devTeam + '-simulator-' + bot.codeName + '-Trading-Simulation-' + bot.dataSet + '-dataSet.V1' fileStorage = assistant.dataDependencies.dataSets.get(key) - if (fileStorage !== undefined){ + if (fileStorage !== undefined) { + assistant.rememberThis('lastSimulatorEngineMessageId', 0) + assistant.rememberThis('lastStopLoss', 0) + assistant.rememberThis('lastTakeProfit', 0) callBackFunction(global.DEFAULT_OK_RESPONSE) - }else{ + } else { logError('initialize -> Failed to initialize storage. Key not found:' + key) callBackFunction(global.DEFAULT_FAIL_RESPONSE) } @@ -96,18 +99,22 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { let simulatorEngineMessage = getSimulatorEngineMessageFromFile(indicatorFileContent) let autopilotResponse = await getAutopilot() + let stopLoss, takeProfit if (simulatorEngineMessage === undefined) { - logWarn('start -> Indicator message not found.') - throw global.DEFAULT_RETRY_RESPONSE + logWarn('start -> Simulator message not available. Will proceed to check SL and TP from previous execution.') + stopLoss = assistant.remindMeOf('lastStopLoss') + takeProfit = assistant.remindMeOf('lastTakeProfit') + } else { + logInfo('Processing simulator record: ' + JSON.stringify(simulatorEngineMessage)) + stopLoss = simulatorEngineMessage.order.stop + takeProfit = simulatorEngineMessage.order.takeProfit } - logInfo('Processing indicator message: ' + JSON.stringify(simulatorEngineMessage)) - // Checking Stop Loss let assetABalance = assistant.getAvailableBalance().assetA let currentRate = assistant.getMarketRate() - if (simulatorEngineMessage !== undefined && assetABalance > 0 && currentRate >= simulatorEngineMessage.order.stop) { + if (stopLoss > 0 && assetABalance > 0 && currentRate >= stopLoss) { logInfo('Closing trade with Stop Loss.') try { let position = await createBuyPosition(currentRate) @@ -116,8 +123,8 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { simulatorExecutorMessage.id = position.id simulatorExecutorMessage.order.id = position.id simulatorExecutorMessage.order.rate = currentRate - simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop - simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit + simulatorExecutorMessage.order.stop = stopLoss + simulatorExecutorMessage.order.takeProfit = takeProfit simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy simulatorExecutorMessage.order.size = assetABalance simulatorExecutorMessage.order.status = ORDER_STATUS.Placed @@ -125,13 +132,16 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { let message = createMessageFromObject(simulatorExecutorMessage) assistant.addExtraData(message) + assistant.rememberThis('lastSimulatorEngineMessageId', 0) + assistant.rememberThis('lastStopLoss', 0) + assistant.rememberThis('lastTakeProfit', 0) } catch (error) { let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.order.rate = position.rate - simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop - simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit + simulatorExecutorMessage.order.stop = stopLoss + simulatorExecutorMessage.order.takeProfit = takeProfit simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy simulatorExecutorMessage.order.size = assetABalance simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected @@ -144,8 +154,8 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { } // Checking Take Profits - if (simulatorEngineMessage !== undefined && assetABalance > 0 && currentRate <= simulatorEngineMessage.order.takeProfit) { - logInfo('Closing trade with Take Profits.') + if (takeProfit > 0 && assetABalance > 0 && currentRate <= takeProfit) { + logInfo('Closing trade with Take Profit.') try { let position = await createBuyPosition(currentRate, assetABalance) @@ -153,8 +163,8 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { simulatorExecutorMessage.id = position.id simulatorExecutorMessage.order.id = position.id simulatorExecutorMessage.order.rate = currentRate - simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop - simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit + simulatorExecutorMessage.order.stop = stopLoss + simulatorExecutorMessage.order.takeProfit = takeProfit simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy simulatorExecutorMessage.order.size = assetABalance simulatorExecutorMessage.order.status = ORDER_STATUS.Placed @@ -162,13 +172,16 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { let message = createMessageFromObject(simulatorExecutorMessage) assistant.addExtraData(message) + assistant.rememberThis('lastSimulatorEngineMessageId', 0) + assistant.rememberThis('lastStopLoss', 0) + assistant.rememberThis('lastTakeProfit', 0) } catch (error) { let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.order.rate = currentRate - simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop - simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit + simulatorExecutorMessage.order.stop = stopLoss + simulatorExecutorMessage.order.takeProfit = takeProfit simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy simulatorExecutorMessage.order.size = assetABalance simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected @@ -180,13 +193,16 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { } } - if (autopilotResponse.autopilot || autopilotControl) { - await manageCloneInAutopilotOn(simulatorEngineMessage) - } else { - await manageCloneInAutopilotOff(simulatorEngineMessage) + if (simulatorEngineMessage !== undefined) { + if (autopilotResponse.autopilot || autopilotControl) { + await manageCloneInAutopilotOn(simulatorEngineMessage) + } else { + await manageCloneInAutopilotOff(simulatorEngineMessage) + } } } catch (error) { - if (error.stack) logError(JSON.stringify(error.stack)) + logError('Error on executorLogic: ' + error) + logError('Stack: ' + JSON.stringify(error.stack)) throw error } } @@ -198,6 +214,12 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { if (simulatorEngineMessage.messageType === MESSAGE_TYPE.Order && simulatorEngineMessage.order.direction === ORDER_DIRECTION.Sell) { + let lastSimulatorEngineMessageId = assistant.remindMeOf('lastSimulatorEngineMessageId') + if (lastSimulatorEngineMessageId >= simulatorEngineMessage.id) { + logInfo("manageCloneInAutopilotOn -> Order message was previously processed.") + return + } + try { let position = await createSellPosition(currentRate, assetBBalance) let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() @@ -211,6 +233,9 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { let message = createMessageFromObject(simulatorExecutorMessage) assistant.addExtraData(message) + assistant.rememberThis('lastSimulatorEngineMessageId', parseInt(simulatorEngineMessage.id)) + assistant.rememberThis('lastStopLoss', simulatorEngineMessage.order.stop) + assistant.rememberThis('lastTakeProfit', simulatorEngineMessage.order.takeProfit) } catch (error) { let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant @@ -542,12 +567,12 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { logWarn('getSimulatorEngineMessageFromFile -> The indicator message was not found at time: ' + bot.processDatetime.valueOf()) } else { - // Running live we will process last available Indicator Message only if it's delayed 25 minutes top - let maxTolerance = 120 * 60 * 1000 + // Running live we will process last available Indicator Message only if it's delayed 10 minutes top + let maxTolerance = 10 * 60 * 1000 if (bot.processDatetime.valueOf() <= (lastAvailableDateTime + maxTolerance)) { return getMessage(indicatorFileContent[lastIndexIndicatorFile][25]) } else { - logWarn('getSimulatorEngineMessageFromFile -> Last available indicator older than 2 hours. Skipping execution.') + logWarn('getSimulatorEngineMessageFromFile -> Last available indicator older than 10 minutes.') } } } catch (error) { @@ -720,7 +745,9 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) { fileStorage.getTextFile[util.promisify.custom] = n => new Promise((resolve, reject) => { fileStorage.getTextFile(containerName, blobName, (result, fileContent) => { if (result !== global.DEFAULT_OK_RESPONSE) { - console.log('[WARN] getFileContent -> The indicator dependency is not ready. Will retry later.') + let errorMessage = '[WARN] getFileContent -> The indicator dependency is not ready. Will retry later.' + console.log(errorMessage) + logWarn(errorMessage) reject(global.DEFAULT_RETRY_RESPONSE) } else { resolve(fileContent) From 0bd6299d176390c4ef0d29923b82e3642403b854 Mon Sep 17 00:00:00 2001 From: Matias Date: Tue, 9 Jul 2019 22:50:42 -0300 Subject: [PATCH 4/5] Adding log info for performance check --- src/schema/mutations/CreateFile.js | 5 +++-- src/schema/queries/FileContent.js | 16 +++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/schema/mutations/CreateFile.js b/src/schema/mutations/CreateFile.js index 0911543cb..b1e501e80 100644 --- a/src/schema/mutations/CreateFile.js +++ b/src/schema/mutations/CreateFile.js @@ -8,7 +8,6 @@ import getDevTeamHost from '../../utils/getDevTeamHost' export const args = { file: { type: FileInputType } } const resolve = async (parent, { file }, context) => { - logger.debug('createFile -> Entering Function: ' + file.container + '/' + file.filePath) try { if (file.accessKey === undefined || file.accessKey === null) { throw new WrongArgumentsError() @@ -19,8 +18,10 @@ const resolve = async (parent, { file }, context) => { if (host === undefined || host === null) { throw new AuthenticationError() } - + let date = new Date() await writeFileContent(file.container, file.filePath, file.fileContent) + let dateAfter = new Date() + logger.info('createFile: ' + file.container + '/...' + file.filePath.substring(file.filePath.length -110 , file.filePath.length) + ', ' + (dateAfter - date)) return 'File created.' } catch (err) { logger.error('createFile -> Error: %s', err.message) diff --git a/src/schema/queries/FileContent.js b/src/schema/queries/FileContent.js index 6856b6f93..63a9164a8 100644 --- a/src/schema/queries/FileContent.js +++ b/src/schema/queries/FileContent.js @@ -2,15 +2,12 @@ import logger from '../../utils/logger' import { FileInputType } from '../types/input' import { GraphQLString } from 'graphql' import { getFileContent } from '../../storage/providers/MinioStorage' -import { getFileContentRemote } from '../../storage/providers/AzureStorage' import { AuthenticationError, WrongArgumentsError } from '../../errors' import getDevTeamHost from '../../utils/getDevTeamHost' export const args = { file: { type: FileInputType } } const resolve = async (parent, { file }, context) => { - logger.debug('getFileContent -> Entering Function: ' + file.container + '/' + file.filePath) - try { if (file.accessKey === undefined || file.accessKey === null) { throw new WrongArgumentsError() @@ -22,18 +19,15 @@ const resolve = async (parent, { file }, context) => { throw new AuthenticationError() } - let fileContent - if (file.storage === 'localStorage') { - fileContent = await getFileContent(file.container, file.filePath) - } else { - fileContent = await getFileContentRemote(file.container, file.filePath, file.storage, file.accessKey) - } - + let date = new Date() + let fileContent = await getFileContent(file.container, file.filePath) + let dateAfter = new Date() + logger.info('getFileContent: ' + file.container + '/...' + file.filePath.substring(file.filePath.length -110 , file.filePath.length) + ', ' + (dateAfter - date)) return fileContent } catch (err) { if (err.message === 'The specified key does not exist.') - logger.warn('getFileContent -> Error: %s', err.message) + logger.debug('getFileContent -> Error: %s', err.message) else { logger.error('getFileContent -> Error: %s', err.message) throw err From a4f4fef51f2f7598c01ed3c071193c63df45dc66 Mon Sep 17 00:00:00 2001 From: Matias Date: Thu, 11 Jul 2019 16:55:38 -0300 Subject: [PATCH 5/5] Updating templates --- src/schema/queries/FileContent.js | 1 + src/storage/templates/Simulator/Commons.js | 363 ++++++++++++------ .../templates/Simulator/User.Bot.Daily.js | 82 +++- .../templates/Simulator/User.Bot.Market.js | 77 +++- .../templates/Simulator/this.bot.config.json | 20 +- src/storage/templates/TradingBot/User.Bot.js | 120 ++++-- .../templates/TradingBot/this.bot.config.json | 102 +---- 7 files changed, 517 insertions(+), 248 deletions(-) diff --git a/src/schema/queries/FileContent.js b/src/schema/queries/FileContent.js index 63a9164a8..43bf10c3d 100644 --- a/src/schema/queries/FileContent.js +++ b/src/schema/queries/FileContent.js @@ -16,6 +16,7 @@ const resolve = async (parent, { file }, context) => { let host = await getDevTeamHost(file.container, file.accessKey) if (host === undefined || host === null) { + logger.error('Error getting file:' + JSON.stringify(file)) throw new AuthenticationError() } diff --git a/src/storage/templates/Simulator/Commons.js b/src/storage/templates/Simulator/Commons.js index f96787ff9..cb6a0f0e8 100644 --- a/src/storage/templates/Simulator/Commons.js +++ b/src/storage/templates/Simulator/Commons.js @@ -65,6 +65,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> Entering function."); } + let executionArray = []; let recordsArray = []; let conditionsArray = []; let strategiesArray = []; @@ -183,8 +184,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { /* Simulation Records */ - let positionRate = 0; - let positionSize = 0; + let tradePositionRate = 0; + let tradePositionSize = 0; let positionInstant; let previousBalanceAssetA = 0; @@ -341,36 +342,56 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { for (let i = 0; i < candles.length; i++) { - /* Update all the data objects available for the simulation. */ let candle = candles[i]; let percentageBandwidth = percentageBandwidthMap.get(candle.begin); let bollingerBand = bollingerBandsMap.get(candle.begin); + let bollingerChannel = getElement(bollingerChannelsArray, candle.begin, candle.end); + let bollingerSubChannel = getElement(bollingerSubChannelsArray, candle.begin, candle.end); + //let LRC = LRCMap.get(candle.begin); - //if (LRC === undefined) { continue; } - if (percentageBandwidth === undefined) { continue; } // percentageBandwidth might start after the first few candles. + /* If any of the needed indicators is missing, then that period is not calculated */ + if (candle.begin < initialDate.valueOf()) { continue; } + if (bollingerBand === undefined) { continue; } + if (percentageBandwidth === undefined) { continue; } // percentageBandwidth might start after the first few candles. + if (bollingerChannel === undefined) { continue; } + if (bollingerSubChannel === undefined) { continue; } + + //if (LRC === undefined) { continue; } periods++; days = periods * timePeriod / ONE_DAY_IN_MILISECONDS; - if (currentDay !== undefined) { + if (currentDay !== undefined) { // This means that we are processing Daily Files if (candle.end < currentDay.valueOf()) { yesterday.Periods++; } - } - let channel = getElement(bollingerChannelsArray, candle.begin, candle.end); - let subChannel = getElement(bollingerSubChannelsArray, candle.begin, candle.end); + /* We skip the candle at the head of the market because i has not closed yet. */ + let candlesPerDay = ONE_DAY_IN_MILISECONDS / timePeriod + if (i === candles.length - 1) { + if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2) ) { + /*We are at the head of the market, thus we skip the last candle because it has not close yet. */ + continue; + /* Note here that in the last candle of the first day or the second day it will use an incomplete candle and partially calculated indicators. + if we skip these two periods, then there will be a hole in the file since the last period will be missing. */ + } + } + } else { // We are processing Market Files + if (i === candles.length - 1) { + continue; + } + } let lastObjects = { candle: clone(candle), //LRC: clone(LRC), bollingerBand: clone(bollingerBand), percentageBandwidth: clone(percentageBandwidth), - channel: clone(channel), - subChannel: clone(subChannel) + bollingerChannel: clone(bollingerChannel), + bollingerSubChannel: clone(bollingerSubChannel) } function clone(obj) { @@ -386,8 +407,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { //lastObjects.LRC.previous = undefined; lastObjects.bollingerBand.previous = undefined; lastObjects.percentageBandwidth.previous = undefined; - lastObjects.channel.previous = undefined; - lastObjects.subChannel.previous = undefined; + lastObjects.bollingerChannel.previous = undefined; + lastObjects.bollingerSubChannel.previous = undefined; lastObjectsArray.push(lastObjects); @@ -415,6 +436,11 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { let strategy = tradingSystem.strategies[j]; + let positionSize = 0 + let positionRate = 0 + + /* Continue with the rest of the formulas and conditions */ + let triggerStage = strategy.triggerStage if (triggerStage !== undefined) { @@ -478,10 +504,67 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (openStage !== undefined) { + /* Default Values*/ + if (baseAsset === 'BTC') { + positionSize = balanceAssetA; + positionRate = candle.close; + } else { + positionSize = balanceAssetB; + positionRate = candle.close; + } + let initialDefinition = openStage.initialDefinition if (initialDefinition !== undefined) { + if (tradePositionSize !== 0) { + positionSize = tradePositionSize + } else { + if (initialDefinition.positionSize !== undefined) { + if (initialDefinition.positionSize.formula !== undefined) { + try { + positionSize = eval(initialDefinition.positionSize.formula.code); + } catch (err) { + initialDefinition.positionSize.formula.error = err.message + } + if (isNaN(positionSize)) { + if (baseAsset === 'BTC') { + positionSize = balanceAssetA; + } else { + positionSize = balanceAssetB; + } + } else { + if (baseAsset === 'BTC') { + if (positionSize > balanceAssetA) { positionSize = balanceAssetA } + } else { + if (positionSize > balanceAssetB) { positionSize = balanceAssetB } + } + } + } + } + } + + if (tradePositionRate !== 0) { + positionRate = tradePositionRate + } else { + if (initialDefinition.positionRate !== undefined) { + if (initialDefinition.positionRate.formula !== undefined) { + try { + positionRate = eval(initialDefinition.positionRate.formula.code); + } catch (err) { + initialDefinition.positionRate.formula.error = err.message + } + if (isNaN(positionRate)) { + if (baseAsset === 'BTC') { + positionRate = candle.close; + } else { + positionRate = candle.close; + } + } + } + } + } + if (initialDefinition.stopLoss !== undefined) { for (let p = 0; p < initialDefinition.stopLoss.phases.length; p++) { @@ -500,7 +583,13 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { formulaValue = MAX_STOP_LOSS_VALUE } } catch (err) { - formulaError = err.message + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } } if (isNaN(formulaValue)) { formulaValue = 0; } if (formulaValue < MIN_STOP_LOSS_VALUE) { @@ -553,7 +642,13 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { formulaValue = MAX_TAKE_PROFIT_VALUE } } catch (err) { - formulaError = err.message + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } } if (isNaN(formulaValue)) { formulaValue = 0; } if (formulaValue < MIN_TAKE_PROFIT_VALUE) { @@ -588,6 +683,9 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } } + + strategy.positionSize = positionSize + strategy.positionRate = positionRate } let manageStage = strategy.manageStage @@ -612,7 +710,13 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { formulaValue = MAX_STOP_LOSS_VALUE } } catch (err) { - formulaError = err.message + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } } if (isNaN(formulaValue)) { formulaValue = 0; } if (formulaValue < MIN_STOP_LOSS_VALUE) { @@ -665,7 +769,13 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { formulaValue = MAX_TAKE_PROFIT_VALUE } } catch (err) { - formulaError = err.message + if (phase.formula.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + formulaError = err.message + } } if (isNaN(formulaValue)) { formulaValue = 0; } if (formulaValue < MIN_TAKE_PROFIT_VALUE) { @@ -700,39 +810,45 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } } - } - function newCondition(key, node) { + function newCondition(key, node) { - let condition; - let error = '' - let value + let condition; + let error = '' + let value - try { - value = eval(node.code); - } catch (err) { - /* - One possible error is that the conditions references a .previous that is undefined. For this - reason and others, we will simply set the value to false. - */ - value = false - node.error = err.message - } + try { + value = eval(node.code); + } catch (err) { + /* + One possible error is that the conditions references a .previous that is undefined. For this + reason and others, we will simply set the value to false. + */ + value = false - condition = { - key: key, - value: value - }; + if (node.code.indexOf('previous') > 0 && err.message.indexOf('of undefined') > 0) { + /* + We are not going to set an error for the casess we are using previous and the error is that the indicator is undefined. + */ + } else { + node.error = err.message + } + } - conditions.set(condition.key, condition); + condition = { + key: key, + value: value + }; - if (condition.value) { - conditionsArrayValues.push(1); - } else { - conditionsArrayValues.push(0); + conditions.set(condition.key, condition); + + if (condition.value) { + conditionsArrayValues.push(1); + } else { + conditionsArrayValues.push(0); + } } } - } /* Trigger On Conditions */ @@ -809,11 +925,12 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } } else { + let stopRunningDate = new Date(candle.begin) if (balance < minimumBalance) { - tradingSystem.error = "Balance below the minimum." + tradingSystem.error = "Min Balance @ " + stopRunningDate.toUTCString() } if (balance > maximumBalance) { - tradingSystem.error = "Balance above the maximum." + tradingSystem.error = "Max Balance @ " + stopRunningDate.toUTCString() } } } @@ -893,7 +1010,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (passed) { - type = '"Sell"'; + type = '"Take Position"'; strategyStage = 'Open Stage'; stopLossStage = 'Open Stage'; @@ -1153,7 +1270,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } marketRate = stopLoss; - type = '"Buy@StopLoss"'; + type = '"Close@StopLoss"'; strategyStage = 'Close Stage'; stopLossStage = 'No Stage'; takeProfitStage = 'No Stage'; @@ -1189,7 +1306,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } marketRate = takeProfit; - type = '"Buy@TakeProfit"'; + type = '"Close@TakeProfit"'; strategyStage = 'Close Stage'; stopLossStage = 'No Stage'; takeProfitStage = 'No Stage'; @@ -1211,44 +1328,24 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { ) { takePositionNow = false - /* positionSize default is the whole balance if no size was defined by the user */ + /* tradePositionSize default is the whole balance if no size was defined by the user */ if (baseAsset === 'BTC') { - positionSize = balanceAssetA; + tradePositionSize = balanceAssetA; } else { - positionSize = balanceAssetB; + tradePositionSize = balanceAssetB; } + tradePositionRate = candle.close - /* Check if the user defined a position size */ + /* Check if the user defined a position size or rate */ let strategy = tradingSystem.strategies[currentStrategyIndex]; - let triggerStage = strategy.triggerStage - if (triggerStage !== undefined) { - if (triggerStage.positionSize !== undefined) { - if (triggerStage.positionSize.formula !== undefined) { - try { - positionSize = eval(triggerStage.positionSize.formula.code); - } catch (err) { - triggerStage.positionSize.formula.error = err.message - } - if (isNaN(positionSize)) { - if (baseAsset === 'BTC') { - positionSize = balanceAssetA; - } else { - positionSize = balanceAssetB; - } - } else { - if (baseAsset === 'BTC') { - if (positionSize > balanceAssetA) { positionSize = balanceAssetA} - } else { - if (positionSize > balanceAssetB) { positionSize = balanceAssetB} - } - } - } - } - } + tradePositionSize = strategy.positionSize; + tradePositionRate = strategy.positionRate; + + calculateTakeProfit(); + calculateStopLoss(); marketRate = candle.close; - positionRate = candle.close; previousBalanceAssetA = balanceAssetA; previousBalanceAssetB = balanceAssetB; @@ -1257,11 +1354,11 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { lastTradeROI = 0; if (baseAsset === 'BTC') { - balanceAssetB = balanceAssetB + positionSize * positionRate; - balanceAssetA = balanceAssetA - positionSize; + balanceAssetB = balanceAssetB + tradePositionSize * tradePositionRate; + balanceAssetA = balanceAssetA - tradePositionSize; } else { - balanceAssetA = balanceAssetA + positionSize / positionRate; - balanceAssetB = balanceAssetB - positionSize; + balanceAssetA = balanceAssetA + tradePositionSize / tradePositionRate; + balanceAssetB = balanceAssetB - tradePositionSize; } positionInstant = candle.end; @@ -1293,12 +1390,12 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (baseAsset === 'BTC') { lastTradeProfitLoss = balanceAssetA - previousBalanceAssetA; - lastTradeROI = lastTradeProfitLoss * 100 / positionSize; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; if (isNaN(lastTradeROI)) { lastTradeROI = 0; } profit = balanceAssetA - initialBalanceA; } else { lastTradeProfitLoss = balanceAssetB - previousBalanceAssetB; - lastTradeROI = lastTradeProfitLoss * 100 / positionSize; + lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; if (isNaN(lastTradeROI)) { lastTradeROI = 0; } profit = balanceAssetB - initialBalanceB; } @@ -1356,8 +1453,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { currentStrategyIndex = -1; stopLoss = 0; - positionRate = 0; - positionSize = 0; + tradePositionRate = 0; + tradePositionSize = 0; positionInstant = undefined; takeProfit = 0; strategyStage = 'No Stage'; @@ -1380,21 +1477,33 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { let messageType; let message; let simulationRecord; - let orderRecord; + let executionRecord; + let executionMessage; messageId++; - if (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage') { + if (strategyStage === 'Open Stage' || strategyStage === 'Manage Stage' || type === '"Close@TakeProfit"' || type === '"Close@StopLoss"') { - if (strategyStage === 'Open Stage') { + if (type === '"Take Position"') { messageType = MESSAGE_TYPE.Order; orderId++; - } - if (strategyStage === 'Manage Stage') { - messageType = MESSAGE_TYPE.OrderUpdate; + } else { + if (type === '"Close@TakeProfit"' || type === '"Close@StopLoss"') { + messageType = MESSAGE_TYPE.OrderClose; + } else { + messageType = MESSAGE_TYPE.OrderUpdate; + } } - orderRecord = createMessage( + let exitOutcome = "" + if (messageType === MESSAGE_TYPE.OrderClose && type === '"Close@TakeProfit"') { + exitOutcome = "TP" + } + if (messageType === MESSAGE_TYPE.OrderClose && type === '"Close@StopLoss"') { + exitOutcome = "SL" + } + + executionMessage = createMessage( messageId, MESSAGE_ENTITY.SimulationEngine, MESSAGE_ENTITY.SimulationExecutor, @@ -1408,19 +1517,19 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { "BTC_USDT", 0, ORDER_TYPE.Limit, - marketRate, + tradePositionRate, stopLoss, takeProfit, ORDER_DIRECTION.Sell, -1, ORDER_STATUS.Signaled, 0, - "") + exitOutcome) } else { - orderRecord = createMessage( + executionMessage = createMessage( messageId, MESSAGE_ENTITY.SimulationEngine, MESSAGE_ENTITY.SimulationExecutor, @@ -1444,6 +1553,14 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { "") } + executionRecord = { + begin: candle.begin, + end: candle.end, + executionRecord: executionMessage + } + + executionArray.push(executionRecord) + let strategyStageNumber switch (strategyStage) { case 'No Stage': { @@ -1497,15 +1614,15 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { periods: periods, days: days, anualizedRateOfReturn: anualizedRateOfReturn, - positionRate: positionRate, + positionRate: tradePositionRate, lastTradeROI: lastTradeROI, strategy: currentStrategyIndex, strategyStageNumber: strategyStageNumber, takeProfit: takeProfit, stopLossPhase: stopLossPhase, takeProfitPhase: takeProfitPhase, - orderRecord: orderRecord, - positionSize: positionSize, + executionRecord: executionMessage, + positionSize: tradePositionSize, initialBalanceA: initialBalanceA, minimumBalanceA: minimumBalanceA, maximumBalanceA: maximumBalanceA, @@ -1605,7 +1722,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> callback -> recordsArray.length = " + recordsArray.length); } - callback(tradingSystem, recordsArray, conditionsArray, strategiesArray, tradesArray, lastObjectsArray); + callback(tradingSystem, executionArray, recordsArray, conditionsArray, strategiesArray, tradesArray, lastObjectsArray); function getElement(pArray, begin, end) { @@ -1620,11 +1737,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } - element = { - direction: 'unknown', - slope: 'unknown' - }; - return element; + return } } catch (err) { @@ -1653,17 +1766,17 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (previous !== undefined) { - if (previous._15 > LRC._15) { LRC.direction15 = 'down'; } - if (previous._15 < LRC._15) { LRC.direction15 = 'up'; } - if (previous._15 === LRC._15) { LRC.direction15 = 'side'; } + if (previous._15 > LRC._15) { LRC.direction15 = 'Down'; } + if (previous._15 < LRC._15) { LRC.direction15 = 'Up'; } + if (previous._15 === LRC._15) { LRC.direction15 = 'Side'; } - if (previous._30 > LRC._30) { LRC.direction30 = 'down'; } - if (previous._30 < LRC._30) { LRC.direction30 = 'up'; } - if (previous._30 === LRC._30) { LRC.direction30 = 'side'; } + if (previous._30 > LRC._30) { LRC.direction30 = 'Down'; } + if (previous._30 < LRC._30) { LRC.direction30 = 'Up'; } + if (previous._30 === LRC._30) { LRC.direction30 = 'Side'; } - if (previous._60 > LRC._60) { LRC.direction60 = 'down'; } - if (previous._60 < LRC._60) { LRC.direction60 = 'up'; } - if (previous._60 === LRC._60) { LRC.direction60 = 'side'; } + if (previous._60 > LRC._60) { LRC.direction60 = 'Down'; } + if (previous._60 < LRC._60) { LRC.direction60 = 'Up'; } + if (previous._60 === LRC._60) { LRC.direction60 = 'Side'; } } @@ -1700,9 +1813,9 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (previous !== undefined) { - if (previous.movingAverage > percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'down'; } - if (previous.movingAverage < percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'up'; } - if (previous.movingAverage === percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'side'; } + if (previous.movingAverage > percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'Down'; } + if (previous.movingAverage < percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'Up'; } + if (previous.movingAverage === percentageBandwidth.movingAverage) { percentageBandwidth.direction = 'Side'; } } @@ -1739,9 +1852,9 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (previous !== undefined) { - if (previous.movingAverage > bollingerBand.movingAverage) { bollingerBand.direction = 'down'; } - if (previous.movingAverage < bollingerBand.movingAverage) { bollingerBand.direction = 'up'; } - if (previous.movingAverage === bollingerBand.movingAverage) { bollingerBand.direction = 'side'; } + if (previous.movingAverage > bollingerBand.movingAverage) { bollingerBand.direction = 'Down'; } + if (previous.movingAverage < bollingerBand.movingAverage) { bollingerBand.direction = 'Up'; } + if (previous.movingAverage === bollingerBand.movingAverage) { bollingerBand.direction = 'Side'; } } @@ -1772,11 +1885,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { begin: dataFile[i][0], end: dataFile[i][1], direction: dataFile[i][2], - period: dataFile[i][3], - firstMovingAverage: dataFile[i][4], - lastMovingAverage: dataFile[i][5], - firstDeviation: dataFile[i][6], - lastDeviation: dataFile[i][7] + period: dataFile[i][3] }; bollingerChannel.previous = previous; @@ -1856,9 +1965,9 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { candle.begin = dataFile[i][4]; candle.end = dataFile[i][5]; - if (candle.open > candle.close) { candle.direction = 'down'; } - if (candle.open < candle.close) { candle.direction = 'up'; } - if (candle.open === candle.close) { candle.direction = 'side'; } + if (candle.open > candle.close) { candle.direction = 'Down'; } + if (candle.open < candle.close) { candle.direction = 'Up'; } + if (candle.open === candle.close) { candle.direction = 'Side'; } candle.previous = previous; diff --git a/src/storage/templates/Simulator/User.Bot.Daily.js b/src/storage/templates/Simulator/User.Bot.Daily.js index 8cc5e30d0..ef105c9a6 100644 --- a/src/storage/templates/Simulator/User.Bot.Daily.js +++ b/src/storage/templates/Simulator/User.Bot.Daily.js @@ -5,6 +5,7 @@ const MODULE_NAME = "User Bot"; + const EXECUTION_FOLDER_NAME = "Trading-Execution"; const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation"; const CONDITIONS_FOLDER_NAME = "Simulation-Conditions"; const STRATEGIES_FOLDER_NAME = "Simulation-Strategies"; @@ -104,16 +105,91 @@ writeFiles, callBackFunction) - function writeFiles(pTradingSystem, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray, pLastObjectsArray) { + function writeFiles(pTradingSystem, pExecutionArray, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray, pLastObjectsArray) { tradingSystem = pTradingSystem + executionArray = pExecutionArray recordsArray = pRecordsArray conditionsArray = pConditionsArray strategiesArray = pStrategiesArray tradesArray = pTradesArray lastObjectsArray = pLastObjectsArray - writeRecordsFile() + writeExecutionFile() + } + + function writeExecutionFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < executionArray.length; i++) { + + let record = executionArray[i]; + + /* Will only add to the file the records of the current day */ + + if (record.begin < currentDay.valueOf()) { continue; } + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + JSON.stringify(record.executionRecord) + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2); + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + EXECUTION_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeRecordsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } } function writeRecordsFile() { @@ -161,7 +237,7 @@ record.takeProfit + "," + record.stopLossPhase + "," + record.takeProfitPhase + "," + - JSON.stringify(record.orderRecord) + "," + + JSON.stringify(record.executionRecord) + "," + record.positionSize + "," + record.initialBalanceA + "," + record.minimumBalanceA + "," + diff --git a/src/storage/templates/Simulator/User.Bot.Market.js b/src/storage/templates/Simulator/User.Bot.Market.js index 80afe2836..a4dc38283 100644 --- a/src/storage/templates/Simulator/User.Bot.Market.js +++ b/src/storage/templates/Simulator/User.Bot.Market.js @@ -5,6 +5,7 @@ const MODULE_NAME = "User Bot"; + const EXECUTION_FOLDER_NAME = "Trading-Execution"; const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation"; const CONDITIONS_FOLDER_NAME = "Simulation-Conditions"; const STRATEGIES_FOLDER_NAME = "Simulation-Strategies"; @@ -104,16 +105,86 @@ writeFiles, callBackFunction) - function writeFiles(pTradingSystem, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray, pLastObjectsArray) { + function writeFiles(pTradingSystem, pExecutionArray, pRecordsArray, pConditionsArray, pStrategiesArray, pTradesArray, pLastObjectsArray) { tradingSystem = pTradingSystem + executionArray = pExecutionArray recordsArray = pRecordsArray conditionsArray = pConditionsArray strategiesArray = pStrategiesArray tradesArray = pTradesArray lastObjectsArray = pLastObjectsArray - writeRecordsFile() + writeExecutionFile() + } + + function writeExecutionFile() { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> Entering function."); } + + let separator = ""; + let fileRecordCounter = 0; + + let fileContent = ""; + + for (let i = 0; i < executionArray.length; i++) { + + let record = executionArray[i]; + + fileContent = fileContent + separator + '[' + + record.begin + "," + + record.end + "," + + JSON.stringify(record.executionRecord) + "]"; + + if (separator === "") { separator = ","; } + + fileRecordCounter++; + + } + + fileContent = "[" + fileContent + "]"; + + let fileName = '' + market.assetA + '_' + market.assetB + '.json'; + + let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion; + let filePath = filePathRoot + "/Output/" + EXECUTION_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel; + filePath += '/' + fileName + + fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> onFileCreated -> Entering function."); } + if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeExecutionFile -> onFileCreated -> fileContent = " + fileContent); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> filePath = " + filePath); + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB); + + callBackFunction(err); + return; + + } + + writeRecordsFile(); + + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> onFileCreated -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> writeExecutionFile -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } } function writeRecordsFile() { @@ -157,7 +228,7 @@ record.takeProfit + "," + record.stopLossPhase + "," + record.takeProfitPhase + "," + - JSON.stringify(record.orderRecord) + "," + + JSON.stringify(record.executionRecord) + "," + record.positionSize + "," + record.initialBalanceA + "," + record.minimumBalanceA + "," + diff --git a/src/storage/templates/Simulator/this.bot.config.json b/src/storage/templates/Simulator/this.bot.config.json index d0d8572de..15b52e5e1 100644 --- a/src/storage/templates/Simulator/this.bot.config.json +++ b/src/storage/templates/Simulator/this.bot.config.json @@ -79,7 +79,7 @@ "major": 1, "minor": 0 }, - "product": "Bollinger-Channels", + "product": "Bollinger-Standard-Channels", "dataSetVersion": "dataSet.V1", "dataSet": "Multi-Period-Market" }, @@ -90,7 +90,7 @@ "major": 1, "minor": 0 }, - "product": "Bollinger-Sub-Channels", + "product": "Bollinger-Standard-Sub-Channels", "dataSetVersion": "dataSet.V1", "dataSet": "Multi-Period-Market" }, @@ -166,7 +166,7 @@ }, "product": "LRC-Points", "dataSetVersion": "dataSet.V1", - "dataSet": "Multi-Period-Market" + "dataSet": "Multi-Period-Daily" }, { "devTeam": "AAMasters", @@ -177,7 +177,7 @@ }, "product": "Percentage-Bandwidth", "dataSetVersion": "dataSet.V1", - "dataSet": "Multi-Period-Market" + "dataSet": "Multi-Period-Daily" }, { "devTeam": "AAMasters", @@ -188,7 +188,7 @@ }, "product": "Bollinger-Bands", "dataSetVersion": "dataSet.V1", - "dataSet": "Multi-Period-Market" + "dataSet": "Multi-Period-Daily" }, { "devTeam": "AAMasters", @@ -197,9 +197,9 @@ "major": 1, "minor": 0 }, - "product": "Bollinger-Channels", + "product": "Bollinger-Standard-Channels", "dataSetVersion": "dataSet.V1", - "dataSet": "Multi-Period-Market" + "dataSet": "Multi-Period-Daily" }, { "devTeam": "AAMasters", @@ -208,9 +208,9 @@ "major": 1, "minor": 0 }, - "product": "Bollinger-Sub-Channels", + "product": "Bollinger-Standard-Sub-Channels", "dataSetVersion": "dataSet.V1", - "dataSet": "Multi-Period-Market" + "dataSet": "Multi-Period-Daily" }, { "devTeam": "AAMasters", @@ -221,7 +221,7 @@ }, "product": "Candles", "dataSetVersion": "dataSet.V1", - "dataSet": "Multi-Period-Market" + "dataSet": "Multi-Period-Daily" } ] } diff --git a/src/storage/templates/TradingBot/User.Bot.js b/src/storage/templates/TradingBot/User.Bot.js index c8fc78f3a..d1a4d4820 100644 --- a/src/storage/templates/TradingBot/User.Bot.js +++ b/src/storage/templates/TradingBot/User.Bot.js @@ -42,13 +42,10 @@ exports.newUserBot = function newUserBot(bot, logger) { throw new Error("Execution Parameter Data Set not defined.") } - let key = bot.devTeam + '-simulator-' + bot.codeName + '-Trading-Simulation-' + bot.dataSet + '-dataSet.V1' + let key = bot.devTeam + '-simulator-' + bot.codeName + '-Trading-Execution-' + bot.dataSet + '-dataSet.V1' fileStorage = assistant.dataDependencies.dataSets.get(key) if (fileStorage !== undefined) { - assistant.rememberThis('lastSimulatorEngineMessageId', 0) - assistant.rememberThis('lastStopLoss', 0) - assistant.rememberThis('lastTakeProfit', 0) callBackFunction(global.DEFAULT_OK_RESPONSE) } else { logError('initialize -> Failed to initialize storage. Key not found:' + key) @@ -93,21 +90,72 @@ exports.newUserBot = function newUserBot(bot, logger) { }) } + function getValueFromPreviousExecution(key) { + let value = assistant.remindMeOf(key) + if (value === undefined) + value = 0 + return value + } + async function executorLogic() { try { let indicatorFileContent = await getIndicatorFile() let simulatorEngineMessage = getSimulatorEngineMessageFromFile(indicatorFileContent) let autopilotResponse = await getAutopilot() - let stopLoss, takeProfit + let stopLoss, takeProfit, receivedMessageId if (simulatorEngineMessage === undefined) { logWarn('start -> Simulator message not available. Will proceed to check SL and TP from previous execution.') - stopLoss = assistant.remindMeOf('lastStopLoss') - takeProfit = assistant.remindMeOf('lastTakeProfit') + stopLoss = getValueFromPreviousExecution('lastStopLoss') + takeProfit = getValueFromPreviousExecution('lastTakeProfit') + receivedMessageId = 0 } else { logInfo('Processing simulator record: ' + JSON.stringify(simulatorEngineMessage)) - stopLoss = simulatorEngineMessage.order.stop - takeProfit = simulatorEngineMessage.order.takeProfit + receivedMessageId = simulatorEngineMessage.id + + // Verify if there is an inconsistent state between the simulator and executor + let isATradeOpen = (getValueFromPreviousExecution('lastSimulatorEngineMessageId') > 0) + let isMessageAHeartBeat = (simulatorEngineMessage.messageType === MESSAGE_TYPE.HeartBeat) + if (isATradeOpen && isMessageAHeartBeat) { + logWarn('An inconsisten state was detected: there is an open trade and a close trade was expected.') + let skippedMessageOrderClose = getSkippedMessageOrderClose(indicatorFileContent) + if (skippedMessageOrderClose === undefined) { + logWarn('There was not a Skipped Message Order Close. Closing the position the best possible.') + stopLoss = assistant.getMarketRate() + takeProfit = assistant.getMarketRate() + } else { + let lastSimulatorEngineMessageId = getValueFromPreviousExecution('lastSimulatorEngineMessageId') + if (skippedMessageOrderClose.id > lastSimulatorEngineMessageId) { + logWarn('The Skipped Message Order Close was found and using it to set the SL and TP.') + isMessageAnOrderClose = (simulatorEngineMessage.messageType === MESSAGE_TYPE.OrderClose) + isStopLoss = (simulatorEngineMessage.order.exitOutcome === ORDER_EXIT_OUTCOME.StopLoss) + isTakeProfit = (simulatorEngineMessage.order.exitOutcome === ORDER_EXIT_OUTCOME.StopLoss) + } else { + logWarn('One Message Order Close was found but is older than the Open Order. Closing the position the best possible.') + stopLoss = assistant.getMarketRate() + takeProfit = assistant.getMarketRate() + } + } + } + + // Validations to check the sync state between the simulator and executor + let isMessageAnOrderUpdate = (simulatorEngineMessage.messageType === MESSAGE_TYPE.OrderUpdate) + let isMessageAnOrderClose = (simulatorEngineMessage.messageType === MESSAGE_TYPE.OrderClose) + let isStopLoss = (simulatorEngineMessage.order.exitOutcome === ORDER_EXIT_OUTCOME.StopLoss) + let isTakeProfit = (simulatorEngineMessage.order.exitOutcome === ORDER_EXIT_OUTCOME.StopLoss) + + if (isMessageAnOrderUpdate) { + stopLoss = simulatorEngineMessage.order.stop + takeProfit = simulatorEngineMessage.order.takeProfit + } else if (isMessageAnOrderClose && isStopLoss) { + stopLoss = assistant.getMarketRate() + takeProfit = 0 + } else if (isMessageAnOrderClose && isTakeProfit) { + stopLoss = 0 + takeProfit = assistant.getMarketRate() + } + assistant.rememberThis('lastStopLoss', stopLoss) + assistant.rememberThis('lastTakeProfit', takeProfit) } // Checking Stop Loss @@ -120,7 +168,7 @@ exports.newUserBot = function newUserBot(bot, logger) { let position = await createBuyPosition(currentRate) let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() - simulatorExecutorMessage.id = position.id + simulatorExecutorMessage.id = receivedMessageId simulatorExecutorMessage.order.id = position.id simulatorExecutorMessage.order.rate = currentRate simulatorExecutorMessage.order.stop = stopLoss @@ -137,6 +185,7 @@ exports.newUserBot = function newUserBot(bot, logger) { assistant.rememberThis('lastTakeProfit', 0) } catch (error) { let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() + simulatorExecutorMessage.id = receivedMessageId simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.order.rate = position.rate @@ -160,7 +209,7 @@ exports.newUserBot = function newUserBot(bot, logger) { let position = await createBuyPosition(currentRate, assetABalance) let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() - simulatorExecutorMessage.id = position.id + simulatorExecutorMessage.id = receivedMessageId simulatorExecutorMessage.order.id = position.id simulatorExecutorMessage.order.rate = currentRate simulatorExecutorMessage.order.stop = stopLoss @@ -177,6 +226,7 @@ exports.newUserBot = function newUserBot(bot, logger) { assistant.rememberThis('lastTakeProfit', 0) } catch (error) { let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() + simulatorExecutorMessage.id = receivedMessageId simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.order.rate = currentRate @@ -185,6 +235,7 @@ exports.newUserBot = function newUserBot(bot, logger) { simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy simulatorExecutorMessage.order.size = assetABalance simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected + simulatorExecutorMessage.order.statusDetails = error.message let message = createMessageFromObject(simulatorExecutorMessage) assistant.addExtraData(message) @@ -214,7 +265,7 @@ exports.newUserBot = function newUserBot(bot, logger) { if (simulatorEngineMessage.messageType === MESSAGE_TYPE.Order && simulatorEngineMessage.order.direction === ORDER_DIRECTION.Sell) { - let lastSimulatorEngineMessageId = assistant.remindMeOf('lastSimulatorEngineMessageId') + let lastSimulatorEngineMessageId = getValueFromPreviousExecution('lastSimulatorEngineMessageId') if (lastSimulatorEngineMessageId >= simulatorEngineMessage.id) { logInfo("manageCloneInAutopilotOn -> Order message was previously processed.") return @@ -223,7 +274,7 @@ exports.newUserBot = function newUserBot(bot, logger) { try { let position = await createSellPosition(currentRate, assetBBalance) let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() - simulatorExecutorMessage.id = position.id + simulatorExecutorMessage.id = simulatorEngineMessage.id simulatorExecutorMessage.order.id = position.id simulatorExecutorMessage.order.rate = currentRate simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop @@ -233,11 +284,12 @@ exports.newUserBot = function newUserBot(bot, logger) { let message = createMessageFromObject(simulatorExecutorMessage) assistant.addExtraData(message) - assistant.rememberThis('lastSimulatorEngineMessageId', parseInt(simulatorEngineMessage.id)) + assistant.rememberThis('lastSimulatorEngineMessageId', simulatorEngineMessage.id) assistant.rememberThis('lastStopLoss', simulatorEngineMessage.order.stop) assistant.rememberThis('lastTakeProfit', simulatorEngineMessage.order.takeProfit) } catch (error) { let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() + simulatorExecutorMessage.id = receivedMessageId simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.messageType = MESSAGE_TYPE.Order @@ -247,6 +299,7 @@ exports.newUserBot = function newUserBot(bot, logger) { simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Sell simulatorExecutorMessage.order.size = assetBBalance simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected + simulatorExecutorMessage.order.statusDetails = error.message let message = createMessageFromObject(simulatorExecutorMessage) assistant.addExtraData(message) @@ -272,7 +325,7 @@ exports.newUserBot = function newUserBot(bot, logger) { // 1) Save audit containing what we received from the cockpit let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() - simulatorExecutorMessage.id = 0 + simulatorExecutorMessage.id = simulatorEngineMessage.id simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingCokpit simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.order = acceptedSignal.orderData // we save the order as received @@ -285,7 +338,7 @@ exports.newUserBot = function newUserBot(bot, logger) { // 3) Save the audit of the order placed simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() - simulatorExecutorMessage.id = position.id + simulatorExecutorMessage.id = simulatorEngineMessage.id simulatorExecutorMessage.from = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingAssistant simulatorExecutorMessage.dateTime = position.date @@ -307,12 +360,13 @@ exports.newUserBot = function newUserBot(bot, logger) { } catch (error) { // Update the cockpit with the error let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() - simulatorExecutorMessage.id = 0 + simulatorExecutorMessage.id = simulatorEngineMessage.id simulatorExecutorMessage.from = MESSAGE_ENTITY.SimulationExecutor simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingCokpit simulatorExecutorMessage.messageType = MESSAGE_TYPE.Order simulatorExecutorMessage.dateTime = bot.processDatetime.valueOf() simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected + simulatorExecutorMessage.order.statusDetails = error.message simulatorExecutorMessage.order = acceptedSignal.order @@ -405,7 +459,7 @@ exports.newUserBot = function newUserBot(bot, logger) { // 2) Save the audit message for the received info from Trading Simulator let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage() - simulatorExecutorMessage.id = 0 + simulatorExecutorMessage.id = simulatorEngineMessage.id simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingCokpit simulatorExecutorMessage.messageType = MESSAGE_TYPE.Order simulatorExecutorMessage.order.id = 0 @@ -524,17 +578,17 @@ exports.newUserBot = function newUserBot(bot, logger) { // Storage functions async function getIndicatorFile() { - logInfo('getIndicatorFile -> Entering function.') + logInfo('getIndicatorFile -> Entering function. ' + bot.processDatetime.toISOString()) let filePath if (bot.processes[0].timePeriod > 2700000) { // Market Files - filePath = 'Trading-Simulation/' + bot.dataSet + '/' + bot.timePeriodFileStorage + filePath = 'Trading-Execution/' + bot.dataSet + '/' + bot.timePeriodFileStorage } else { // Daily Files let dateTime = bot.processDatetime let datePath = dateTime.getUTCFullYear() + '/' + pad(dateTime.getUTCMonth() + 1, 2) + '/' + pad(dateTime.getUTCDate(), 2) - filePath = 'Trading-Simulation/' + bot.dataSet + '/' + bot.timePeriodFileStorage + '/' + datePath + filePath = 'Trading-Execution/' + bot.dataSet + '/' + bot.timePeriodFileStorage + '/' + datePath } /* @@ -561,7 +615,7 @@ exports.newUserBot = function newUserBot(bot, logger) { if (bot.processDatetime.valueOf() <= lastAvailableDateTime || !isExecutionToday()) { for (let i = 0; i < indicatorFileContent.length; i++) { if (bot.processDatetime.valueOf() >= indicatorFileContent[i][0] && bot.processDatetime.valueOf() < indicatorFileContent[i][1]) { - return getMessage(indicatorFileContent[i][25]) + return getMessage(indicatorFileContent[i][2]) } } @@ -570,7 +624,7 @@ exports.newUserBot = function newUserBot(bot, logger) { // Running live we will process last available Indicator Message only if it's delayed 10 minutes top let maxTolerance = 10 * 60 * 1000 if (bot.processDatetime.valueOf() <= (lastAvailableDateTime + maxTolerance)) { - return getMessage(indicatorFileContent[lastIndexIndicatorFile][25]) + return getMessage(indicatorFileContent[lastIndexIndicatorFile][2]) } else { logWarn('getSimulatorEngineMessageFromFile -> Last available indicator older than 10 minutes.') } @@ -581,6 +635,22 @@ exports.newUserBot = function newUserBot(bot, logger) { } } + function getSkippedMessageOrderClose(indicatorFileContent) { + try { + let lastIndexIndicatorFile = indicatorFileContent.length - 1 + + for (let i = lastIndexIndicatorFile; i >= 0; i--) { + let simulatorMessage = getMessage(indicatorFileContent[i][2]) + if (simulatorMessage.messageType === MESSAGE_TYPE.OrderClose) { + return simulatorMessage + } + } + } catch (error) { + logError('getSkippedMessageOrderClose -> error = ' + error.message) + throw global.DEFAULT_RETRY_RESPONSE + } + } + // Helper Functions function isExecutionToday() { let localDate = new Date() @@ -774,16 +844,18 @@ exports.newUserBot = function newUserBot(bot, logger) { simulatorExecutorMessage.dateTime = bot.processDatetime.valueOf() simulatorExecutorMessage.order = {} + simulatorExecutorMessage.order.id = 0 simulatorExecutorMessage.order.dateTime = bot.processDatetime.valueOf() simulatorExecutorMessage.order.creator = ORDER_CREATOR.SimulationEngine simulatorExecutorMessage.order.owner = ORDER_OWNER.User simulatorExecutorMessage.order.exchange = global.EXCHANGE_NAME - simulatorExecutorMessage.order.market = global.MARKET.name + simulatorExecutorMessage.order.market = global.MARKET.assetA + '_' + global.MARKET.assetB simulatorExecutorMessage.order.marginEnabled = ORDER_MARGIN_ENABLED.False simulatorExecutorMessage.order.type = ORDER_TYPE.Limit simulatorExecutorMessage.order.status = ORDER_STATUS.Placed simulatorExecutorMessage.order.sizeFilled = 0 simulatorExecutorMessage.order.exitOutcome = '' + simulatorExecutorMessage.order.statusMessage = '' return simulatorExecutorMessage } diff --git a/src/storage/templates/TradingBot/this.bot.config.json b/src/storage/templates/TradingBot/this.bot.config.json index c3d22f52e..d59dba4bc 100644 --- a/src/storage/templates/TradingBot/this.bot.config.json +++ b/src/storage/templates/TradingBot/this.bot.config.json @@ -7,7 +7,7 @@ "minor": 0, "patch": 0 }, - "devTeam": "aavikings", + "devTeam": "vikings", "profilePicture": "bumblebee.png", "dataSetVersion": "dataSet.V1", "processes": [ @@ -73,7 +73,7 @@ "major": 1, "minor": 0 }, - "product": "Trading-Simulation", + "product": "Trading-Execution", "dataSetVersion": "dataSet.V1", "dataSet": "Multi-Period-Market" }, @@ -84,7 +84,7 @@ "major": 1, "minor": 0 }, - "product": "Trading-Simulation", + "product": "Trading-Execution", "dataSetVersion": "dataSet.V1", "dataSet": "Multi-Period-Daily" } @@ -101,27 +101,7 @@ { "codeName": "Oficial History", "type": "File Sequence", - "validPeriods": [ - "24-hs", - "12-hs", - "08-hs", - "06-hs", - "04-hs", - "03-hs", - "02-hs", - "01-hs", - "45-min", - "40-min", - "30-min", - "20-min", - "15-min", - "10-min", - "05-min", - "04-min", - "03-min", - "02-min", - "01-min" - ], + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs", "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], "filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process", "fileName": "Execution.History.Live.@Sequence.json" } @@ -131,11 +111,11 @@ "name": "Poloniex" } ], - "plotter": { - "devTeam": "AAMasters", - "codeName": "PlottersTradingSimulation", - "moduleName": "SimulationExecution" - } + "plotter": { + "devTeam": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "SimulationExecution" + } }, { "codeName": "Backtest Trading History", @@ -146,27 +126,7 @@ { "codeName": "Backtest History", "type": "File Sequence", - "validPeriods": [ - "24-hs", - "12-hs", - "08-hs", - "06-hs", - "04-hs", - "03-hs", - "02-hs", - "01-hs", - "45-min", - "40-min", - "30-min", - "20-min", - "15-min", - "10-min", - "05-min", - "04-min", - "03-min", - "02-min", - "01-min" - ], + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs", "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], "filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process", "fileName": "Execution.History.Backtest.@Sequence.json" } @@ -176,11 +136,11 @@ "name": "Poloniex" } ], - "plotter": { - "devTeam": "AAMasters", - "codeName": "PlottersTradingSimulation", - "moduleName": "SimulationExecution" - } + "plotter": { + "devTeam": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "SimulationExecution" + } }, { "codeName": "Competition Trading History", @@ -191,27 +151,7 @@ { "codeName": "Competition History", "type": "File Sequence", - "validPeriods": [ - "24-hs", - "12-hs", - "08-hs", - "06-hs", - "04-hs", - "03-hs", - "02-hs", - "01-hs", - "45-min", - "40-min", - "30-min", - "20-min", - "15-min", - "10-min", - "05-min", - "04-min", - "03-min", - "02-min", - "01-min" - ], + "validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs", "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], "filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process", "fileName": "Execution.History.Competition.@Sequence.json" } @@ -221,11 +161,11 @@ "name": "Poloniex" } ], - "plotter": { - "devTeam": "AAMasters", - "codeName": "PlottersTradingSimulation", - "moduleName": "SimulationExecution" - } + "plotter": { + "devTeam": "AAMasters", + "codeName": "PlottersTradingSimulation", + "moduleName": "SimulationExecution" + } } ], "marketRateProvider": {