From a345567808f7c05e5d0830df645a445da924020c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 7 Oct 2020 15:59:29 +0200 Subject: [PATCH 01/41] Port Already in Use Bug --- EventServerClient.js | 84 ++++++++++++++++++++++++++------------------ server.js | 49 ++++++++++++++++---------- 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/EventServerClient.js b/EventServerClient.js index a5de99f5a..09bf453cb 100644 --- a/EventServerClient.js +++ b/EventServerClient.js @@ -1,7 +1,7 @@ exports.newEventsServerClient = function newEventsServerClient(host, port) { const MODULE = "Task Server " - const ERROR_LOG = true + const INFO_LOG = false let thisObject = { @@ -27,6 +27,8 @@ port = process.env.WEB_SOCKETS_SERVER_PORT } + let messageCounter = 0 + let commandCounter = 0 return thisObject @@ -39,25 +41,32 @@ function setuptWebSockets(callBackFunction) { try { + if (INFO_LOG === true) { + console.log('setuptWebSockets at ' + host + ':' + port) + } WEB_SOCKETS_CLIENT = new WEB_SOCKET('ws://' + host + ':' + port ) - WEB_SOCKETS_CLIENT.onerror = error => { - console.log('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> On connection error -> error = ' + JSON.stringify(error)) + WEB_SOCKETS_CLIENT.onerror = err => { + console.log('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> On connection error -> error = ' + err.stack) + console.log('[ERROR] This could mean that the port '+ port +' is taken by some other app running at your system. To resolve this issue, please pick another port number and change it at the .ENV file inside the Superalgos folder AND at the .ENV file inside the TaskServer folder. After that run the app again. ') } WEB_SOCKETS_CLIENT.onopen = () => { - if (INFO_LOG === true) { - console.log('Websocket connection opened.') - } - - if (callBackFunction !== undefined) { - callBackFunction() + try { + if (INFO_LOG === true) { + console.log('Websocket connection opened.') + } + + if (callBackFunction !== undefined) { + callBackFunction() + } + } catch(err) { + console.log('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> onopen -> err = ' + err.stack) } } WEB_SOCKETS_CLIENT.onmessage = e => { - try { if (INFO_LOG === true) { - console.log('Websocket Message Received: ' + e.data) + console.log('Websocket Message Received: ' + e.data.substring(0, 1000)) } let message = JSON.parse(e.data) @@ -84,11 +93,11 @@ return } } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> onmessage -> err = ' + err.stack) } + console.log('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> onmessage -> err = ' + err.stack) } } } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> err = ' + err.stack) } + console.log('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> err = ' + err.stack) } } @@ -109,30 +118,35 @@ } function sendCommand(command, responseCallBack, eventsCallBack) { - - if (command.action === 'listenToEvent') { - let key - if (command.callerId) { - key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId + try { + if (command.action === 'listenToEvent') { + let key + if (command.callerId) { + key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId + } else { + key = command.eventHandlerName + '-' + command.eventType + } + let handler = { + eventHandlerName: command.eventHandlerName, + eventType: command.eventType, + callerId: command.callerId, + callBack: eventsCallBack + } + eventListeners.set(key, handler) + } + if (command.callerId && responseCallBack) { + responseWaiters.set(command.callerId, responseCallBack) + } + + if (WEB_SOCKETS_CLIENT.readyState === 1) { // 1 means connected and ready. + + WEB_SOCKETS_CLIENT.send("Task Server" + "|*|" + "|*|" + JSON.stringify(command)) + } else { - key = command.eventHandlerName + '-' + command.eventType + console.log('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> sendCommand -> WebSocket message could not be sent because the connection was not ready. Message = ' + JSON.stringify(command)) } - let handler = { - eventHandlerName: command.eventHandlerName, - eventType: command.eventType, - callerId: command.callerId, - callBack: eventsCallBack - } - eventListeners.set(key, handler) - } - if (command.callerId && responseCallBack) { - responseWaiters.set(command.callerId, responseCallBack) - } - - if (WEB_SOCKETS_CLIENT.readyState === 1) { // 1 means connected and ready. - WEB_SOCKETS_CLIENT.send("Task Server" + "|*|" + "|*|" + JSON.stringify(command)) - } else { - console.log('[ERROR] Task Server -> Event Server Client -> setuptWebSockets -> sendCommand -> WebSocket message could not be sent because the connection was not ready. Message = ' + JSON.stringify(command)) + } catch(err) { + console.log('[ERROR] Task Server -> Event Server Client -> sendCommand -> err = ' + err.stack) } } diff --git a/server.js b/server.js index 3ff27cf3f..5ec84ec11 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,4 @@ - /* Callbacks default responses. */ global.DEFAULT_OK_RESPONSE = { result: "Ok", @@ -316,7 +315,9 @@ function finalizeLoggers() { global.LOGGER_MAP.forEach(forEachLogger) function forEachLogger(logger) { - logger.finalize() + if (logger !== undefined) { + logger.finalize() + } } } @@ -375,12 +376,14 @@ global.EXIT_NODE_PROCESS = function exitProcess() { /* Cleaning Before Exiting. */ clearInterval(global.HEARTBEAT_INTERVAL_HANDLER) - for (let i = 0; i < global.TASK_NODE.bot.processes.length; i++) { - let config = global.TASK_NODE.bot.processes[i].config - let process = global.TASK_NODE.bot.processes[i] - - key = process.name + '-' + process.type + '-' + process.id - global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Stopped') // Meaning Process Stopped + if (global.TASK_NODE !== undefined) { + for (let i = 0; i < global.TASK_NODE.bot.processes.length; i++) { + let config = global.TASK_NODE.bot.processes[i].config + let process = global.TASK_NODE.bot.processes[i] + + key = process.name + '-' + process.type + '-' + process.id + global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Stopped') // Meaning Process Stopped + } } finalizeLoggers() @@ -426,11 +429,15 @@ function preLoader() { global.EVENT_SERVER_CLIENT.listenToEvent('Task Server - ' + taskId, 'Run Task', undefined, 'Task Server - ' + taskId, undefined, eventReceived) global.EVENT_SERVER_CLIENT.raiseEvent('Task Manager - ' + taskId, 'Nodejs Process Ready for Task') function eventReceived(message) { - global.APP_SCHEMA_ARRAY = JSON.parse(message.event.appSchema) - setUpAppSchema() - global.TASK_NODE = JSON.parse(message.event.taskDefinition) - global.TASK_NETWORK = JSON.parse(message.event.networkDefinition) - bootLoader() + try { + global.APP_SCHEMA_ARRAY = JSON.parse(message.event.appSchema) + setUpAppSchema() + global.TASK_NODE = JSON.parse(message.event.taskDefinition) + global.TASK_NETWORK = JSON.parse(message.event.networkDefinition) + bootLoader() + } catch(err) { + console.log('[ERROR] Task Server -> server -> preLoader -> eventReceived -> ' + err.stack) + } } } catch (err) { console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack) @@ -442,11 +449,16 @@ function preLoader() { //console.log('[INFO] Task Server -> server -> preLoader -> Waiting for event to start debugging...') global.EVENT_SERVER_CLIENT.listenToEvent('Task Server', 'Debug Task Started', undefined, 'Task Server', undefined, startDebugging) function startDebugging(message) { - global.APP_SCHEMA_ARRAY = JSON.parse(message.event.appSchema) - setUpAppSchema() - global.TASK_NODE = JSON.parse(message.event.taskDefinition) - global.TASK_NETWORK = JSON.parse(message.event.networkDefinition) - bootLoader() + try { + global.APP_SCHEMA_ARRAY = JSON.parse(message.event.appSchema) + setUpAppSchema() + global.TASK_NODE = JSON.parse(message.event.taskDefinition) + global.TASK_NETWORK = JSON.parse(message.event.networkDefinition) + bootLoader() + + } catch(err) { + console.log('[ERROR] Task Server -> server -> preLoader -> startDebugging -> ' + err.stack) + } } } catch (err) { console.log('[ERROR] Task Server -> server -> preLoader -> global.TASK_NODE -> ' + err.stack) @@ -502,6 +514,7 @@ function bootLoader() { global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Error', event) } + for (let processIndex = 0; processIndex < global.TASK_NODE.bot.processes.length; processIndex++) { let config = global.TASK_NODE.bot.processes[processIndex].config From 70915a296bba3d64979db95f23b5341a1c2980f3 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 7 Oct 2020 23:41:08 +0200 Subject: [PATCH 02/41] Deep Scan Medium Severity --- DataSet.js | 6 ++++-- IndicatorBot.js | 2 +- LowFrequencyTrading/TradingPosition.js | 8 ++++---- LowFrequencyTrading/TradingSystem.js | 2 +- MultiPeriodDaily.js | 7 +++---- ProcessExecutionEvents.js | 2 +- ProcessOutput.js | 4 ++-- SensorBot.js | 2 +- Session.js | 2 +- TradingBot.js | 1 - server.js | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/DataSet.js b/DataSet.js index 5b43b7bbf..4637e72b9 100644 --- a/DataSet.js +++ b/DataSet.js @@ -24,8 +24,8 @@ exports.newDataSet = function newDataSet(BOT, logger) { try { thisObject.node = dataDependency.referenceParent; - logger.fileName = MODULE_NAME + "." + thisObject.node.type + "." + thisObject.node.name + "." + thisObject.node.id; - + logger.fileName = MODULE_NAME + /* Some very basic validations that we have all the information needed. */ if (thisObject.node === undefined) { logger.write(MODULE_NAME, "[ERROR] initialize -> Data Dependency without Reference Parent -> dataDependency = " + JSON.stringify(dataDependency)); @@ -33,6 +33,8 @@ exports.newDataSet = function newDataSet(BOT, logger) { return } + logger.fileName = MODULE_NAME + "." + thisObject.node.type + "." + thisObject.node.name + "." + thisObject.node.id; + if (thisObject.node.config.codeName === undefined) { logger.write(MODULE_NAME, "[ERROR] initialize -> Dataset witn no codeName defined -> Product Dataset = " + JSON.stringify(thisObject.node)); callBackFunction(global.DEFAULT_FAIL_RESPONSE); diff --git a/IndicatorBot.js b/IndicatorBot.js index cd513dee4..2f55a1daa 100644 --- a/IndicatorBot.js +++ b/IndicatorBot.js @@ -61,7 +61,7 @@ function onCommonsDownloaded(err, text) { if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.code || err.message); + parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.message); callBackFunction(global.DEFAULT_OK_RESPONSE); return; } diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index 8476a4685..bad18fabf 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -186,7 +186,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng const message = 'Target Size In Base Asset cannot be undefined. Fix this please.' badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula) } - if (value === undefined) { + if (value === 0) { const message = 'Target Size In Base Asset cannot be zero. Fix this please.' badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInBaseAsset.formula) } @@ -222,7 +222,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng const message = 'Target Size In Quoted Asset cannot be undefined. Fix this please.' badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula) } - if (value === undefined) { + if (value === 0) { const message = 'Target Size In Quoted Asset cannot be zero. Fix this please.' badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula) } @@ -244,8 +244,8 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng /* Remember how the end user defined this stage. */ tradingEngineStageNode.stageDefinedIn.value = 'Quoted Asset' } else { - const errorText = 'You need to specify a Formula for this.' - badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset.formula) + const message = 'You need to specify a Formula for this.' + badDefinitionUnhandledException(undefined, message, tradingSystemStageNode.initialTargets.targetSizeInQuotedAsset) } } } diff --git a/LowFrequencyTrading/TradingSystem.js b/LowFrequencyTrading/TradingSystem.js index d90c558ae..2e3af7ee3 100644 --- a/LowFrequencyTrading/TradingSystem.js +++ b/LowFrequencyTrading/TradingSystem.js @@ -310,6 +310,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM if (error !== undefined) { tradingSystem.errors.push([node.id, error]) + logger.write(MODULE_NAME, '[INFO] evalFormula -> error = ' + error) return } if (value !== undefined) { @@ -323,7 +324,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM } logger.write(MODULE_NAME, '[INFO] evalFormula -> value = ' + value) - logger.write(MODULE_NAME, '[INFO] evalFormula -> error = ' + error) } } diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index cdc4a892b..68527a72f 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -67,7 +67,6 @@ function getContextVariables() { try { let thisReport; - let reportKey; let statusReport; /* We look first for the bot who knows the begining of the market in order to get when the market starts. @@ -89,7 +88,7 @@ thisReport = statusReport.file; if (thisReport.beginingOfMarket === undefined) { - logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey) + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> thisReport.beginingOfMarket === undefined ") logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.") let customOK = { @@ -122,7 +121,7 @@ thisReport = statusReport.file; if (thisReport.lastFile === undefined) { - logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey) + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> thisReport.lastFile === undefined") let customOK = { result: global.CUSTOM_OK_RESPONSE.result, @@ -157,7 +156,7 @@ beginingOfMarket = new Date(thisReport.beginingOfMarket) if (beginingOfMarket.valueOf() !== contextVariables.dateBeginOfMarket.valueOf()) { // Reset Mechanism for Begining of the Market - logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey) + logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated.") beginingOfMarket = new Date(contextVariables.dateBeginOfMarket.valueOf()) startFromBegining() diff --git a/ProcessExecutionEvents.js b/ProcessExecutionEvents.js index 9b94c0365..ae74beee2 100644 --- a/ProcessExecutionEvents.js +++ b/ProcessExecutionEvents.js @@ -11,7 +11,7 @@ let thisObject = { networkNode: undefined, - initialize, initialize, + initialize: initialize, finalize: finalize, start: start, finish: finish diff --git a/ProcessOutput.js b/ProcessOutput.js index 9f6f0ee71..6075ee9e7 100644 --- a/ProcessOutput.js +++ b/ProcessOutput.js @@ -86,7 +86,7 @@ let product = outputDataset.referenceParent.parentNode.config.codeName let dataset = outputDataset.referenceParent.config.codeName - key = dataMine + "-" + botName + "-" + product + "-" + dataset + "-" + bot.exchange + "-" + bot.market.baseAsset + '/' + bot.market.quotedAsset + let key = dataMine + "-" + botName + "-" + product + "-" + dataset + "-" + bot.exchange + "-" + bot.market.baseAsset + '/' + bot.market.quotedAsset let event = { lastFile: lastFile, timeFrames: timeFrames @@ -165,7 +165,7 @@ let product = outputDataset.referenceParent.parentNode.config.codeName let dataset = outputDataset.referenceParent.config.codeName - key = dataMine + "-" + botName + "-" + product + "-" + dataset + "-" + bot.exchange + "-" + bot.market.baseAsset + '/' + bot.market.quotedAsset + let key = dataMine + "-" + botName + "-" + product + "-" + dataset + "-" + bot.exchange + "-" + bot.market.baseAsset + '/' + bot.market.quotedAsset let event = { lastFile: lastFile, timeFrames: timeFrames diff --git a/SensorBot.js b/SensorBot.js index 13e5c251d..e4c1f6a70 100644 --- a/SensorBot.js +++ b/SensorBot.js @@ -63,7 +63,7 @@ function onCommonsDownloaded(err, text) { if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.code || err.message); + parentLogger.write(MODULE_NAME, "[WARN] initialize -> onBotDownloaded -> onCommonsDownloaded -> Commons not found: " + err.message); callBackFunction(global.DEFAULT_OK_RESPONSE); return; } diff --git a/Session.js b/Session.js index 9b8c82b67..582500864 100644 --- a/Session.js +++ b/Session.js @@ -322,7 +322,7 @@ /* Slippage */ if (bot.SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. - bot.SESSION.parameters.timeRange = { + bot.SESSION.parameters.slippage = { name: 'Missing Slippage', type: 'Slippage', config: { diff --git a/TradingBot.js b/TradingBot.js index 9d4d48ee7..4e0f2b6fb 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -251,7 +251,6 @@ global.unexpectedError = err.message processStopped() return - return; } default: { logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); diff --git a/server.js b/server.js index 5ec84ec11..1fd442a8b 100644 --- a/server.js +++ b/server.js @@ -381,7 +381,7 @@ global.EXIT_NODE_PROCESS = function exitProcess() { let config = global.TASK_NODE.bot.processes[i].config let process = global.TASK_NODE.bot.processes[i] - key = process.name + '-' + process.type + '-' + process.id + let key = process.name + '-' + process.type + '-' + process.id global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Stopped') // Meaning Process Stopped } } From eaa233e970b32d4f37a643e02625eb77050b760c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 9 Oct 2020 18:35:16 +0200 Subject: [PATCH 03/41] Update MultiPeriodDaily.js --- MultiPeriodDaily.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MultiPeriodDaily.js b/MultiPeriodDaily.js index 68527a72f..4d0e4b922 100644 --- a/MultiPeriodDaily.js +++ b/MultiPeriodDaily.js @@ -138,7 +138,7 @@ statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. - logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Self Reference -> Status Report does not exist or Self Reference not defined. Retrying Later. ") + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Self Reference -> Status Report does not exist or Self Reference not defined or badly configured. Check that at the Status Dependency you have chosen Self Reference at the config. Retrying Later. ") callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } From 966c137690aad358bd32e9293edd4b75177be357 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 13 Oct 2020 19:13:32 +0200 Subject: [PATCH 04/41] Update ExchangeAPI.js --- LowFrequencyTrading/ExchangeAPI.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/LowFrequencyTrading/ExchangeAPI.js b/LowFrequencyTrading/ExchangeAPI.js index a6d314c43..6b9ff2e92 100644 --- a/LowFrequencyTrading/ExchangeAPI.js +++ b/LowFrequencyTrading/ExchangeAPI.js @@ -27,7 +27,7 @@ let key = bot.KEY let secret = bot.SECRET - + const exchangeClass = ccxt[exchangeId] const exchangeConstructorParams = { 'apiKey': key, @@ -129,7 +129,23 @@ return order.id } catch (err) { tradingSystem.errors.push([tradingSystemOrder.id, "createOrder -> Error = " + err.message]) - logError("createOrder -> Error = " + err.message); + logError("createOrder -> Error = " + err.message) + if (err.message.indexOf('API-key format invalid') >= 0) { + logError ('The exchange says the API key provided is not good or it is not in the correct format. This is what you are using:') + errorFooter() + } + if (err.message.indexOf('Signature for this request is not valid.') >= 0) { + logError ('The exchange says the secret provided is not good, incorrect or not in the correct format. This is what you are using:') + errorFooter() + } + function errorFooter(){ + logError ('key: ->' + bot.KEY + '<-') + logError ('secret: ->' + bot.SECRET + '<-') + logError ('key.length:' + bot.KEY.length ) + logError ('secret.length:' + bot.SECRET.length ) + logError ('Double check that you copied the key at the codeName property and the secret at secret property without bringing any invisible caracter from the exchange web site, or leaving any character from the sample text. Also check that the keys are not disabled at the exchange ot that they are not restricted by IP.') + logError ('As a remainder. Binance key and secret are 64 bytes in lenght each one. ' ) + } } } From eaee2e0baa9d363d90e7f0d89fffa69e79a82467 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 14 Oct 2020 09:48:49 +0200 Subject: [PATCH 05/41] Update TradingBot.js --- TradingBot.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TradingBot.js b/TradingBot.js index 4e0f2b6fb..c5a5d05cb 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -106,10 +106,10 @@ /* Checking if we should process this loop or not.*/ if (bot.STOP_SESSION === true) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be ran."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be run."); } console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) - + " Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be ran. "); + + " Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be run. "); nextWaitTime = 'Waiting for Session'; loopControl(nextWaitTime); @@ -551,7 +551,7 @@ waitingTime = waitTime / 1000 label = 'seconds' } - processHeartBeat(undefined, undefined, "Waiting " + waitingTime + " " + label + " for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be ran. ") + processHeartBeat(undefined, undefined, "Waiting " + waitingTime + " " + label + " for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be run. ") logger.persist(); } break From 7011ecfeea565932547d78b2c7ff6324ac136b9f Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 21 Oct 2020 18:29:08 +0200 Subject: [PATCH 06/41] Bug Fixed --- LowFrequencyTrading/TradingEngine.js | 2 ++ LowFrequencyTrading/TradingPosition.js | 2 +- LowFrequencyTrading/TradingRecords.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index b9dc6f889..20f158fe3 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -66,6 +66,8 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { function setCurrentCycle(cycle) { tradingEngine.current.episode.cycle.value = cycle + tradingEngine.current.episode.cycle.lastBegin.value = tradingEngine.current.episode.cycle.begin.value + tradingEngine.current.episode.cycle.lastEnd.value = tradingEngine.current.episode.cycle.end.value switch (cycle) { case 'First': { tradingEngine.current.episode.cycle.begin.value = diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index bad18fabf..67cac1447 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -83,7 +83,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function closePosition() { tradingEngine.current.position.status.value = 'Closed' - tradingEngine.current.position.end.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.position.end.value = tradingEngine.current.episode.candle.begin.value tradingEngine.current.position.endRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.position.positionBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.position.positionQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index f3de8553c..7de6ec80b 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -152,7 +152,7 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { /* The Target Node is the node from where we are going to exctract the value. We will use the codeName of the Record Property to match it with - the any of the properties of the Root Node to get the Target Node. + any of the properties of the Root Node to get the Target Node. */ let targetNode try { From 866a5715680eccf34b990a93ce2eee0165ba0544 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 21 Oct 2020 19:51:22 +0200 Subject: [PATCH 07/41] Fixes --- LowFrequencyTrading/TradingEpisode.js | 2 +- LowFrequencyTrading/TradingPosition.js | 2 +- LowFrequencyTrading/TradingStages.js | 6 +++--- LowFrequencyTrading/TradingStrategy.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/LowFrequencyTrading/TradingEpisode.js b/LowFrequencyTrading/TradingEpisode.js index 76bb7a8d3..e400945e7 100644 --- a/LowFrequencyTrading/TradingEpisode.js +++ b/LowFrequencyTrading/TradingEpisode.js @@ -73,7 +73,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin function closeEpisode() { tradingEngine.current.episode.status.value = 'Closed' - tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.begin.value tradingEngine.current.episode.endRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.episode.episodeBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.episode.episodeQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index 67cac1447..2ff04cd96 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -55,7 +55,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng tradingEngine.current.position.status.value = 'Open' tradingEngine.current.position.serialNumber.value = tradingEngine.current.episode.episodeCounters.positions.value + 1 tradingEngine.current.position.identifier.value = global.UNIQUE_ID() - tradingEngine.current.position.begin.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.position.begin.value = tradingEngine.current.episode.candle.begin.value tradingEngine.current.position.beginRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.position.positionBaseAsset.beginBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.position.positionQuotedAsset.beginBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index b3465bf18..434e45f0f 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -992,14 +992,14 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM function openStage(stage) { /* Recording the opening at the Trading Engine Data Structure */ - stage.begin.value = tradingEngine.current.episode.cycle.begin.value + stage.begin.value = tradingEngine.current.episode.candle.begin.value stage.beginRate.value = tradingEngine.current.episode.candle.close.value - stage.end.value = tradingEngine.current.episode.cycle.end.value + stage.end.value = tradingEngine.current.episode.candle.begin.value } function closeStage(stage) { /* Recording the closing at the Trading Engine Data Structure */ - stage.end.value = tradingEngine.current.episode.cycle.end.value + stage.end.value = tradingEngine.current.episode.candle.begin.value stage.endRate.value = tradingEngine.current.episode.candle.close.value } } diff --git a/LowFrequencyTrading/TradingStrategy.js b/LowFrequencyTrading/TradingStrategy.js index 5225f705d..4ad903dad 100644 --- a/LowFrequencyTrading/TradingStrategy.js +++ b/LowFrequencyTrading/TradingStrategy.js @@ -37,7 +37,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng tradingEngine.current.strategy.status.value = 'Open' tradingEngine.current.strategy.serialNumber.value = tradingEngine.current.episode.episodeCounters.strategies.value tradingEngine.current.strategy.identifier.value = global.UNIQUE_ID() - tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.candle.begin.value tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.end.value tradingEngine.current.strategy.beginRate.value = tradingEngine.current.episode.candle.min.value @@ -52,7 +52,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng function closeStrategy(exitType) { tradingEngine.current.strategy.status.value = 'Closed' tradingEngine.current.strategy.exitType.value = exitType - tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.begin.value tradingEngine.current.strategy.endRate.value = tradingEngine.current.episode.candle.min.value /* Now that the strategy is closed, it is the right time to move this strategy from current to last at the Trading Engine data structure. From b34a56a783d876cc4360aada0376b9b545038444 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 22 Oct 2020 12:44:44 +0200 Subject: [PATCH 08/41] Better Handling of Begin and End --- LowFrequencyTrading/TradingEpisode.js | 11 +++--- LowFrequencyTrading/TradingPosition.js | 38 ++++++++++++++++----- LowFrequencyTrading/TradingStages.js | 47 ++++++++++++++++++-------- LowFrequencyTrading/TradingStrategy.js | 14 +++++--- 4 files changed, 78 insertions(+), 32 deletions(-) diff --git a/LowFrequencyTrading/TradingEpisode.js b/LowFrequencyTrading/TradingEpisode.js index e400945e7..de5c74f75 100644 --- a/LowFrequencyTrading/TradingEpisode.js +++ b/LowFrequencyTrading/TradingEpisode.js @@ -50,6 +50,10 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin or the task / session was restarted. */ if (bot.FIRST_EXECUTION === true) { + /* Starting begin and end */ + tradingEngine.current.episode.begin.value = tradingEngine.current.episode.candle.begin.value + tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.end.value + /* Getting the begin Balance from the session configuration */ tradingEngine.current.episode.episodeBaseAsset.beginBalance.value = sessionParameters.sessionBaseAsset.config.initialBalance tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value = sessionParameters.sessionQuotedAsset.config.initialBalance @@ -62,7 +66,6 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin tradingEngine.current.episode.status.value = 'Open' tradingEngine.current.episode.serialNumber.value = 1 tradingEngine.current.episode.identifier.value = global.UNIQUE_ID() - tradingEngine.current.episode.begin.value = tradingEngine.current.episode.candle.begin.value tradingEngine.current.episode.beginRate.value = tradingEngine.current.episode.candle.close.value } } @@ -73,7 +76,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin function closeEpisode() { tradingEngine.current.episode.status.value = 'Closed' - tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.begin.value + tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.end.value tradingEngine.current.episode.endRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.episode.episodeBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.episode.episodeQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value @@ -85,7 +88,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin function updateEnds() { if (tradingEngine.current.episode.status.value === 'Open') { - tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.episode.end.value = tradingEngine.current.episode.end.value + sessionParameters.timeFrame.config.value tradingEngine.current.episode.endRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.episode.episodeBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.episode.episodeQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value @@ -160,7 +163,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin tradingEngine.current.episode.episodeBaseAsset.hitRatio.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.hitRatio.value, 10) tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value, 10) } - + /* Updating Annualized Rate Of Return diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index 2ff04cd96..e4e233002 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -51,11 +51,14 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function openPosition(situationName) { + /* Starting begin and end */ + tradingEngine.current.position.begin.value = tradingEngine.current.episode.cycle.lastBegin.value + tradingEngine.current.position.end.value = tradingEngine.current.episode.cycle.lastEnd.value + /* Recording the opening at the Trading Engine Data Structure */ tradingEngine.current.position.status.value = 'Open' tradingEngine.current.position.serialNumber.value = tradingEngine.current.episode.episodeCounters.positions.value + 1 tradingEngine.current.position.identifier.value = global.UNIQUE_ID() - tradingEngine.current.position.begin.value = tradingEngine.current.episode.candle.begin.value tradingEngine.current.position.beginRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.position.positionBaseAsset.beginBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.position.positionQuotedAsset.beginBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value @@ -83,7 +86,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function closePosition() { tradingEngine.current.position.status.value = 'Closed' - tradingEngine.current.position.end.value = tradingEngine.current.episode.candle.begin.value + tradingEngine.current.position.end.value = tradingEngine.current.episode.cycle.lastEnd.value tradingEngine.current.position.endRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.position.positionBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.position.positionQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value @@ -94,17 +97,17 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng tradingEngineModule.cloneValues(tradingEngine.current.position, tradingEngine.last.position) cycleBasedStatistics() - + /* Updating Hits & Fails */ if (tradingEngine.current.position.positionBaseAsset.profitLoss.value > 0) { tradingEngine.current.episode.episodeBaseAsset.hits.value++ - } + } if (tradingEngine.current.position.positionBaseAsset.profitLoss.value < 0) { tradingEngine.current.episode.episodeBaseAsset.fails.value++ } if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value > 0) { tradingEngine.current.episode.episodeQuotedAsset.hits.value++ - } + } if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value < 0) { tradingEngine.current.episode.episodeQuotedAsset.fails.value++ } @@ -112,10 +115,27 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function applyStopLossFormula(formulas, formulaId) { tradingEngine.current.position.stopLoss.value = formulas.get(formulaId) + updateStopLossTakeProfitBeginEnd(tradingEngine.current.position.stopLoss) } function applyTakeProfitFormula(formulas, formulaId) { tradingEngine.current.position.takeProfit.value = formulas.get(formulaId) + updateStopLossTakeProfitBeginEnd(tradingEngine.current.position.takeProfit) + } + + function updateStopLossTakeProfitBeginEnd(node) { + /* + Both the Stop Loss and the Take Profit have their own Begin and End. + Ther reason for this is because they represent targets to be checked + at the next candle, so it does not apply that they share the begin and + end of the position itself. + */ + node.begin.value = + tradingEngine.current.episode.candle.begin.value + + sessionParameters.timeFrame.config.value + node.end.value = + tradingEngine.current.episode.candle.end.value + + sessionParameters.timeFrame.config.value } function updateStopLoss(phase) { @@ -253,7 +273,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function updateEnds() { if (tradingEngine.current.position.status.value === 'Open') { - tradingEngine.current.position.end.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.position.end.value = tradingEngine.current.position.end.value + sessionParameters.timeFrame.config.value tradingEngine.current.position.endRate.value = tradingEngine.current.episode.candle.close.value tradingEngine.current.position.positionBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngine.current.position.positionQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value @@ -305,7 +325,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng /* Hit Fail Calculation */ if (tradingEngine.current.position.positionBaseAsset.ROI.value > 0) { tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Hit' - } + } if (tradingEngine.current.position.positionBaseAsset.ROI.value < 0) { tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Fail' } @@ -314,7 +334,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng } if (tradingEngine.current.position.positionQuotedAsset.ROI.value > 0) { tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Hit' - } + } if (tradingEngine.current.position.positionQuotedAsset.ROI.value < 0) { tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Fail' } @@ -348,7 +368,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng /* Hit Fail Calculation */ if (tradingEngine.current.position.positionStatistics.ROI.value > 0) { tradingEngine.current.position.positionStatistics.hitFail.value = 'Hit' - } + } if (tradingEngine.current.position.positionStatistics.ROI.value < 0) { tradingEngine.current.position.positionStatistics.hitFail.value = 'Fail' } diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index 434e45f0f..e81b5503a 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -393,6 +393,8 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM function runManageStage() { + if (tradingEngine.current.episode.cycle.value !== 'First') { return } + runWhenStatusIsOpening() runWhenStatusIsOpen() @@ -595,7 +597,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM } announcementsModule.makeAnnoucements(moveToPhaseEvent) - + /* Reset this counter */ tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1 return // only one event can pass at the time @@ -786,12 +788,12 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM to be known before we start placing orders at the Close Stage. */ if ( - tradingEngine.current.strategyCloseStage.status.value === 'Open' && - ( - tradingEngine.current.strategyOpenStage.status.value === 'Closed' || - tradingEngine.current.strategyOpenStage.status.value == tradingEngine.current.strategyOpenStage.status.config.initialValue - ) - ) { + tradingEngine.current.strategyCloseStage.status.value === 'Open' && + ( + tradingEngine.current.strategyOpenStage.status.value === 'Closed' || + tradingEngine.current.strategyOpenStage.status.value == tradingEngine.current.strategyOpenStage.status.config.initialValue + ) + ) { /* This will happen as long as the Close Stage is Open. */ @@ -893,7 +895,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM when we see at the exchange that orders were filled. Note that the comparison is made with a Rounding Factor in order to avoid rounding problems. */ - const ROUNDING_ERROR_CORRECTION_FACTOR = 1.001 + const ROUNDING_ERROR_CORRECTION_FACTOR = 1.001 switch (tradingEngineStage.stageDefinedIn.value) { case 'Base Asset': { @@ -990,35 +992,50 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM closeStage(stage) } + /* + The way the begining and end of the Stages object works is as follows: + This object begins with the last cycle, that is the current cycle diplaced + backwards to the current candle. And it ends at the end of the last cycle. + During mantainance, we add the time frame of the candle, since we assume it + will survive the 2 cycles without the end being updated. If it closes, + then we update the end to the end of the last cycle, overrinding the + mantainance if needed. If we assume that in the same cycle there will be + no closing and opening of the same type of object, then there can be no + overlap. + */ function openStage(stage) { /* Recording the opening at the Trading Engine Data Structure */ - stage.begin.value = tradingEngine.current.episode.candle.begin.value + stage.begin.value = tradingEngine.current.episode.cycle.lastBegin.value + stage.end.value = tradingEngine.current.episode.cycle.lastEnd.value stage.beginRate.value = tradingEngine.current.episode.candle.close.value - stage.end.value = tradingEngine.current.episode.candle.begin.value } function closeStage(stage) { /* Recording the closing at the Trading Engine Data Structure */ - stage.end.value = tradingEngine.current.episode.candle.begin.value + stage.end.value = tradingEngine.current.episode.cycle.lastEnd.value stage.endRate.value = tradingEngine.current.episode.candle.close.value } } function updateEnds() { + /* + Note that we can not use the cycle here because this is executed via mantain + which in turn is executed before the first cycle for this candle is set. + */ if (tradingEngine.current.strategyTriggerStage.status.value === 'Open') { - tradingEngine.current.strategyTriggerStage.end.value = tradingEngine.current.episode.cycle.end.value + tradingEngine.current.strategyTriggerStage.end.value = tradingEngine.current.strategyTriggerStage.end.value + sessionParameters.timeFrame.config.value tradingEngine.current.strategyTriggerStage.endRate.value = tradingEngine.current.episode.candle.close.value } if (tradingEngine.current.strategyOpenStage.status.value === 'Open') { - tradingEngine.current.strategyOpenStage.end.value = tradingEngine.current.episode.cycle.end.value + tradingEngine.current.strategyOpenStage.end.value = tradingEngine.current.strategyOpenStage.end.value + sessionParameters.timeFrame.config.value tradingEngine.current.strategyOpenStage.endRate.value = tradingEngine.current.episode.candle.close.value } if (tradingEngine.current.strategyManageStage.status.value === 'Open') { - tradingEngine.current.strategyManageStage.end.value = tradingEngine.current.episode.cycle.end.value + tradingEngine.current.strategyManageStage.end.value = tradingEngine.current.strategyManageStage.end.value + sessionParameters.timeFrame.config.value tradingEngine.current.strategyManageStage.endRate.value = tradingEngine.current.episode.candle.close.value } if (tradingEngine.current.strategyCloseStage.status.value === 'Open') { - tradingEngine.current.strategyCloseStage.end.value = tradingEngine.current.episode.cycle.end.value + tradingEngine.current.strategyCloseStage.end.value = tradingEngine.current.strategyCloseStage.end.value + sessionParameters.timeFrame.config.value tradingEngine.current.strategyCloseStage.endRate.value = tradingEngine.current.episode.candle.close.value } } diff --git a/LowFrequencyTrading/TradingStrategy.js b/LowFrequencyTrading/TradingStrategy.js index 4ad903dad..9e9b8b8ce 100644 --- a/LowFrequencyTrading/TradingStrategy.js +++ b/LowFrequencyTrading/TradingStrategy.js @@ -13,15 +13,18 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng } let tradingEngine + let sessionParameters return thisObject function initialize() { tradingEngine = bot.simulationState.tradingEngine + sessionParameters = bot.SESSION.parameters } function finalize() { tradingEngine = undefined + sessionParameters = undefined } function mantain() { @@ -34,11 +37,14 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng } function openStrategy(index, situationName, strategyName) { + /* Starting begin and end */ + tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.cycle.lastBegin.value + tradingEngine.current.strategy.end.value = tradingEngine.current.episode.cycle.lastEnd.value + + /* Recording the opening at the Trading Engine Data Structure */ tradingEngine.current.strategy.status.value = 'Open' tradingEngine.current.strategy.serialNumber.value = tradingEngine.current.episode.episodeCounters.strategies.value tradingEngine.current.strategy.identifier.value = global.UNIQUE_ID() - tradingEngine.current.strategy.begin.value = tradingEngine.current.episode.candle.begin.value - tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.end.value tradingEngine.current.strategy.beginRate.value = tradingEngine.current.episode.candle.min.value tradingEngine.current.strategy.index.value = index @@ -52,7 +58,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng function closeStrategy(exitType) { tradingEngine.current.strategy.status.value = 'Closed' tradingEngine.current.strategy.exitType.value = exitType - tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.begin.value + tradingEngine.current.strategy.end.value = tradingEngine.current.episode.cycle.lastEnd.value tradingEngine.current.strategy.endRate.value = tradingEngine.current.episode.candle.min.value /* Now that the strategy is closed, it is the right time to move this strategy from current to last at the Trading Engine data structure. @@ -62,7 +68,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng function updateEnds() { if (tradingEngine.current.strategy.status.value === 'Open') { - tradingEngine.current.strategy.end.value = tradingEngine.current.episode.candle.end.value + tradingEngine.current.strategy.end.value = tradingEngine.current.strategy.end.value + sessionParameters.timeFrame.config.value tradingEngine.current.strategy.endRate.value = tradingEngine.current.episode.candle.close.value } } From bd6ef8e3d08f9422e6d564711fd305977d233177 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 22 Oct 2020 12:59:50 +0200 Subject: [PATCH 09/41] Last STOP and TP --- LowFrequencyTrading/TradingPosition.js | 9 + LowFrequencyTrading/TradingStages.js | 554 ++++++++++++------------- 2 files changed, 286 insertions(+), 277 deletions(-) diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index e4e233002..327b0478c 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -82,6 +82,15 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function closingPosition(exitType) { tradingEngine.current.position.status.value = 'Closing' tradingEngine.current.position.exitType.value = exitType + + /* + By the time we figured out that the stop loss or take profit were hit, or + for whatever reason the position needs to be closed, the take profit and + stop loss values for the next candle have already been calculated. In order + to avoid being plotted, we will put them in zero. + */ + tradingEngine.current.position.stopLoss.value = 0 + tradingEngine.current.position.takeProfit.value = 0 } function closePosition() { diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index e81b5503a..64d01d985 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -430,331 +430,331 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM /* Checking if Stop or Take Profit were hit */ checkStopLossOrTakeProfitWasHit() } - } - function calculateStopLoss() { - let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] - let manageStage = strategy.manageStage - let phase - - if (manageStage !== undefined) { - if (manageStage.managedStopLoss !== undefined) { - phase = manageStage.managedStopLoss.phases[tradingEngine.current.position.stopLoss.stopLossPhase.value - 1] - } - } - - if (phase !== undefined) { - if (phase.formula !== undefined) { - let previousValue = tradingEngine.current.position.stopLoss.value - tradingPositionModule.applyStopLossFormula(tradingSystem.formulas, phase.formula.id) - - if (tradingEngine.current.position.stopLoss.value !== previousValue) { - announcementsModule.makeAnnoucements(phase) + function calculateStopLoss() { + let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] + let manageStage = strategy.manageStage + let phase + + if (manageStage !== undefined) { + if (manageStage.managedStopLoss !== undefined) { + phase = manageStage.managedStopLoss.phases[tradingEngine.current.position.stopLoss.stopLossPhase.value - 1] } } - } - } - - function calculateTakeProfit() { - let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] - let manageStage = strategy.manageStage - let phase - - if (manageStage !== undefined) { - if (manageStage.managedTakeProfit !== undefined) { - phase = manageStage.managedTakeProfit.phases[tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1] - } - } - - if (phase !== undefined) { - if (phase.formula !== undefined) { - let previousValue = tradingEngine.current.position.takeProfit.value - tradingPositionModule.applyTakeProfitFormula(tradingSystem.formulas, phase.formula.id) - - if (tradingEngine.current.position.takeProfit.value !== previousValue) { - announcementsModule.makeAnnoucements(phase) - } - } - } - } - - function calculateStopLossPosition() { - /* - The position of the Stop Loss (above or below the price) is needed in order to know - later if the price hit the Stop Loss or not at every Simulation candle. When we get - the first values of the simulation of the Stop Loss we check if it is above or below - the Position Rate, and we assign the values Above or Below to it. - */ - if (tradingEngine.current.position.stopLoss.stopLossPosition.value === tradingEngine.current.position.stopLoss.stopLossPosition.config.initialValue) { - if (tradingEngine.current.position.stopLoss.value > tradingEngine.current.position.entryTargetRate.value) { - tradingEngine.current.position.stopLoss.stopLossPosition.value = 'Above' - } else { - tradingEngine.current.position.stopLoss.stopLossPosition.value = 'Below' - } - } - } - - function calculateTakeProfitPosition() { - /* - The position of the Take Profit (above or below the price) is needed in order to know - later if the price hit the Take Profit or not at every Simulation candle. When we get - the first values of the simulation of the Take Profits we check if it is above or below - the Position Rates, and we assign the values Above or Below to it. - */ - if (tradingEngine.current.position.takeProfit.takeProfitPosition.value === tradingEngine.current.position.takeProfit.takeProfitPosition.config.initialValue) { - if (tradingEngine.current.position.takeProfit.value > tradingEngine.current.position.entryTargetRate.value) { - tradingEngine.current.position.takeProfit.takeProfitPosition.value = 'Above' - } else { - tradingEngine.current.position.takeProfit.takeProfitPosition.value = 'Below' - } - } - } - - function checkStopPhasesEvents() { - let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] - let manageStage = strategy.manageStage - let parentNode - let phaseIndex - let phase - let stopLoss - - if (manageStage !== undefined) { - if (manageStage.managedStopLoss !== undefined) { - parentNode = manageStage - phaseIndex = tradingEngine.current.position.stopLoss.stopLossPhase.value - 1 - stopLoss = manageStage.managedStopLoss - phase = stopLoss.phases[phaseIndex] - } - } - - if (parentNode !== undefined) { - if (phase === undefined) { return } // trying to jump to a phase that does not exists. - - /* Check the next Phase Event. */ - let nextPhaseEvent = phase.nextPhaseEvent - if (nextPhaseEvent !== undefined) { - for (let k = 0; k < nextPhaseEvent.situations.length; k++) { - let situation = nextPhaseEvent.situations[k] - let passed - if (situation.conditions.length > 0) { - passed = true - } - - passed = tradingSystem.checkConditions(situation, passed) - - tradingSystem.values.push([situation.id, passed]) - if (passed) { - tradingSystem.highlights.push(situation.id) - tradingSystem.highlights.push(nextPhaseEvent.id) - tradingSystem.highlights.push(phase.id) - tradingSystem.highlights.push(stopLoss.id) - tradingSystem.highlights.push(parentNode.id) - tradingSystem.highlights.push(manageStage.id) - - tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1) - - announcementsModule.makeAnnoucements(nextPhaseEvent) - - /* Reset this counter */ - tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1 - return // only one event can pass at the time + + if (phase !== undefined) { + if (phase.formula !== undefined) { + let previousValue = tradingEngine.current.position.stopLoss.value + tradingPositionModule.applyStopLossFormula(tradingSystem.formulas, phase.formula.id) + + if (tradingEngine.current.position.stopLoss.value !== previousValue) { + announcementsModule.makeAnnoucements(phase) } } } - - /* Check the Move to Phase Events. */ - for (let n = 0; n < phase.moveToPhaseEvents.length; n++) { - let moveToPhaseEvent = phase.moveToPhaseEvents[n] - if (moveToPhaseEvent !== undefined) { - for (let k = 0; k < moveToPhaseEvent.situations.length; k++) { - let situation = moveToPhaseEvent.situations[k] + } + + function calculateTakeProfit() { + let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] + let manageStage = strategy.manageStage + let phase + + if (manageStage !== undefined) { + if (manageStage.managedTakeProfit !== undefined) { + phase = manageStage.managedTakeProfit.phases[tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1] + } + } + + if (phase !== undefined) { + if (phase.formula !== undefined) { + let previousValue = tradingEngine.current.position.takeProfit.value + tradingPositionModule.applyTakeProfitFormula(tradingSystem.formulas, phase.formula.id) + + if (tradingEngine.current.position.takeProfit.value !== previousValue) { + announcementsModule.makeAnnoucements(phase) + } + } + } + } + + function calculateStopLossPosition() { + /* + The position of the Stop Loss (above or below the price) is needed in order to know + later if the price hit the Stop Loss or not at every Simulation candle. When we get + the first values of the simulation of the Stop Loss we check if it is above or below + the Position Rate, and we assign the values Above or Below to it. + */ + if (tradingEngine.current.position.stopLoss.stopLossPosition.value === tradingEngine.current.position.stopLoss.stopLossPosition.config.initialValue) { + if (tradingEngine.current.position.stopLoss.value > tradingEngine.current.position.entryTargetRate.value) { + tradingEngine.current.position.stopLoss.stopLossPosition.value = 'Above' + } else { + tradingEngine.current.position.stopLoss.stopLossPosition.value = 'Below' + } + } + } + + function calculateTakeProfitPosition() { + /* + The position of the Take Profit (above or below the price) is needed in order to know + later if the price hit the Take Profit or not at every Simulation candle. When we get + the first values of the simulation of the Take Profits we check if it is above or below + the Position Rates, and we assign the values Above or Below to it. + */ + if (tradingEngine.current.position.takeProfit.takeProfitPosition.value === tradingEngine.current.position.takeProfit.takeProfitPosition.config.initialValue) { + if (tradingEngine.current.position.takeProfit.value > tradingEngine.current.position.entryTargetRate.value) { + tradingEngine.current.position.takeProfit.takeProfitPosition.value = 'Above' + } else { + tradingEngine.current.position.takeProfit.takeProfitPosition.value = 'Below' + } + } + } + + function checkStopPhasesEvents() { + let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] + let manageStage = strategy.manageStage + let parentNode + let phaseIndex + let phase + let stopLoss + + if (manageStage !== undefined) { + if (manageStage.managedStopLoss !== undefined) { + parentNode = manageStage + phaseIndex = tradingEngine.current.position.stopLoss.stopLossPhase.value - 1 + stopLoss = manageStage.managedStopLoss + phase = stopLoss.phases[phaseIndex] + } + } + + if (parentNode !== undefined) { + if (phase === undefined) { return } // trying to jump to a phase that does not exists. + + /* Check the next Phase Event. */ + let nextPhaseEvent = phase.nextPhaseEvent + if (nextPhaseEvent !== undefined) { + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + let situation = nextPhaseEvent.situations[k] let passed if (situation.conditions.length > 0) { passed = true } - + passed = tradingSystem.checkConditions(situation, passed) - + tradingSystem.values.push([situation.id, passed]) if (passed) { tradingSystem.highlights.push(situation.id) - tradingSystem.highlights.push(moveToPhaseEvent.id) + tradingSystem.highlights.push(nextPhaseEvent.id) tradingSystem.highlights.push(phase.id) tradingSystem.highlights.push(stopLoss.id) tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(manageStage.id) - - let moveToPhase = moveToPhaseEvent.referenceParent - if (moveToPhase !== undefined) { - for (let q = 0; q < stopLoss.phases.length; q++) { - if (stopLoss.phases[q].id === moveToPhase.id) { - tradingPositionModule.updateStopLoss(q + 1) - } - } - } else { - tradingSystem.errors.push([moveToPhaseEvent.id, 'This Node needs to reference a Phase.']) - continue - } - - announcementsModule.makeAnnoucements(moveToPhaseEvent) - + + tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1) + + announcementsModule.makeAnnoucements(nextPhaseEvent) + /* Reset this counter */ - tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1 + tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1 return // only one event can pass at the time } } } + + /* Check the Move to Phase Events. */ + for (let n = 0; n < phase.moveToPhaseEvents.length; n++) { + let moveToPhaseEvent = phase.moveToPhaseEvents[n] + if (moveToPhaseEvent !== undefined) { + for (let k = 0; k < moveToPhaseEvent.situations.length; k++) { + let situation = moveToPhaseEvent.situations[k] + let passed + if (situation.conditions.length > 0) { + passed = true + } + + passed = tradingSystem.checkConditions(situation, passed) + + tradingSystem.values.push([situation.id, passed]) + if (passed) { + tradingSystem.highlights.push(situation.id) + tradingSystem.highlights.push(moveToPhaseEvent.id) + tradingSystem.highlights.push(phase.id) + tradingSystem.highlights.push(stopLoss.id) + tradingSystem.highlights.push(parentNode.id) + tradingSystem.highlights.push(manageStage.id) + + let moveToPhase = moveToPhaseEvent.referenceParent + if (moveToPhase !== undefined) { + for (let q = 0; q < stopLoss.phases.length; q++) { + if (stopLoss.phases[q].id === moveToPhase.id) { + tradingPositionModule.updateStopLoss(q + 1) + } + } + } else { + tradingSystem.errors.push([moveToPhaseEvent.id, 'This Node needs to reference a Phase.']) + continue + } + + announcementsModule.makeAnnoucements(moveToPhaseEvent) + + /* Reset this counter */ + tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1 + return // only one event can pass at the time + } + } + } + } } } - } - - function checkTakeProfitPhaseEvents() { - let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] - let openStage = strategy.openStage - let manageStage = strategy.manageStage - let parentNode - let phaseIndex - let phase - let takeProfit - - if (manageStage !== undefined) { - if (manageStage.managedTakeProfit !== undefined) { - parentNode = manageStage - phaseIndex = tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1 - takeProfit = manageStage.managedTakeProfit - phase = takeProfit.phases[phaseIndex] - } - } - - if (parentNode !== undefined) { - if (phase === undefined) { return } // trying to jump to a phase that does not exists. - - /* Check the next Phase Event. */ - let nextPhaseEvent = phase.nextPhaseEvent - if (nextPhaseEvent !== undefined) { - for (let k = 0; k < nextPhaseEvent.situations.length; k++) { - let situation = nextPhaseEvent.situations[k] - let passed - if (situation.conditions.length > 0) { - passed = true - } - - passed = tradingSystem.checkConditions(situation, passed) - - tradingSystem.values.push([situation.id, passed]) - if (passed) { - tradingSystem.highlights.push(situation.id) - tradingSystem.highlights.push(nextPhaseEvent.id) - tradingSystem.highlights.push(phase.id) - tradingSystem.highlights.push(takeProfit.id) - tradingSystem.highlights.push(parentNode.id) - tradingSystem.highlights.push(manageStage.id) - - tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1) - - announcementsModule.makeAnnoucements(nextPhaseEvent) - - /* Reset this counter */ - tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1 - return // only one event can pass at the time - } + + function checkTakeProfitPhaseEvents() { + let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] + let openStage = strategy.openStage + let manageStage = strategy.manageStage + let parentNode + let phaseIndex + let phase + let takeProfit + + if (manageStage !== undefined) { + if (manageStage.managedTakeProfit !== undefined) { + parentNode = manageStage + phaseIndex = tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1 + takeProfit = manageStage.managedTakeProfit + phase = takeProfit.phases[phaseIndex] } } - - /* Check the Move to Phase Events. */ - for (let n = 0; n < phase.moveToPhaseEvents.length; n++) { - let moveToPhaseEvent = phase.moveToPhaseEvents[n] - if (moveToPhaseEvent !== undefined) { - for (let k = 0; k < moveToPhaseEvent.situations.length; k++) { - let situation = moveToPhaseEvent.situations[k] + + if (parentNode !== undefined) { + if (phase === undefined) { return } // trying to jump to a phase that does not exists. + + /* Check the next Phase Event. */ + let nextPhaseEvent = phase.nextPhaseEvent + if (nextPhaseEvent !== undefined) { + for (let k = 0; k < nextPhaseEvent.situations.length; k++) { + let situation = nextPhaseEvent.situations[k] let passed if (situation.conditions.length > 0) { passed = true } - + passed = tradingSystem.checkConditions(situation, passed) - + tradingSystem.values.push([situation.id, passed]) if (passed) { tradingSystem.highlights.push(situation.id) - tradingSystem.highlights.push(moveToPhaseEvent.id) + tradingSystem.highlights.push(nextPhaseEvent.id) tradingSystem.highlights.push(phase.id) tradingSystem.highlights.push(takeProfit.id) tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(manageStage.id) - - let moveToPhase = moveToPhaseEvent.referenceParent - if (moveToPhase !== undefined) { - for (let q = 0; q < takeProfit.phases.length; q++) { - if (takeProfit.phases[q].id === moveToPhase.id) { - tradingPositionModule.updateTakeProfit(q + 1) - } - } - } else { - tradingSystem.errors.push([moveToPhaseEvent.id, 'This Node needs to reference a Phase.']) - continue - } - - announcementsModule.makeAnnoucements(moveToPhaseEvent) - + + tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1) + + announcementsModule.makeAnnoucements(nextPhaseEvent) + /* Reset this counter */ - tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1 + tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1 return // only one event can pass at the time } } } + + /* Check the Move to Phase Events. */ + for (let n = 0; n < phase.moveToPhaseEvents.length; n++) { + let moveToPhaseEvent = phase.moveToPhaseEvents[n] + if (moveToPhaseEvent !== undefined) { + for (let k = 0; k < moveToPhaseEvent.situations.length; k++) { + let situation = moveToPhaseEvent.situations[k] + let passed + if (situation.conditions.length > 0) { + passed = true + } + + passed = tradingSystem.checkConditions(situation, passed) + + tradingSystem.values.push([situation.id, passed]) + if (passed) { + tradingSystem.highlights.push(situation.id) + tradingSystem.highlights.push(moveToPhaseEvent.id) + tradingSystem.highlights.push(phase.id) + tradingSystem.highlights.push(takeProfit.id) + tradingSystem.highlights.push(parentNode.id) + tradingSystem.highlights.push(manageStage.id) + + let moveToPhase = moveToPhaseEvent.referenceParent + if (moveToPhase !== undefined) { + for (let q = 0; q < takeProfit.phases.length; q++) { + if (takeProfit.phases[q].id === moveToPhase.id) { + tradingPositionModule.updateTakeProfit(q + 1) + } + } + } else { + tradingSystem.errors.push([moveToPhaseEvent.id, 'This Node needs to reference a Phase.']) + continue + } + + announcementsModule.makeAnnoucements(moveToPhaseEvent) + + /* Reset this counter */ + tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1 + return // only one event can pass at the time + } + } + } + } } } - } - - function checkStopLossOrTakeProfitWasHit() { - { - let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] - /* - Checking what happened since the last execution. We need to know if the Stop Loss - or our Take Profit were hit. - */ - - /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ - if ( - ( - tradingEngine.current.position.stopLoss.stopLossPosition.value === 'Above' && - tradingEngine.current.episode.candle.max.value >= tradingEngine.current.position.stopLoss.value - ) || - ( - tradingEngine.current.position.stopLoss.stopLossPosition.value === 'Below' && - tradingEngine.current.episode.candle.min.value <= tradingEngine.current.position.stopLoss.value - ) - ) { - logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Stop Loss was hit.') - - tradingPositionModule.closingPosition('Stop Loss') - changeStageStatus('Close Stage', 'Opening') - changeStageStatus('Manage Stage', 'Closed') - announcementsModule.makeAnnoucements(strategy.closeStage) + + function checkStopLossOrTakeProfitWasHit() { + { + let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] + /* + Checking what happened since the last execution. We need to know if the Stop Loss + or our Take Profit were hit. + */ + + /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ + if ( + ( + tradingEngine.current.position.stopLoss.stopLossPosition.value === 'Above' && + tradingEngine.current.episode.candle.max.value >= tradingEngine.current.position.stopLoss.value + ) || + ( + tradingEngine.current.position.stopLoss.stopLossPosition.value === 'Below' && + tradingEngine.current.episode.candle.min.value <= tradingEngine.current.position.stopLoss.value + ) + ) { + logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Stop Loss was hit.') + + tradingPositionModule.closingPosition('Stop Loss') + changeStageStatus('Close Stage', 'Opening') + changeStageStatus('Manage Stage', 'Closed') + announcementsModule.makeAnnoucements(strategy.closeStage) + return + } + + /* Take Profit condition: Here we verify if the Take Profit was hit or not. */ + if ( + ( + tradingEngine.current.position.takeProfit.takeProfitPosition.value === 'Below' && + tradingEngine.current.episode.candle.min.value <= tradingEngine.current.position.takeProfit.value + ) || + ( + tradingEngine.current.position.takeProfit.takeProfitPosition.value === 'Above' && + tradingEngine.current.episode.candle.max.value >= tradingEngine.current.position.takeProfit.value + ) + ) { + logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Take Profit was hit.') + + tradingPositionModule.closingPosition('Take Profit') + changeStageStatus('Close Stage', 'Opening') + changeStageStatus('Manage Stage', 'Closed') + announcementsModule.makeAnnoucements(strategy.closeStage) + return + } return } - - /* Take Profit condition: Here we verify if the Take Profit was hit or not. */ - if ( - ( - tradingEngine.current.position.takeProfit.takeProfitPosition.value === 'Below' && - tradingEngine.current.episode.candle.min.value <= tradingEngine.current.position.takeProfit.value - ) || - ( - tradingEngine.current.position.takeProfit.takeProfitPosition.value === 'Above' && - tradingEngine.current.episode.candle.max.value >= tradingEngine.current.position.takeProfit.value - ) - ) { - logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Take Profit was hit.') - - tradingPositionModule.closingPosition('Take Profit') - changeStageStatus('Close Stage', 'Opening') - changeStageStatus('Manage Stage', 'Closed') - announcementsModule.makeAnnoucements(strategy.closeStage) - return - } - return } } } From bfdb411601f255a886cbe58b35a40044ca2c3a5c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 22 Oct 2020 14:51:24 +0200 Subject: [PATCH 10/41] Update TradingPosition.js --- LowFrequencyTrading/TradingPosition.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index 327b0478c..1dcf49727 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -123,15 +123,38 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng } function applyStopLossFormula(formulas, formulaId) { + updateStopLossTakeProfitFinalValue(tradingEngine.current.position.stopLoss) tradingEngine.current.position.stopLoss.value = formulas.get(formulaId) + updateStopLossTakeProfitInitialValue(tradingEngine.current.position.stopLoss) updateStopLossTakeProfitBeginEnd(tradingEngine.current.position.stopLoss) } function applyTakeProfitFormula(formulas, formulaId) { + updateStopLossTakeProfitFinalValue(tradingEngine.current.position.takeProfit) tradingEngine.current.position.takeProfit.value = formulas.get(formulaId) + updateStopLossTakeProfitInitialValue(tradingEngine.current.position.takeProfit) updateStopLossTakeProfitBeginEnd(tradingEngine.current.position.takeProfit) } + function updateStopLossTakeProfitInitialValue(node) { + /* + We store the first Stop Loss or Take Profit value in a separate node. + To know if it is the first one we compare its current value to the initial value. + */ + if (node.initialValue.value === node.initialValue.config.initialValue) { + node.initialValue.value = node.value + } + } + + function updateStopLossTakeProfitFinalValue(node) { + /* + We set the final value just before calcualting the new value, since the new calculated + value is going to be discarded if the position is closed after it is calculated, but + during the same candle. + */ + node.finalValue.value = node.value + } + function updateStopLossTakeProfitBeginEnd(node) { /* Both the Stop Loss and the Take Profit have their own Begin and End. From b3b4c7f4ad2858bf004c01663780cf32765bfd94 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 23 Oct 2020 19:41:50 +0200 Subject: [PATCH 11/41] Actual Size, Size to be Filled and Fees To be Paid --- LowFrequencyTrading/TradingOrders.js | 417 ++++++++++++++++++-------- LowFrequencyTrading/TradingRecords.js | 5 +- LowFrequencyTrading/TradingStages.js | 4 + 3 files changed, 306 insertions(+), 120 deletions(-) diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 76d1cabe0..2b55c08b7 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -236,10 +236,16 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (tradingEngineOrder.orderBaseAsset.size === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.size === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderBaseAsset.sizeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.sizeFilled === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderBaseAsset.feesPaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.feesPaid === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderBaseAsset.actualSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.actualSize === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderBaseAsset.sizeToBeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.sizeToBeFilled === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderBaseAsset.feesToBePaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.feesToBePaid === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.size === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.size === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.sizeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.sizeFilled === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.feesPaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.feesPaid === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderQuotedAsset.actualSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.actualSize === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderQuotedAsset.sizeToBeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.sizeToBeFilled === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderQuotedAsset.feesToBePaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.feesToBePaid === undefined', tradingEngineOrder) } } async function tryToOpenOrder(tradingEngineStage, executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName) { @@ -407,13 +413,15 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM } } - let previousQuotedAssetSize = tradingEngineOrder.orderQuotedAsset.size.value let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value + actualSizeSimulation() actualRateSimulation() + feesToBePaidSimulation() + sizeToBeFilledSimulation() percentageFilledSimulation() feesPaidSimulation() sizeFilledSimulation() @@ -433,14 +441,26 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM simulateCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, 'Closing Stage') } + async function actualSizeSimulation() { + /* + While backtesting and paper trading, we do not really need to change + simulate an Actual Size. We will just copy to that field the + size of the order. + */ + tradingEngineOrder.orderBaseAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.size.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderQuotedAsset.size.value + + } + function actualRateSimulation() { /* Actual Rate Simulation */ let calculatedBasedOnTradingSystem = false + let previousQuotedAssetActualSize basedOnTradingSystem() basedOnSessionParameters() considerBestMatchWithOrderBook() - recalculateQuotedAsset() + recalculateActualSize() recalculateSizePlaced() function basedOnTradingSystem() { @@ -513,49 +533,100 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) } - function recalculateQuotedAsset() { + function recalculateActualSize() { /* - Recalculate the Quouted Asset Size based on the newly calculated Actual Rate. + The Quoted Asset Actual Size in a backtest is equal to the Quoted Asset Size, which is related to + the Base Asset Size by the Order Rate. Since now we have the Actual Rate, we will need to recalculate + the Quoted Asset Actual Size using the Actual Rate. */ - tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.orderStatistics.actualRate.value - tradingEngineOrder.orderQuotedAsset.size.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.size.value, 10) + previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value + + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) } function recalculateSizePlaced() { /* - Recaulculate the Size Placed Based on the newly calculated Quoted Asset Size. + For the Quoted Asset, the Size Placed was previously calculated with the previous Actual Size (or Size when these were equal). + Since we have just recaclcualted the Actual Size in Quoted Asset, we need to fix the Size Placed for the stage which was + calculated with the previous value. */ tradingEngineStage.stageQuotedAsset.sizePlaced.value = tradingEngineStage.stageQuotedAsset.sizePlaced.value - - previousQuotedAssetSize + previousQuotedAssetActualSize tradingEngineStage.stageQuotedAsset.sizePlaced.value = tradingEngineStage.stageQuotedAsset.sizePlaced.value + - tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineOrder.orderQuotedAsset.actualSize.value tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) } } + async function feesToBePaidSimulation() { + /* + The Fees To Be Paid are the fees that will be paid once the order is 100% Filled. + */ + switch (tradingEngineOrder.type) { + case 'Market Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + 100 + ) + break + } + case 'Limit Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + 100 + ) + break + } + } + } + + async function sizeToBeFilledSimulation() { + /* + The Size to Be Filled is the Actual Size minus the Fees to be Paid. + */ + tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = + tradingEngineOrder.orderBaseAsset.actualSize.value - + tradingEngineOrder.orderBaseAsset.feesToBePaid.value + + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = + tradingEngineOrder.orderQuotedAsset.actualSize.value - + tradingEngineOrder.orderQuotedAsset.feesToBePaid.value + } + function percentageFilledSimulation() { /* - Order Filling Simulation: the way we are going to handle this is the following. - For buy orders we are going to check if the price was below the order rate, and - for sell orders we are going to check if the price was above the order rate. + When we are live, it is the exchange who knows which order was filled and in + which amount. Since we are backtesting here, we need to simulate what happens + at the exchange. We can not simulate that an order is filled if the candle we + can see did not touch the order. + + For buy orders we are going to check if the price was below the order actual rate, and + for sell orders we are going to check if the price was above the order actual rate. - If we have a partial filling defined we are going to use it to estimate how much - of the order we will consider filled. + If we have a partial filling defined specifically for this order, + we are going to use it to estimate how much of the order we will consider filled. */ let orderWasHit switch (tradingSystemOrder.type) { case 'Limit Buy Order': { - if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.rate.value) { + if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.actualRate.value) { orderWasHit = true } break } case 'Limit Sell Order': { - if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.rate.value) { + if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.actualRate.value) { orderWasHit = true } break @@ -590,7 +661,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM } /* If there is no definition for this at the trading system we will assume orders - are 100% filled every single time. + are 100% filled every single time they are hit, which is the most likely thing to happen. */ if (calculatedBasedOnTradingSystem === false) { /* We will assume that 100% of the order was filled */ @@ -626,7 +697,13 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) { if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) { - applyFeePercentage(tradingEngineOrder, tradingSystemOrder, tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage) + applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) return } } @@ -637,24 +714,41 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM */ switch (tradingEngineOrder.type) { case 'Market Order': { - applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker) + applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) break } case 'Limit Order': { - applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker) + applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) break } } } function sizeFilledSimulation() { - /* Size Filled */ + /* + Size Filled is the amount of Base Asset or Quoted Asset that at the exchange matches + another counterparty order. We alredy have simulated the Percentage Filled and the + Size To Be Filled, which is the order Actual Size minus the Fees To Be Paid. + */ + tradingEngineOrder.orderBaseAsset.sizeFilled.value = - tradingEngineOrder.orderBaseAsset.size.value * + tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.size.value * + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) @@ -704,7 +798,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM await updateEndsWithCycle(tradingEngineOrder) tradingSystem.infos.push([tradingSystemOrder.id, 'checkExchangeEvents -> Closing Order with Exit Type ' + tradingEngineOrder.exitType.value]) - + await syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order) return } @@ -769,8 +863,6 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM async function syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order) { - let previousBaseAssetSize = tradingEngineOrder.orderBaseAsset.size.value - let previousQuotedAssetSize = tradingEngineOrder.orderQuotedAsset.size.value let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value @@ -778,6 +870,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM await actualSizeCalculation() await actualRateCalculation() + await feesToBePaidCalculation() + await sizeToBeFilledCalculation() await percentageFilledCalculation() await feesPaidCalculation() await sizeFilledCalculation() @@ -797,46 +891,63 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM async function actualSizeCalculation() { /* When we submit the order to the exchange, we do it specifying the order size - in Base Asset. The exchange might take that size or might change it a little - bit if for example, if it does not accept as many decimals as we are sending. + in Base Asset. It is mandatory to be in Base Asset as per CCXT Library API. + + The exchange might take that size or might change it a little + bit. For example, if it does not accept as many decimals as we are sending. If we identify the situation in which the size accepted is different than the size we have accounted for, we need to make several adjustments so that the accounting syncronizes with reality. */ - if (tradingEngineOrder.orderBaseAsset.size.value === order.amount) { return } - /* - We will sync the Base Asset size, and recalculate the Quoted Asset size too. + + /* This calculation only happens once, the first time the order is checked. */ + if (tradingEngineOrder.orderBaseAsset.actualSize.value !== tradingEngineOrder.orderBaseAsset.actualSize.config.initialValue) { return } + + /* We receive the actual size from the exchange at the order.amount field. */ + tradingEngineOrder.orderBaseAsset.actualSize.value = order.amount + + /* + If the exchange accepted the size sent on the request to create the order, + we don't need to do anything else here. */ - tradingEngineOrder.orderBaseAsset.size.value = order.amount - tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value - - tradingEngineOrder.orderBaseAsset.size.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.size.value, 10) - tradingEngineOrder.orderQuotedAsset.size.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.size.value, 10) + if (tradingEngineOrder.orderBaseAsset.size.value === tradingEngineOrder.orderBaseAsset.actualSize.value) { return } /* - Finaly we need to unaccount the previous size placed and correctly account - for the the new sizes we now have. - */ - tradingEngineStage.stageBaseAsset.sizePlaced.value = - tradingEngineStage.stageBaseAsset.sizePlaced.value - - previousBaseAssetSize + If not, we will calculate the Quoted Asset Actual Size too. Note that the amount received + is in Base Asset only. In the next formula, we are useing the rate of the order. + This rate might be replaced afteerwards for an actual rate. When that happens we will + need to adjust these results. + */ + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.rate.value - tradingEngineStage.stageBaseAsset.sizePlaced.value = - tradingEngineStage.stageBaseAsset.sizePlaced.value + - tradingEngineOrder.orderBaseAsset.size.value + tradingEngineOrder.orderBaseAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.actualSize.value, 10) + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value - - previousQuotedAssetSize + recalculateSizePlaced() - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value + - tradingEngineOrder.orderQuotedAsset.size.value + function recalculateSizePlaced() { + /* + Wee also need to unaccount the previous size placed and correctly account + for the the new actual sizes we now have. + */ + tradingEngineStage.stageBaseAsset.sizePlaced.value = + tradingEngineStage.stageBaseAsset.sizePlaced.value - + tradingEngineOrder.orderBaseAsset.size.value - tradingEngineStage.stageBaseAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizePlaced.value, 10) - tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + tradingEngineStage.stageBaseAsset.sizePlaced.value = + tradingEngineStage.stageBaseAsset.sizePlaced.value + + tradingEngineOrder.orderBaseAsset.actualSize.value - previousBaseAssetSize = tradingEngineOrder.orderBaseAsset.size.value - previousQuotedAssetSize = tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value - + tradingEngineOrder.orderQuotedAsset.size.value + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value + + tradingEngineOrder.orderQuotedAsset.actualSize.value + + tradingEngineStage.stageBaseAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizePlaced.value, 10) + tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + } } async function actualRateCalculation() { @@ -866,56 +977,124 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) + /* + If the Actual Rate happens to be exctly the same than the order rate, then there is + nothing else to do here. Otherwise there are adjustments to be made. + */ if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return } - /* - Now we know the Actual Rate at which the order was filled. Since the actual rate - is not the same as the Rate we defined for the order, we need to syncronize - the Order Size for Quoted Asset since it was calculated with the Order Size that we - now know it is not the once really used at the exchange. We will recalculate the - Order Size in Quoted Asset and not in Base Asset, since the Order Size in Base Asset - is the one we sent to the exchange, so that is fixed, regardless of how we initially - got the Order Size in Base Asset, eigher from direct imput from the user or calculated - from the Order Size in Quoted Asset input it by the user. So here we go. - */ - tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.orderStatistics.actualRate.value - tradingEngineOrder.orderQuotedAsset.size.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.size.value, 10) - /* - Great, this up-to-date order size in Base Asset and the Actual Rate can now be used for example to calculate - the orders fees in Quoted Assets, and it is perfectly accurate. The next problem - we need to fix is that the sizePlaced for Quoted Asset that we already have calculated - was based on the Order Rate, not the Actual Rate, so we need to fix that by anuncounting - the previous size and accounting the new one, all this only for Quoted Asset. - */ - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value - - previousQuotedAssetSize - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value + - tradingEngineOrder.orderQuotedAsset.size.value + let previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value + recalculateActualSize() + recalculateSizePlaced() - tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + function recalculateActualSize() { + /* + Now we know the Actual Rate at which the order was filled. Since the actual rate + is not the same as the Rate we defined for the order, we need to syncronize + the Actual Order Size for Quoted Asset since it was calculated with the Order Size that we + now know it is not the one really used at the exchange. We will recalculate the + Actual Size in Quoted Asset and not in Base Asset, since the Actual Size in Base Asset + is the one we accepted by the exchange, so that is fixed, regardless of how we initially + got the Order Size in Base Asset, either from direct imput from the user or calculated + from the Order Size in Quoted Asset input it by the user. So here we go. + */ + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + } + + function recalculateSizePlaced() { + /* + Since we changed the Actual Size in Quoted Asset, we need to fix the Size Placed in Quoted Asset + that was previously calculated with the previous value of actualSize. + */ + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value - + previousQuotedAssetActualSize + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value + + tradingEngineOrder.orderQuotedAsset.actualSize.value + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + } + } + + async function feesToBePaidCalculation() { + /* + The Fees To Be Paid are the fees that will be paid once the order is 100% Filled. + */ + switch (tradingEngineOrder.type) { + case 'Market Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + 100 + ) + break + } + case 'Limit Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + 100 + ) + break + } + } + } + + async function sizeToBeFilledCalculation() { + /* + The Size to Be Filled is the Actual Size minus the Fees to be Paid. + */ + tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = + tradingEngineOrder.orderBaseAsset.actualSize.value - + tradingEngineOrder.orderBaseAsset.feesToBePaid.value + + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = + tradingEngineOrder.orderQuotedAsset.actualSize.value - + tradingEngineOrder.orderQuotedAsset.feesToBePaid.value } async function percentageFilledCalculation() { - /* Percentage Filled Calculation */ - tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (order.filled + order.remaining) + /* + Percentage Filled Calculation: The only information we get from the exchange is the order.filled. + We know this field will go up until reaching the Size To Be Filled. Once it reaches that number + the order will be 100% filled. + */ + tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value) tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10) } async function feesPaidCalculation() { /* The exchange fees are taken from the Base Asset or the Quoted Asset depending if we - are buying or selling. Within the order information received we can not see the fees paid so we need to - calculate them by ourselves. To do this it is needed to be used the Session Fees Parameters. + are buying or selling. Within the order information received from the exchange we can not see the fees paid so we need to + calculate them ourselves. To do this it is needed to be used the Session Fees Parameters. */ switch (tradingEngineOrder.type) { case 'Market Order': { - await applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker) + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) break } case 'Limit Order': { - await applyFeePercentage(tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker) + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) break } } @@ -936,7 +1115,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM this in this formula to get the Size Filled in Quoted Asset. */ tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.size.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) } @@ -1003,65 +1182,65 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* Balances Update */ switch (true) { /* - Let's remember that for Buy orders, the fees are paid in Base Asset. So we - actually don't receive the whole Size Filled in Base Asset, but the Size - Filled minus the Fees Paid. For the case of Quoted Asset, we actually - need to susbstract the whole Size Filled from the balance. + Let's remember that for Buy orders, the fees are paid in Base Asset.So we + need to substract from the balance of Quoted Asset both the size filled and + the fees, and we will receive at the balance of Base Asset just the size filled, + bacuase the exchange gets the fees in Base Asset for them. */ case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { /* Balance Base Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value - - previousBaseAssetSizeFilled + - previousBaseAssetFeesPaid + previousBaseAssetSizeFilled /* Balance Base Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value + - tradingEngineOrder.orderBaseAsset.sizeFilled.value - - tradingEngineOrder.orderBaseAsset.feesPaid.value + tradingEngineOrder.orderBaseAsset.sizeFilled.value /* Balance Quoted Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value + - previousQuotedAssetSizeFilled + previousQuotedAssetSizeFilled + + previousQuotedAssetFeesPaid /* Balance Quoted Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value - - tradingEngineOrder.orderQuotedAsset.sizeFilled.value + tradingEngineOrder.orderQuotedAsset.sizeFilled.value - + tradingEngineOrder.orderQuotedAsset.feesPaid.value break } /* - For Sell orders, the fees are being paid in Quote Asset, so we never - acutally receive the whole Filled Size in Quote Asset, but the Filled - Size minus the Fees Paid. But we do expend the whole Filled Size in - Base Asset. + For Sell orders, the fees are being paid in Quote Asset, so we will substract + from the Base Asset balance the size filled and the fees, and we will add + to the Quoted Asset balance only the size filled, because the exchange + get those fees. */ case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { /* Balance Base Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value + - previousBaseAssetSizeFilled + previousBaseAssetSizeFilled + + previousBaseAssetFeesPaid /* Balance Base Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value - - tradingEngineOrder.orderBaseAsset.sizeFilled.value + tradingEngineOrder.orderBaseAsset.sizeFilled.value - + tradingEngineOrder.orderBaseAsset.feesPaid.value /* Balance Quoted Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value - - previousQuotedAssetSizeFilled + - previousQuotedAssetFeesPaid + previousQuotedAssetSizeFilled /* Balance Quoted Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value + - tradingEngineOrder.orderQuotedAsset.sizeFilled.value - - tradingEngineOrder.orderQuotedAsset.feesPaid.value + tradingEngineOrder.orderQuotedAsset.sizeFilled.value break } } @@ -1164,15 +1343,15 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM Size Placed accumulates for each asset, the order size placed at the exchange. A cancelation means that only the part filled can be considered placed, so we need to substract from the stage size the remainder. To achieve this with the information we currently have, we are going first - to unaccount the order size, and the account only the sizeFilled + the feesPaid. + to unaccount the order actual size, and the account only the sizeFilled + the feesPaid. */ tradingEngineStage.stageBaseAsset.sizePlaced.value = tradingEngineStage.stageBaseAsset.sizePlaced.value - - tradingEngineOrder.orderBaseAsset.size.value + tradingEngineOrder.orderBaseAsset.actualSize.value tradingEngineStage.stageQuotedAsset.sizePlaced.value = tradingEngineStage.stageQuotedAsset.sizePlaced.value - - tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineOrder.orderQuotedAsset.actualSize.value tradingEngineStage.stageBaseAsset.sizePlaced.value = tradingEngineStage.stageBaseAsset.sizePlaced.value + @@ -1186,26 +1365,26 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) } - async function applyFeePercentage(tradingEngineOrder, tradingSystemOrder, feePercentage) { + async function applyFeePercentage(feesNode, tradingEngineOrder, tradingSystemOrder, feePercentage, percentageFilled) { switch (true) { case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { /* - In this case the fees are taken from the Base Asset you receive. + In this case the fees are taken from the Base Asset you receive as a result of the trade at the exchange. */ - tradingEngineOrder.orderBaseAsset.feesPaid.value = - tradingEngineOrder.orderBaseAsset.size.value * + feesNode.value = + tradingEngineOrder.orderBaseAsset.actualSize.value * feePercentage / 100 * - tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + percentageFilled / 100 break } case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { /* - In this case the fees are taken from the Quoted Asset you receive. + In this case the fees are taken from the Quoted Asset you receive as a result of the trade at the exchange. */ tradingEngineOrder.orderQuotedAsset.feesPaid.value = - tradingEngineOrder.orderQuotedAsset.size.value * + tradingEngineOrder.orderQuotedAsset.actualSize.value * feePercentage / 100 * - tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + percentageFilled / 100 break } } diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index 7de6ec80b..0c2a2bfcc 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -143,10 +143,13 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { the Root Node specifically for this property only. */ if (recordProperty.config.nodePath !== undefined) { + if ( recordProperty.config.nodePath === "tradingEngine.exchangeOrders.marketBuyOrders.marketOrders[index].orderBaseAsset") { + let a = tradingEngine + } try { propertyRootNode = eval(recordProperty.config.nodePath) } catch (err) { - badDefinitionUnhandledException(err, 'Error Evaluation Record Property nodePath -> nodePath = ' + recordProperty.config.nodePath, product, recordProperty) + badDefinitionUnhandledException(err, 'Error Evaluating Record Property nodePath -> nodePath = ' + recordProperty.config.nodePath, product, recordProperty) } } /* diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index 64d01d985..2de568a8c 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -891,8 +891,10 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM The Stage is closed when the placedSize reaches the targetSize, and the fillSize + feesPaid reaches the placedSize. This depends on how the user defined the stage: either on Base Asset or Quoted Asset. + This can happens at any time when we update the sizeFilled and the feesPaid values when we see at the exchange that orders were filled. + Note that the comparison is made with a Rounding Factor in order to avoid rounding problems. */ const ROUNDING_ERROR_CORRECTION_FACTOR = 1.001 @@ -923,9 +925,11 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM break } } + function positionFilled() { changeStageStatus(stageName, 'Closed', 'Position Size Filled') } + function checkCloseStageEvent(tradingSystemStage) { /* Check the Close Stage Event */ tradingSystem.evalConditions(tradingSystemStage, 'Close Stage Event') From 8d284696fa089cfacf600b87e049424a088f304d Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 08:59:10 +0100 Subject: [PATCH 12/41] First Iteration of Fixes --- LowFrequencyTrading/TradingEpisode.js | 34 ++++---- LowFrequencyTrading/TradingOrders.js | 25 +++--- LowFrequencyTrading/TradingProcess.js | 2 +- LowFrequencyTrading/TradingSimulation.js | 27 +++--- LowFrequencyTrading/TradingStages.js | 102 ++++++++++++----------- Session.js | 4 +- 6 files changed, 101 insertions(+), 93 deletions(-) diff --git a/LowFrequencyTrading/TradingEpisode.js b/LowFrequencyTrading/TradingEpisode.js index de5c74f75..ff08c22a1 100644 --- a/LowFrequencyTrading/TradingEpisode.js +++ b/LowFrequencyTrading/TradingEpisode.js @@ -47,27 +47,27 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin /* This function is called each time the simulation starts. That does not mean the Episode must be opened there, since it might happen that it is looping at the end of the market - or the task / session was restarted. + or the task / session was restarted. We will only execute if the episode was never opened before. */ - if (bot.FIRST_EXECUTION === true) { - /* Starting begin and end */ - tradingEngine.current.episode.begin.value = tradingEngine.current.episode.candle.begin.value - tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.end.value + if (tradingEngine.current.episode.begin.value !== tradingEngine.current.episode.begin.config.initialValue) { return } - /* Getting the begin Balance from the session configuration */ - tradingEngine.current.episode.episodeBaseAsset.beginBalance.value = sessionParameters.sessionBaseAsset.config.initialBalance - tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value = sessionParameters.sessionQuotedAsset.config.initialBalance + /* Starting begin and end */ + tradingEngine.current.episode.begin.value = tradingEngine.current.episode.candle.begin.value + tradingEngine.current.episode.end.value = tradingEngine.current.episode.candle.end.value - /* The current balance is also the begin balance, that is how this starts. */ - tradingEngine.current.episode.episodeBaseAsset.balance.value = sessionParameters.sessionBaseAsset.config.initialBalance - tradingEngine.current.episode.episodeQuotedAsset.balance.value = sessionParameters.sessionQuotedAsset.config.initialBalance + /* Getting the begin Balance from the session configuration */ + tradingEngine.current.episode.episodeBaseAsset.beginBalance.value = sessionParameters.sessionBaseAsset.config.initialBalance + tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value = sessionParameters.sessionQuotedAsset.config.initialBalance - /* Recording the opening at the Trading Engine Data Structure */ - tradingEngine.current.episode.status.value = 'Open' - tradingEngine.current.episode.serialNumber.value = 1 - tradingEngine.current.episode.identifier.value = global.UNIQUE_ID() - tradingEngine.current.episode.beginRate.value = tradingEngine.current.episode.candle.close.value - } + /* The current balance is also the begin balance, that is how this starts. */ + tradingEngine.current.episode.episodeBaseAsset.balance.value = sessionParameters.sessionBaseAsset.config.initialBalance + tradingEngine.current.episode.episodeQuotedAsset.balance.value = sessionParameters.sessionQuotedAsset.config.initialBalance + + /* Recording the opening at the Trading Engine Data Structure */ + tradingEngine.current.episode.status.value = 'Open' + tradingEngine.current.episode.serialNumber.value = 1 + tradingEngine.current.episode.identifier.value = global.UNIQUE_ID() + tradingEngine.current.episode.beginRate.value = tradingEngine.current.episode.candle.close.value } function updateExitType(exitType) { diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 2b55c08b7..c84fb623b 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -906,14 +906,9 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* We receive the actual size from the exchange at the order.amount field. */ tradingEngineOrder.orderBaseAsset.actualSize.value = order.amount - /* - If the exchange accepted the size sent on the request to create the order, - we don't need to do anything else here. - */ - if (tradingEngineOrder.orderBaseAsset.size.value === tradingEngineOrder.orderBaseAsset.actualSize.value) { return } /* If not, we will calculate the Quoted Asset Actual Size too. Note that the amount received - is in Base Asset only. In the next formula, we are useing the rate of the order. + is in Base Asset only. In the next formula, we are unsing the rate of the order. This rate might be replaced afteerwards for an actual rate. When that happens we will need to adjust these results. */ @@ -922,6 +917,12 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingEngineOrder.orderBaseAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.actualSize.value, 10) tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + /* + If the exchange accepted the size sent on the request to create the order, + we don't need to do anything else here. + */ + if (tradingEngineOrder.orderBaseAsset.size.value === tradingEngineOrder.orderBaseAsset.actualSize.value) { return } + recalculateSizePlaced() function recalculateSizePlaced() { @@ -1197,7 +1198,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* Balance Base Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value + - tradingEngineOrder.orderBaseAsset.sizeFilled.value + tradingEngineOrder.orderBaseAsset.sizeFilled.value /* Balance Quoted Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = @@ -1223,24 +1224,24 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* Balance Base Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value + - previousBaseAssetSizeFilled + + previousBaseAssetSizeFilled + previousBaseAssetFeesPaid /* Balance Base Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value - - tradingEngineOrder.orderBaseAsset.sizeFilled.value - + tradingEngineOrder.orderBaseAsset.sizeFilled.value - tradingEngineOrder.orderBaseAsset.feesPaid.value /* Balance Quoted Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value - - previousQuotedAssetSizeFilled + previousQuotedAssetSizeFilled /* Balance Quoted Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value + - tradingEngineOrder.orderQuotedAsset.sizeFilled.value + tradingEngineOrder.orderQuotedAsset.sizeFilled.value break } } @@ -1381,7 +1382,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* In this case the fees are taken from the Quoted Asset you receive as a result of the trade at the exchange. */ - tradingEngineOrder.orderQuotedAsset.feesPaid.value = + feesNode.value = tradingEngineOrder.orderQuotedAsset.actualSize.value * feePercentage / 100 * percentageFilled / 100 diff --git a/LowFrequencyTrading/TradingProcess.js b/LowFrequencyTrading/TradingProcess.js index bddb9883a..17aca909c 100644 --- a/LowFrequencyTrading/TradingProcess.js +++ b/LowFrequencyTrading/TradingProcess.js @@ -151,7 +151,7 @@ await writeProcessFiles() bot.sessionHeartBeat(undefined, undefined, 'Sleeping') } else { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date') + bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date. No candles found at.') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index 186f2872e..87a64b504 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -53,7 +53,6 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin /* Variables needed for heartbeat functionality */ let heartBeatDate let previousHeartBeatDate - let firstLoopExecution = true /* Estimation of the Initial Candle to Process in this Run. */ @@ -73,7 +72,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin meaning that the dataSet needs to be updated with more up-to-date data. */ bot.SESSION.stop('Data is not up-to-date enough. Please start the Masters Data Mining Operation.') - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Data is not up-to-date enough. Stopping the Session now. ' ) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Data is not up-to-date enough. Stopping the Session now. ') } return } } else { @@ -107,7 +106,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin tradingEngine.current.episode.candle.index.value = i /* This is the current candle the Simulation is working at. */ - let candle = candles[tradingEngine.current.episode.candle.index.value] + let candle = candles[tradingEngine.current.episode.candle.index.value] if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Candle Begin @ ' + (new Date(candle.begin)).toLocaleString()) } if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Candle End @ ' + (new Date(candle.end)).toLocaleString()) } @@ -117,10 +116,8 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin /* We emit a heart beat so that the UI can now where we are at the overal process.*/ heartBeat() - if (firstLoopExecution === true) { - tradingEpisodeModule.openEpisode() - firstLoopExecution = false - } + /* Opening the Episode, if needed. */ + tradingEpisodeModule.openEpisode() if (checkInitialDatetime() === false) { if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Candle Before the Initia Date Time @ ' + (new Date(candle.begin)).toLocaleString()) } @@ -214,7 +211,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin closeEpisode('Final Datetime Reached') breakLoop = true bot.SESSION.stop('Final Datetime Reached') - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Final Datetime Reached. Stopping the Session now. ' ) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Final Datetime Reached. Stopping the Session now. ') } return } @@ -222,7 +219,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin closeEpisode('Min or Max Balance Reached') breakLoop = true bot.SESSION.stop('Min or Max Balance Reached') - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Min or Max Balance Reached. Stopping the Session now. ' ) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Min or Max Balance Reached. Stopping the Session now. ') } return } } @@ -287,23 +284,23 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin } /* Date only hearbeat */ - if (sessionParameters.heartbeats.config.date === true && sessionParameters.heartbeats.config.candleIndex === false) { + if (sessionParameters.heartbeats.config.date === true && sessionParameters.heartbeats.config.candleIndex === false) { hartbeatText = hartbeatText + currentDateString bot.processHeartBeat(hartbeatText, percentage) return } } - + /* When the Candle Index nees to be shown, then we can not send the hearbet only when the dates changes, we have to send it for every candle. It might also contain the date information. */ - if (sessionParameters.heartbeats.config.candleIndex === true ) { - if (sessionParameters.heartbeats.config.date === true ) { + if (sessionParameters.heartbeats.config.candleIndex === true) { + if (sessionParameters.heartbeats.config.date === true) { hartbeatText = hartbeatText + currentDateString } - hartbeatText = hartbeatText + ' Candle # ' + tradingEngine.current.episode.candle.index.value + hartbeatText = hartbeatText + ' Candle # ' + tradingEngine.current.episode.candle.index.value bot.processHeartBeat(hartbeatText, percentage) } } @@ -420,7 +417,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin The second +1 is because we need to compare the next candle (remember that the loops allways avoid the last candle of the dataset available.) */ - if (tradingEngine.current.episode.candle.index.value + 1 + 1 === candles.length ) { + if (tradingEngine.current.episode.candle.index.value + 1 + 1 === candles.length) { /* When processing daily files, we need a mechanism to turn from one day to the next one. That mechanism is the one implemented here. If we detect that the next candle is the last candle of diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index 2de568a8c..774e1c1d2 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -435,48 +435,48 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] let manageStage = strategy.manageStage let phase - + if (manageStage !== undefined) { if (manageStage.managedStopLoss !== undefined) { phase = manageStage.managedStopLoss.phases[tradingEngine.current.position.stopLoss.stopLossPhase.value - 1] } } - + if (phase !== undefined) { if (phase.formula !== undefined) { let previousValue = tradingEngine.current.position.stopLoss.value tradingPositionModule.applyStopLossFormula(tradingSystem.formulas, phase.formula.id) - + if (tradingEngine.current.position.stopLoss.value !== previousValue) { announcementsModule.makeAnnoucements(phase) } } } } - + function calculateTakeProfit() { let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] let manageStage = strategy.manageStage let phase - + if (manageStage !== undefined) { if (manageStage.managedTakeProfit !== undefined) { phase = manageStage.managedTakeProfit.phases[tradingEngine.current.position.takeProfit.takeProfitPhase.value - 1] } } - + if (phase !== undefined) { if (phase.formula !== undefined) { let previousValue = tradingEngine.current.position.takeProfit.value tradingPositionModule.applyTakeProfitFormula(tradingSystem.formulas, phase.formula.id) - + if (tradingEngine.current.position.takeProfit.value !== previousValue) { announcementsModule.makeAnnoucements(phase) } } } } - + function calculateStopLossPosition() { /* The position of the Stop Loss (above or below the price) is needed in order to know @@ -492,7 +492,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM } } } - + function calculateTakeProfitPosition() { /* The position of the Take Profit (above or below the price) is needed in order to know @@ -508,7 +508,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM } } } - + function checkStopPhasesEvents() { let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] let manageStage = strategy.manageStage @@ -516,7 +516,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM let phaseIndex let phase let stopLoss - + if (manageStage !== undefined) { if (manageStage.managedStopLoss !== undefined) { parentNode = manageStage @@ -525,10 +525,10 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM phase = stopLoss.phases[phaseIndex] } } - + if (parentNode !== undefined) { if (phase === undefined) { return } // trying to jump to a phase that does not exists. - + /* Check the next Phase Event. */ let nextPhaseEvent = phase.nextPhaseEvent if (nextPhaseEvent !== undefined) { @@ -538,9 +538,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM if (situation.conditions.length > 0) { passed = true } - + passed = tradingSystem.checkConditions(situation, passed) - + tradingSystem.values.push([situation.id, passed]) if (passed) { tradingSystem.highlights.push(situation.id) @@ -549,18 +549,18 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM tradingSystem.highlights.push(stopLoss.id) tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(manageStage.id) - + tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1) - + announcementsModule.makeAnnoucements(nextPhaseEvent) - + /* Reset this counter */ tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1 return // only one event can pass at the time } } } - + /* Check the Move to Phase Events. */ for (let n = 0; n < phase.moveToPhaseEvents.length; n++) { let moveToPhaseEvent = phase.moveToPhaseEvents[n] @@ -571,9 +571,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM if (situation.conditions.length > 0) { passed = true } - + passed = tradingSystem.checkConditions(situation, passed) - + tradingSystem.values.push([situation.id, passed]) if (passed) { tradingSystem.highlights.push(situation.id) @@ -582,7 +582,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM tradingSystem.highlights.push(stopLoss.id) tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(manageStage.id) - + let moveToPhase = moveToPhaseEvent.referenceParent if (moveToPhase !== undefined) { for (let q = 0; q < stopLoss.phases.length; q++) { @@ -594,9 +594,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM tradingSystem.errors.push([moveToPhaseEvent.id, 'This Node needs to reference a Phase.']) continue } - + announcementsModule.makeAnnoucements(moveToPhaseEvent) - + /* Reset this counter */ tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1 return // only one event can pass at the time @@ -606,7 +606,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM } } } - + function checkTakeProfitPhaseEvents() { let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] let openStage = strategy.openStage @@ -615,7 +615,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM let phaseIndex let phase let takeProfit - + if (manageStage !== undefined) { if (manageStage.managedTakeProfit !== undefined) { parentNode = manageStage @@ -624,10 +624,10 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM phase = takeProfit.phases[phaseIndex] } } - + if (parentNode !== undefined) { if (phase === undefined) { return } // trying to jump to a phase that does not exists. - + /* Check the next Phase Event. */ let nextPhaseEvent = phase.nextPhaseEvent if (nextPhaseEvent !== undefined) { @@ -637,9 +637,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM if (situation.conditions.length > 0) { passed = true } - + passed = tradingSystem.checkConditions(situation, passed) - + tradingSystem.values.push([situation.id, passed]) if (passed) { tradingSystem.highlights.push(situation.id) @@ -648,18 +648,18 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM tradingSystem.highlights.push(takeProfit.id) tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(manageStage.id) - + tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1) - + announcementsModule.makeAnnoucements(nextPhaseEvent) - + /* Reset this counter */ tradingEngine.current.episode.distanceToEvent.nextPhase.value = 1 return // only one event can pass at the time } } } - + /* Check the Move to Phase Events. */ for (let n = 0; n < phase.moveToPhaseEvents.length; n++) { let moveToPhaseEvent = phase.moveToPhaseEvents[n] @@ -670,9 +670,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM if (situation.conditions.length > 0) { passed = true } - + passed = tradingSystem.checkConditions(situation, passed) - + tradingSystem.values.push([situation.id, passed]) if (passed) { tradingSystem.highlights.push(situation.id) @@ -681,7 +681,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM tradingSystem.highlights.push(takeProfit.id) tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(manageStage.id) - + let moveToPhase = moveToPhaseEvent.referenceParent if (moveToPhase !== undefined) { for (let q = 0; q < takeProfit.phases.length; q++) { @@ -693,9 +693,9 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM tradingSystem.errors.push([moveToPhaseEvent.id, 'This Node needs to reference a Phase.']) continue } - + announcementsModule.makeAnnoucements(moveToPhaseEvent) - + /* Reset this counter */ tradingEngine.current.episode.distanceToEvent.moveToPhase.value = 1 return // only one event can pass at the time @@ -705,7 +705,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM } } } - + function checkStopLossOrTakeProfitWasHit() { { let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] @@ -713,7 +713,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM Checking what happened since the last execution. We need to know if the Stop Loss or our Take Profit were hit. */ - + /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ if ( ( @@ -726,14 +726,14 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM ) ) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Stop Loss was hit.') - + tradingPositionModule.closingPosition('Stop Loss') changeStageStatus('Close Stage', 'Opening') changeStageStatus('Manage Stage', 'Closed') announcementsModule.makeAnnoucements(strategy.closeStage) return } - + /* Take Profit condition: Here we verify if the Take Profit was hit or not. */ if ( ( @@ -746,7 +746,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM ) ) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Take Profit was hit.') - + tradingPositionModule.closingPosition('Take Profit') changeStageStatus('Close Stage', 'Opening') changeStageStatus('Manage Stage', 'Closed') @@ -894,7 +894,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM This can happens at any time when we update the sizeFilled and the feesPaid values when we see at the exchange that orders were filled. - + Note that the comparison is made with a Rounding Factor in order to avoid rounding problems. */ const ROUNDING_ERROR_CORRECTION_FACTOR = 1.001 @@ -902,7 +902,11 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM switch (tradingEngineStage.stageDefinedIn.value) { case 'Base Asset': { if ( - tradingEngineStage.stageBaseAsset.sizeFilled.value * ROUNDING_ERROR_CORRECTION_FACTOR + ( + tradingEngineStage.stageBaseAsset.sizeFilled.value + + tradingEngineStage.stageBaseAsset.feesPaid.value + ) + * ROUNDING_ERROR_CORRECTION_FACTOR >= tradingEngineStage.stageBaseAsset.targetSize.value ) { @@ -914,7 +918,11 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM } case 'Quoted Asset': { if ( - tradingEngineStage.stageQuotedAsset.sizeFilled.value * ROUNDING_ERROR_CORRECTION_FACTOR + ( + tradingEngineStage.stageQuotedAsset.sizeFilled.value + + tradingEngineStage.stageQuotedAsset.feesPaid.value + ) + * ROUNDING_ERROR_CORRECTION_FACTOR >= tradingEngineStage.stageQuotedAsset.targetSize.value ) { diff --git a/Session.js b/Session.js index 582500864..e35d08c1b 100644 --- a/Session.js +++ b/Session.js @@ -369,7 +369,9 @@ function startLiveTrading(message) { if (bot.KEY === undefined || bot.SECRET === undefined) { - if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> Key 'codeName' or 'secret' not provided. Plese check that and try again."); } + let errorMessage = "Key 'codeName' or 'secret' not provided. Plese check that and try again." + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> " + errorMessage); } + bot.sessionError(bot.SESSION, errorMessage) return false } return true From c184cbfd4f46b30e266786b337b017e3ce818fe7 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 10:08:10 +0100 Subject: [PATCH 13/41] New Modules --- LowFrequencyTrading/OrdersCalculations.js | 269 +++++++++ LowFrequencyTrading/OrdersSimulations.js | 337 +++++++++++ LowFrequencyTrading/TradingOrders.js | 692 +++------------------- 3 files changed, 693 insertions(+), 605 deletions(-) create mode 100644 LowFrequencyTrading/OrdersCalculations.js create mode 100644 LowFrequencyTrading/OrdersSimulations.js diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js new file mode 100644 index 000000000..103ea1b86 --- /dev/null +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -0,0 +1,269 @@ +exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { + /* + When we are live trading, we need to syncronize with the exchange. + */ + const MODULE_NAME = 'Orders Calculations' + + let thisObject = { + actualSizeCalculation: actualSizeCalculation, + actualRateCalculation: actualRateCalculation, + feesToBePaidCalculation: feesToBePaidCalculation, + sizeToBeFilledCalculation: sizeToBeFilledCalculation, + finapercentageFilledCalculationlize: percentageFilledCalculation, + feesPaidCalculation: feesPaidCalculation, + sizeFilledCalculation: sizeFilledCalculation, + initialize: initialize, + finalize: finalize + } + + let tradingEngine + let tradingSystem + let sessionParameters + + return thisObject + + function initialize() { + sessionParameters = bot.SESSION.parameters + tradingEngine = bot.simulationState.tradingEngine + tradingSystem = bot.simulationState.tradingSystem + } + + function finalize() { + sessionParameters = undefined + tradingEngine = undefined + tradingSystem = undefined + } + + async function actualSizeCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + When we submit the order to the exchange, we do it specifying the order size + in Base Asset. It is mandatory to be in Base Asset as per CCXT Library API. + + The exchange might take that size or might change it a little + bit. For example, if it does not accept as many decimals as we are sending. + If we identify the situation in which the size accepted is different than the + size we have accounted for, we need to make several adjustments so that the + accounting syncronizes with reality. + */ + + /* This calculation only happens once, the first time the order is checked. */ + if (tradingEngineOrder.orderBaseAsset.actualSize.value !== tradingEngineOrder.orderBaseAsset.actualSize.config.initialValue) { return } + + /* We receive the actual size from the exchange at the order.amount field. */ + tradingEngineOrder.orderBaseAsset.actualSize.value = order.amount + + /* + If not, we will calculate the Quoted Asset Actual Size too. Note that the amount received + is in Base Asset only. In the next formula, we are unsing the rate of the order. + This rate might be replaced afteerwards for an actual rate. When that happens we will + need to adjust these results. + */ + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.rate.value + + tradingEngineOrder.orderBaseAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.actualSize.value, 10) + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + + /* + If the exchange accepted the size sent on the request to create the order, + we don't need to do anything else here. + */ + if (tradingEngineOrder.orderBaseAsset.size.value === tradingEngineOrder.orderBaseAsset.actualSize.value) { return } + + recalculateSizePlaced() + + function recalculateSizePlaced() { + /* + Wee also need to unaccount the previous size placed and correctly account + for the the new actual sizes we now have. + */ + tradingEngineStage.stageBaseAsset.sizePlaced.value = + tradingEngineStage.stageBaseAsset.sizePlaced.value - + tradingEngineOrder.orderBaseAsset.size.value + + tradingEngineStage.stageBaseAsset.sizePlaced.value = + tradingEngineStage.stageBaseAsset.sizePlaced.value + + tradingEngineOrder.orderBaseAsset.actualSize.value + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value - + tradingEngineOrder.orderQuotedAsset.size.value + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value + + tradingEngineOrder.orderQuotedAsset.actualSize.value + + tradingEngineStage.stageBaseAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizePlaced.value, 10) + tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + } + } + + async function actualRateCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + Actual Rate Calculation: Let's remember that the exchange may not take the rate + we send it with the order (in Limit orders) and for any reason change it a little bit. + For example we might have sent a rate with too many decimals and the exchange truncate + them. Another thing that might happen is that we send a rate that is matching orders already + at the order book. In this case we would get an actual rate not only different than the one + we sent, but also, better since the trading engine inside the exchange would match out orders + with the first orders best by price. + + In the case of Market Orders we don't even send a rate. For all those reason + we get and store the Actual Rate at which the order was filled. + */ + if (order.average !== undefined) { + /* + We will use the average whenever is available. + */ + tradingEngineOrder.orderStatistics.actualRate.value = order.average + } else { + /* + Otherwise we use the order.price. + */ + tradingEngineOrder.orderStatistics.actualRate.value = order.price + } + + tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) + + /* + If the Actual Rate happens to be exctly the same than the order rate, then there is + nothing else to do here. Otherwise there are adjustments to be made. + */ + if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return } + + let previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value + recalculateActualSize() + recalculateSizePlaced() + + function recalculateActualSize() { + /* + Now we know the Actual Rate at which the order was filled. Since the actual rate + is not the same as the Rate we defined for the order, we need to syncronize + the Actual Order Size for Quoted Asset since it was calculated with the Order Size that we + now know it is not the one really used at the exchange. We will recalculate the + Actual Size in Quoted Asset and not in Base Asset, since the Actual Size in Base Asset + is the one we accepted by the exchange, so that is fixed, regardless of how we initially + got the Order Size in Base Asset, either from direct imput from the user or calculated + from the Order Size in Quoted Asset input it by the user. So here we go. + */ + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + } + + function recalculateSizePlaced() { + /* + Since we changed the Actual Size in Quoted Asset, we need to fix the Size Placed in Quoted Asset + that was previously calculated with the previous value of actualSize. + */ + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value - + previousQuotedAssetActualSize + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value + + tradingEngineOrder.orderQuotedAsset.actualSize.value + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + } + } + + async function feesToBePaidCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + The Fees To Be Paid are the fees that will be paid once the order is 100% Filled. + */ + switch (tradingEngineOrder.type) { + case 'Market Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + 100 + ) + break + } + case 'Limit Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + 100 + ) + break + } + } + } + + async function sizeToBeFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + The Size to Be Filled is the Actual Size minus the Fees to be Paid. + */ + tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = + tradingEngineOrder.orderBaseAsset.actualSize.value - + tradingEngineOrder.orderBaseAsset.feesToBePaid.value + + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = + tradingEngineOrder.orderQuotedAsset.actualSize.value - + tradingEngineOrder.orderQuotedAsset.feesToBePaid.value + } + + async function percentageFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + Percentage Filled Calculation: The only information we get from the exchange is the order.filled. + We know this field will go up until reaching the Size To Be Filled. Once it reaches that number + the order will be 100% filled. + */ + tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value) + tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10) + } + + async function feesPaidCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + The exchange fees are taken from the Base Asset or the Quoted Asset depending if we + are buying or selling. Within the order information received from the exchange we can not see the fees paid so we need to + calculate them ourselves. To do this it is needed to be used the Session Fees Parameters. + */ + switch (tradingEngineOrder.type) { + case 'Market Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) + break + } + case 'Limit Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) + break + } + } + } + + async function sizeFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + CCXT returns order.filled with an amount denominated in Base Asset. We will + take it from there for our Order Base Asset. The amount in order.filled does + not account for the fees, which is exactly what we need in this case. + */ + tradingEngineOrder.orderBaseAsset.sizeFilled.value = order.filled + tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) + /* + For Quoted Asset, CCXT does not return any field with the size filled, so we + need to calculate that by ourselves. Since we know the Size in Quoted Asset thet + is already adjusted with the Actual Rate and the Percentage Filled, we will use all + this in this formula to get the Size Filled in Quoted Asset. + */ + tradingEngineOrder.orderQuotedAsset.sizeFilled.value = + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + + tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) + } +} \ No newline at end of file diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js new file mode 100644 index 000000000..410f4d330 --- /dev/null +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -0,0 +1,337 @@ +exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { + /* + When we are backtesting or paper trading, we need to simulate what would have happened at the exchange. + */ + const MODULE_NAME = 'Orders Simulations' + + let thisObject = { + actualSizeSimulation: actualSizeSimulation, + actualRateSimulation: actualRateSimulation, + feesToBePaidSimulation: feesToBePaidSimulation, + sizeToBeFilledSimulation: sizeToBeFilledSimulation, + percentageFilledSimulation: percentageFilledSimulation, + feesPaidSimulation: feesPaidSimulation, + sizeFilledSimulation: sizeFilledSimulation, + initialize: initialize, + finalize: finalize + } + + let tradingEngine + let tradingSystem + let sessionParameters + + return thisObject + + function initialize() { + sessionParameters = bot.SESSION.parameters + tradingEngine = bot.simulationState.tradingEngine + tradingSystem = bot.simulationState.tradingSystem + } + + function finalize() { + sessionParameters = undefined + tradingEngine = undefined + tradingSystem = undefined + } + + async function actualSizeSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { + /* + While backtesting and paper trading, we do not really need to change + simulate an Actual Size. We will just copy to that field the + size of the order. + */ + tradingEngineOrder.orderBaseAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.size.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderQuotedAsset.size.value + + } + + function actualRateSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { + /* Actual Rate Simulation */ + let calculatedBasedOnTradingSystem = false + let previousQuotedAssetActualSize + + basedOnTradingSystem() + basedOnSessionParameters() + considerBestMatchWithOrderBook() + recalculateActualSize() + recalculateSizePlaced() + + function basedOnTradingSystem() { + /* Based on the Trading System Definition */ + if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) { + /* Calculate this only once for this order */ + if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) { + tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id) + if (tradingEngineOrder.orderStatistics.actualRate.value !== undefined) { + calculatedBasedOnTradingSystem = true + } + } + } + } + } + } + + function basedOnSessionParameters() { + /* Based on the Session Parameters Definition */ + if (calculatedBasedOnTradingSystem === false) { + switch (tradingEngineOrder.type) { + case 'Market Order': { + /* Actual Rate is simulated based on the Session Paremeters */ + let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 + switch (tradingSystemOrder.type) { + case 'Market Sell Order': { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount + break + } + case 'Market Buy Order': { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount + break + } + } + break + } + case 'Limit Order': { + /* In Limit Orders the actual rate is the rate of the order, there is no slippage */ + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + break + } + } + } + } + + function considerBestMatchWithOrderBook() { + /* + Until here we have got the rate based on the formula definition or based on session parameters slippage. + THe last check is about watching what happened in the market. Let's remember that the exchange will + fill the order with the best possible matches at it's order book. That means that if the rate + we set for the order was too low (for a sale order) or too hight (for a buy order), the actual rate + should be better than expected. + */ + switch (true) { + case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { + if (tradingEngineOrder.orderStatistics.actualRate.value > tradingEngine.current.episode.candle.max.value) { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.max.value + } + break + } + case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { + if (tradingEngineOrder.orderStatistics.actualRate.value < tradingEngine.current.episode.candle.min.value) { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.min.value + } + break + } + } + tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) + } + + function recalculateActualSize() { + /* + The Quoted Asset Actual Size in a backtest is equal to the Quoted Asset Size, which is related to + the Base Asset Size by the Order Rate. Since now we have the Actual Rate, we will need to recalculate + the Quoted Asset Actual Size using the Actual Rate. + */ + previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value + + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + } + + function recalculateSizePlaced() { + /* + For the Quoted Asset, the Size Placed was previously calculated with the previous Actual Size (or Size when these were equal). + Since we have just recaclcualted the Actual Size in Quoted Asset, we need to fix the Size Placed for the stage which was + calculated with the previous value. + */ + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value - + previousQuotedAssetActualSize + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = + tradingEngineStage.stageQuotedAsset.sizePlaced.value + + tradingEngineOrder.orderQuotedAsset.actualSize.value + + tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + } + } + + async function feesToBePaidSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { + /* + The Fees To Be Paid are the fees that will be paid once the order is 100% Filled. + */ + switch (tradingEngineOrder.type) { + case 'Market Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + 100 + ) + break + } + case 'Limit Order': { + await applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesToBePaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + 100 + ) + break + } + } + } + + async function sizeToBeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { + /* + The Size to Be Filled is the Actual Size minus the Fees to be Paid. + */ + tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = + tradingEngineOrder.orderBaseAsset.actualSize.value - + tradingEngineOrder.orderBaseAsset.feesToBePaid.value + + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = + tradingEngineOrder.orderQuotedAsset.actualSize.value - + tradingEngineOrder.orderQuotedAsset.feesToBePaid.value + } + + function percentageFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { + /* + When we are live, it is the exchange who knows which order was filled and in + which amount. Since we are backtesting here, we need to simulate what happens + at the exchange. We can not simulate that an order is filled if the candle we + can see did not touch the order. + + For buy orders we are going to check if the price was below the order actual rate, and + for sell orders we are going to check if the price was above the order actual rate. + + If we have a partial filling defined specifically for this order, + we are going to use it to estimate how much of the order we will consider filled. + */ + let orderWasHit + switch (tradingSystemOrder.type) { + case 'Limit Buy Order': { + if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.actualRate.value) { + orderWasHit = true + } + break + } + case 'Limit Sell Order': { + if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.actualRate.value) { + orderWasHit = true + } + break + } + case 'Market Buy Order': { + orderWasHit = true + break + } + case 'Market Sell Order': { + orderWasHit = true + break + } + } + if (orderWasHit === true) { + let calculatedBasedOnTradingSystem = false + + /* Based on the Trading System Definition */ + if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) { + + /* Percentage Filled */ + let percentageFilled = tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability * 100 + if (tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled > 100) { + percentageFilled = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value + } + tradingEngineOrder.orderStatistics.percentageFilled.value = tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled + tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10) + calculatedBasedOnTradingSystem = true + } + } + } + /* + If there is no definition for this at the trading system we will assume orders + are 100% filled every single time they are hit, which is the most likely thing to happen. + */ + if (calculatedBasedOnTradingSystem === false) { + /* We will assume that 100% of the order was filled */ + tradingEngineOrder.orderStatistics.percentageFilled.value = 100 + } + } + } + + function feesPaidSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { + /* + The way the Fess Paid are calculated is as follows: From the order size we apply the % of fee, + that would give us the total fees for that order. To that we apply the % filled of the orders, + that give use the final result of the fees paid for the current % filled. + + But before that, we will see if there is an specific definition on how to simulate the fees for + this order, or if there is not, we use the fees session parameters definitions. + */ + + /* Based on the Trading System Definition */ + if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) { + applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) + return + } + } + } + + /* + Another way to simulate the Fees Paid is by using the Session Paremeters configuration for Fees. + */ + switch (tradingEngineOrder.type) { + case 'Market Order': { + applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.taker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) + break + } + case 'Limit Order': { + applyFeePercentage( + tradingEngineOrder.orderBaseAsset.feesPaid, + tradingEngineOrder, + tradingSystemOrder, + sessionParameters.feeStructure.config.maker, + tradingEngineOrder.orderStatistics.percentageFilled.value + ) + break + } + } + } + + function sizeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { + /* + Size Filled is the amount of Base Asset or Quoted Asset that at the exchange matches + another counterparty order. We alredy have simulated the Percentage Filled and the + Size To Be Filled, which is the order Actual Size minus the Fees To Be Paid. + */ + + tradingEngineOrder.orderBaseAsset.sizeFilled.value = + tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value * + tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + + tradingEngineOrder.orderQuotedAsset.sizeFilled.value = + tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * + tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + + tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) + tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) + } +} \ No newline at end of file diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index c84fb623b..51969100b 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -148,7 +148,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (tradingEngine.current.episode.cycle.value === 'Second') { continue } /* Simulate Events that happens at the Exchange, if needed. */ - simulateExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) + simulateCheckExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) /* Check Events that happens at the Exchange, if needed. */ let allGood = await checkExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) @@ -395,367 +395,6 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM } } - function simulateExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) { - - /* Filter by Session Type */ - switch (bot.SESSION.type) { - case 'Backtesting Session': { - break - } - case 'Live Trading Session': { - return - } - case 'Fordward Testing Session': { - return - } - case 'Paper Trading Session': { - break - } - } - - let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value - let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value - let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value - let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value - - actualSizeSimulation() - actualRateSimulation() - feesToBePaidSimulation() - sizeToBeFilledSimulation() - percentageFilledSimulation() - feesPaidSimulation() - sizeFilledSimulation() - - doTheAccounting( - tradingEngineStage, - tradingSystemOrder, - tradingEngineOrder, - previousBaseAssetSizeFilled, - previousQuotedAssetSizeFilled, - previousBaseAssetFeesPaid, - previousQuotedAssetFeesPaid - ) - - /* If the Stage is Closing and this order is still open, we need to cancel it now */ - if (tradingEngineStage.status.value === 'Closing' && tradingEngineOrder.status.value !== 'Closed') { - simulateCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, 'Closing Stage') - } - - async function actualSizeSimulation() { - /* - While backtesting and paper trading, we do not really need to change - simulate an Actual Size. We will just copy to that field the - size of the order. - */ - tradingEngineOrder.orderBaseAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.size.value - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderQuotedAsset.size.value - - } - - function actualRateSimulation() { - /* Actual Rate Simulation */ - let calculatedBasedOnTradingSystem = false - let previousQuotedAssetActualSize - - basedOnTradingSystem() - basedOnSessionParameters() - considerBestMatchWithOrderBook() - recalculateActualSize() - recalculateSizePlaced() - - function basedOnTradingSystem() { - /* Based on the Trading System Definition */ - if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) { - /* Calculate this only once for this order */ - if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) { - tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id) - if (tradingEngineOrder.orderStatistics.actualRate.value !== undefined) { - calculatedBasedOnTradingSystem = true - } - } - } - } - } - } - - function basedOnSessionParameters() { - /* Based on the Session Parameters Definition */ - if (calculatedBasedOnTradingSystem === false) { - switch (tradingEngineOrder.type) { - case 'Market Order': { - /* Actual Rate is simulated based on the Session Paremeters */ - let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 - switch (tradingSystemOrder.type) { - case 'Market Sell Order': { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount - break - } - case 'Market Buy Order': { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount - break - } - } - break - } - case 'Limit Order': { - /* In Limit Orders the actual rate is the rate of the order, there is no slippage */ - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - break - } - } - } - } - - function considerBestMatchWithOrderBook() { - /* - Until here we have got the rate based on the formula definition or based on session parameters slippage. - THe last check is about watching what happened in the market. Let's remember that the exchange will - fill the order with the best possible matches at it's order book. That means that if the rate - we set for the order was too low (for a sale order) or too hight (for a buy order), the actual rate - should be better than expected. - */ - switch (true) { - case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { - if (tradingEngineOrder.orderStatistics.actualRate.value > tradingEngine.current.episode.candle.max.value) { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.max.value - } - break - } - case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { - if (tradingEngineOrder.orderStatistics.actualRate.value < tradingEngine.current.episode.candle.min.value) { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.min.value - } - break - } - } - tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) - } - - function recalculateActualSize() { - /* - The Quoted Asset Actual Size in a backtest is equal to the Quoted Asset Size, which is related to - the Base Asset Size by the Order Rate. Since now we have the Actual Rate, we will need to recalculate - the Quoted Asset Actual Size using the Actual Rate. - */ - previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value - tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) - } - - function recalculateSizePlaced() { - /* - For the Quoted Asset, the Size Placed was previously calculated with the previous Actual Size (or Size when these were equal). - Since we have just recaclcualted the Actual Size in Quoted Asset, we need to fix the Size Placed for the stage which was - calculated with the previous value. - */ - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value - - previousQuotedAssetActualSize - - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value + - tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) - } - } - - async function feesToBePaidSimulation() { - /* - The Fees To Be Paid are the fees that will be paid once the order is 100% Filled. - */ - switch (tradingEngineOrder.type) { - case 'Market Order': { - await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.taker, - 100 - ) - break - } - case 'Limit Order': { - await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.maker, - 100 - ) - break - } - } - } - - async function sizeToBeFilledSimulation() { - /* - The Size to Be Filled is the Actual Size minus the Fees to be Paid. - */ - tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = - tradingEngineOrder.orderBaseAsset.actualSize.value - - tradingEngineOrder.orderBaseAsset.feesToBePaid.value - - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = - tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineOrder.orderQuotedAsset.feesToBePaid.value - } - - function percentageFilledSimulation() { - /* - When we are live, it is the exchange who knows which order was filled and in - which amount. Since we are backtesting here, we need to simulate what happens - at the exchange. We can not simulate that an order is filled if the candle we - can see did not touch the order. - - For buy orders we are going to check if the price was below the order actual rate, and - for sell orders we are going to check if the price was above the order actual rate. - - If we have a partial filling defined specifically for this order, - we are going to use it to estimate how much of the order we will consider filled. - */ - let orderWasHit - switch (tradingSystemOrder.type) { - case 'Limit Buy Order': { - if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.actualRate.value) { - orderWasHit = true - } - break - } - case 'Limit Sell Order': { - if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.actualRate.value) { - orderWasHit = true - } - break - } - case 'Market Buy Order': { - orderWasHit = true - break - } - case 'Market Sell Order': { - orderWasHit = true - break - } - } - if (orderWasHit === true) { - let calculatedBasedOnTradingSystem = false - - /* Based on the Trading System Definition */ - if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) { - - /* Percentage Filled */ - let percentageFilled = tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability * 100 - if (tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled > 100) { - percentageFilled = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value - } - tradingEngineOrder.orderStatistics.percentageFilled.value = tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled - tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10) - calculatedBasedOnTradingSystem = true - } - } - } - /* - If there is no definition for this at the trading system we will assume orders - are 100% filled every single time they are hit, which is the most likely thing to happen. - */ - if (calculatedBasedOnTradingSystem === false) { - /* We will assume that 100% of the order was filled */ - tradingEngineOrder.orderStatistics.percentageFilled.value = 100 - } - /* Check if we need to close the order */ - if (tradingEngineOrder.orderStatistics.percentageFilled.value === 100) { - - /* Close this Order */ - tradingEngineOrder.status.value = 'Closed' - tradingEngineOrder.exitType.value = 'Filled' - - /* Initialize this */ - tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1 - - updateEndsWithCycle(tradingEngineOrder) - tradingSystem.infos.push([tradingSystemOrder.id, 'percentageFilledSimulation -> Closing Order with Exit Type ' + tradingEngineOrder.exitType.value]) - } - } - } - - function feesPaidSimulation() { - /* - The way the Fess Paid are calculated is as follows: From the order size we apply the % of fee, - that would give us the total fees for that order. To that we apply the % filled of the orders, - that give use the final result of the fees paid for the current % filled. - - But before that, we will see if there is an specific definition on how to simulate the fees for - this order, or if there is not, we use the fees session parameters definitions. - */ - - /* Based on the Trading System Definition */ - if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) { - applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, - tradingEngineOrder, - tradingSystemOrder, - tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage, - tradingEngineOrder.orderStatistics.percentageFilled.value - ) - return - } - } - } - - /* - Another way to simulate the Fees Paid is by using the Session Paremeters configuration for Fees. - */ - switch (tradingEngineOrder.type) { - case 'Market Order': { - applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.taker, - tradingEngineOrder.orderStatistics.percentageFilled.value - ) - break - } - case 'Limit Order': { - applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.maker, - tradingEngineOrder.orderStatistics.percentageFilled.value - ) - break - } - } - } - - function sizeFilledSimulation() { - /* - Size Filled is the amount of Base Asset or Quoted Asset that at the exchange matches - another counterparty order. We alredy have simulated the Percentage Filled and the - Size To Be Filled, which is the order Actual Size minus the Fees To Be Paid. - */ - - tradingEngineOrder.orderBaseAsset.sizeFilled.value = - tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value * - tradingEngineOrder.orderStatistics.percentageFilled.value / 100 - - tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * - tradingEngineOrder.orderStatistics.percentageFilled.value / 100 - - tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) - tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) - } - } - async function checkExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) { /* Filter by Session Type */ @@ -861,6 +500,78 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM return true } + function simulateCheckExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) { + + /* Filter by Session Type */ + switch (bot.SESSION.type) { + case 'Backtesting Session': { + break + } + case 'Live Trading Session': { + return + } + case 'Fordward Testing Session': { + return + } + case 'Paper Trading Session': { + break + } + } + + simulateSyncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) + + /* Check if we need to close the order */ + if (tradingEngineOrder.orderStatistics.percentageFilled.value === 100) { + + /* Close this Order */ + tradingEngineOrder.status.value = 'Closed' + tradingEngineOrder.exitType.value = 'Filled' + + /* Initialize this */ + tradingEngine.current.episode.distanceToEvent.closeOrder.value = 1 + + updateEndsWithCycle(tradingEngineOrder) + tradingSystem.infos.push([tradingSystemOrder.id, 'percentageFilledSimulation -> Closing Order with Exit Type ' + tradingEngineOrder.exitType.value]) + } + + /* If the Stage is Closing and this order is still open, we need to cancel it now */ + if (tradingEngineStage.status.value === 'Closing' && tradingEngineOrder.status.value !== 'Closed') { + simulateCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, 'Closing Stage') + } + } + + function simulateSyncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) { + + let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value + let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value + let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value + let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value + + const ORDERS_SIMULATIONS_MODULE = require('./OrdersSimulations.js') + let ordersSimulationsModule = ORDERS_SIMULATIONS_MODULE.newOrdersSimulations(bot, logger) + ordersSimulationsModule.initialize() + + ordersSimulationsModule.actualSizeSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + ordersSimulationsModule.actualRateSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + ordersSimulationsModule.feesToBePaidSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + ordersSimulationsModule.sizeToBeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + ordersSimulationsModule.percentageFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + ordersSimulationsModule.feesPaidSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + ordersSimulationsModule.sizeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + + ordersSimulationsModule.finalize() + + doTheAccounting( + tradingEngineStage, + tradingSystemOrder, + tradingEngineOrder, + previousBaseAssetSizeFilled, + previousQuotedAssetSizeFilled, + previousBaseAssetFeesPaid, + previousQuotedAssetFeesPaid + ) + } + async function syncWithExchange(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order) { let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value @@ -868,13 +579,19 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value - await actualSizeCalculation() - await actualRateCalculation() - await feesToBePaidCalculation() - await sizeToBeFilledCalculation() - await percentageFilledCalculation() - await feesPaidCalculation() - await sizeFilledCalculation() + const ORDERS_CALCULATIONS_MODULE = require('./OrdersCalculations.js') + let ordersCalculationsModule = ORDERS_CALCULATIONS_MODULE.newOrdersCalculations(bot, logger) + ordersCalculationsModule.initialize() + + await ordersCalculationsModule.actualSizeCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + await ordersCalculationsModule.actualRateCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + await ordersCalculationsModule.feesToBePaidCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + await ordersCalculationsModule.sizeToBeFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + await ordersCalculationsModule.percentageFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + await ordersCalculationsModule.feesPaidCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + await ordersCalculationsModule.sizeFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + + ordersCalculationsModule.finalize() await doTheAccounting( tradingEngineStage, @@ -885,241 +602,6 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM previousBaseAssetFeesPaid, previousQuotedAssetFeesPaid ) - - return - - async function actualSizeCalculation() { - /* - When we submit the order to the exchange, we do it specifying the order size - in Base Asset. It is mandatory to be in Base Asset as per CCXT Library API. - - The exchange might take that size or might change it a little - bit. For example, if it does not accept as many decimals as we are sending. - If we identify the situation in which the size accepted is different than the - size we have accounted for, we need to make several adjustments so that the - accounting syncronizes with reality. - */ - - /* This calculation only happens once, the first time the order is checked. */ - if (tradingEngineOrder.orderBaseAsset.actualSize.value !== tradingEngineOrder.orderBaseAsset.actualSize.config.initialValue) { return } - - /* We receive the actual size from the exchange at the order.amount field. */ - tradingEngineOrder.orderBaseAsset.actualSize.value = order.amount - - /* - If not, we will calculate the Quoted Asset Actual Size too. Note that the amount received - is in Base Asset only. In the next formula, we are unsing the rate of the order. - This rate might be replaced afteerwards for an actual rate. When that happens we will - need to adjust these results. - */ - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.rate.value - - tradingEngineOrder.orderBaseAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.actualSize.value, 10) - tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) - - /* - If the exchange accepted the size sent on the request to create the order, - we don't need to do anything else here. - */ - if (tradingEngineOrder.orderBaseAsset.size.value === tradingEngineOrder.orderBaseAsset.actualSize.value) { return } - - recalculateSizePlaced() - - function recalculateSizePlaced() { - /* - Wee also need to unaccount the previous size placed and correctly account - for the the new actual sizes we now have. - */ - tradingEngineStage.stageBaseAsset.sizePlaced.value = - tradingEngineStage.stageBaseAsset.sizePlaced.value - - tradingEngineOrder.orderBaseAsset.size.value - - tradingEngineStage.stageBaseAsset.sizePlaced.value = - tradingEngineStage.stageBaseAsset.sizePlaced.value + - tradingEngineOrder.orderBaseAsset.actualSize.value - - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value - - tradingEngineOrder.orderQuotedAsset.size.value - - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value + - tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineStage.stageBaseAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizePlaced.value, 10) - tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) - } - } - - async function actualRateCalculation() { - /* - Actual Rate Calculation: Let's remember that the exchange may not take the rate - we send it with the order (in Limit orders) and for any reason change it a little bit. - For example we might have sent a rate with too many decimals and the exchange truncate - them. Another thing that might happen is that we send a rate that is matching orders already - at the order book. In this case we would get an actual rate not only different than the one - we sent, but also, better since the trading engine inside the exchange would match out orders - with the first orders best by price. - - In the case of Market Orders we don't even send a rate. For all those reason - we get and store the Actual Rate at which the order was filled. - */ - if (order.average !== undefined) { - /* - We will use the average whenever is available. - */ - tradingEngineOrder.orderStatistics.actualRate.value = order.average - } else { - /* - Otherwise we use the order.price. - */ - tradingEngineOrder.orderStatistics.actualRate.value = order.price - } - - tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) - - /* - If the Actual Rate happens to be exctly the same than the order rate, then there is - nothing else to do here. Otherwise there are adjustments to be made. - */ - if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return } - - let previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value - recalculateActualSize() - recalculateSizePlaced() - - function recalculateActualSize() { - /* - Now we know the Actual Rate at which the order was filled. Since the actual rate - is not the same as the Rate we defined for the order, we need to syncronize - the Actual Order Size for Quoted Asset since it was calculated with the Order Size that we - now know it is not the one really used at the exchange. We will recalculate the - Actual Size in Quoted Asset and not in Base Asset, since the Actual Size in Base Asset - is the one we accepted by the exchange, so that is fixed, regardless of how we initially - got the Order Size in Base Asset, either from direct imput from the user or calculated - from the Order Size in Quoted Asset input it by the user. So here we go. - */ - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value - tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) - } - - function recalculateSizePlaced() { - /* - Since we changed the Actual Size in Quoted Asset, we need to fix the Size Placed in Quoted Asset - that was previously calculated with the previous value of actualSize. - */ - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value - - previousQuotedAssetActualSize - - tradingEngineStage.stageQuotedAsset.sizePlaced.value = - tradingEngineStage.stageQuotedAsset.sizePlaced.value + - tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) - } - } - - async function feesToBePaidCalculation() { - /* - The Fees To Be Paid are the fees that will be paid once the order is 100% Filled. - */ - switch (tradingEngineOrder.type) { - case 'Market Order': { - await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.taker, - 100 - ) - break - } - case 'Limit Order': { - await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.maker, - 100 - ) - break - } - } - } - - async function sizeToBeFilledCalculation() { - /* - The Size to Be Filled is the Actual Size minus the Fees to be Paid. - */ - tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = - tradingEngineOrder.orderBaseAsset.actualSize.value - - tradingEngineOrder.orderBaseAsset.feesToBePaid.value - - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = - tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineOrder.orderQuotedAsset.feesToBePaid.value - } - - async function percentageFilledCalculation() { - /* - Percentage Filled Calculation: The only information we get from the exchange is the order.filled. - We know this field will go up until reaching the Size To Be Filled. Once it reaches that number - the order will be 100% filled. - */ - tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value) - tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10) - } - - async function feesPaidCalculation() { - /* - The exchange fees are taken from the Base Asset or the Quoted Asset depending if we - are buying or selling. Within the order information received from the exchange we can not see the fees paid so we need to - calculate them ourselves. To do this it is needed to be used the Session Fees Parameters. - */ - switch (tradingEngineOrder.type) { - case 'Market Order': { - await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.taker, - tradingEngineOrder.orderStatistics.percentageFilled.value - ) - break - } - case 'Limit Order': { - await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, - tradingEngineOrder, - tradingSystemOrder, - sessionParameters.feeStructure.config.maker, - tradingEngineOrder.orderStatistics.percentageFilled.value - ) - break - } - } - } - - async function sizeFilledCalculation() { - /* - CCXT returns order.filled with an amount denominated in Base Asset. We will - take it from there for our Order Base Asset. The amount in order.filled does - not account for the fees, which is exactly what we need in this case. - */ - tradingEngineOrder.orderBaseAsset.sizeFilled.value = order.filled - tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) - /* - For Quoted Asset, CCXT does not return any field with the size filled, so we - need to calculate that by ourselves. Since we know the Size in Quoted Asset thet - is already adjusted with the Actual Rate and the Percentage Filled, we will use all - this in this formula to get the Size Filled in Quoted Asset. - */ - tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 - - tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) - } } async function doTheAccounting( @@ -1382,7 +864,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* In this case the fees are taken from the Quoted Asset you receive as a result of the trade at the exchange. */ - feesNode.value = + feesNode.value = tradingEngineOrder.orderQuotedAsset.actualSize.value * feePercentage / 100 * percentageFilled / 100 From d9599a9716a6f3df95311dd3e077d75542f39b66 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 11:37:46 +0100 Subject: [PATCH 14/41] Several Fixes --- LowFrequencyTrading/OrdersCalculations.js | 100 ++++++++++++---------- LowFrequencyTrading/TradingOrders.js | 84 +++++++++--------- LowFrequencyTrading/TradingStages.js | 26 +++--- 3 files changed, 113 insertions(+), 97 deletions(-) diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js index 103ea1b86..4c985113c 100644 --- a/LowFrequencyTrading/OrdersCalculations.js +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -33,48 +33,54 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { tradingEngine = undefined tradingSystem = undefined } - + async function actualSizeCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { /* When we submit the order to the exchange, we do it specifying the order size - in Base Asset. It is mandatory to be in Base Asset as per CCXT Library API. + in Base Asset. It is mandatory to be in Base Asset as per CCXT Library API and + probably as per the Exchange API too. The exchange might take that size or might change it a little bit. For example, if it does not accept as many decimals as we are sending. - If we identify the situation in which the size accepted is different than the - size we have accounted for, we need to make several adjustments so that the + + We have also seen that for Market Orders, the exchange usually does not take + the size requested exactly. Sometimes is a little bit less or even a little bit + more. For Limit orders it is usually the same. + + If it happens that the size accepted (Actual Size) is different than the + size requested (Size), we need to make several adjustments so that the accounting syncronizes with reality. */ - /* This calculation only happens once, the first time the order is checked. */ + /* This calculation needs to happen only once, the first time the order is checked. */ if (tradingEngineOrder.orderBaseAsset.actualSize.value !== tradingEngineOrder.orderBaseAsset.actualSize.config.initialValue) { return } /* We receive the actual size from the exchange at the order.amount field. */ tradingEngineOrder.orderBaseAsset.actualSize.value = order.amount - /* - If not, we will calculate the Quoted Asset Actual Size too. Note that the amount received - is in Base Asset only. In the next formula, we are unsing the rate of the order. - This rate might be replaced afteerwards for an actual rate. When that happens we will - need to adjust these results. - */ - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.rate.value - - tradingEngineOrder.orderBaseAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.actualSize.value, 10) - tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) - - /* - If the exchange accepted the size sent on the request to create the order, - we don't need to do anything else here. - */ - if (tradingEngineOrder.orderBaseAsset.size.value === tradingEngineOrder.orderBaseAsset.actualSize.value) { return } - + recalculateActualSize() recalculateSizePlaced() - function recalculateSizePlaced() { + function recalculateActualSize() { /* - Wee also need to unaccount the previous size placed and correctly account - for the the new actual sizes we now have. + We will recalculate the Quoted Asset Actual Size. This will also give to it an initial value the first time we do this. + In the next formula, we are unsing the rate of the order because we dont know yet the Actual Rate. + This rate might be replaced afteerwards for the Actual Rate when this is calculated again once + the Actual Rate is known. + */ + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.rate.value + + tradingEngineOrder.orderBaseAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.actualSize.value, 10) + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + } + + function recalculateSizePlaced() { + /* + We will also recalculate the Size Placed that was previously calculated with the order Size, + and now we will do it with the order Actual Size. + + We need to unaccount the previous size placed and correctly account + for the the new actual sizes we now know. */ tradingEngineStage.stageBaseAsset.sizePlaced.value = tradingEngineStage.stageBaseAsset.sizePlaced.value - @@ -104,7 +110,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { For example we might have sent a rate with too many decimals and the exchange truncate them. Another thing that might happen is that we send a rate that is matching orders already at the order book. In this case we would get an actual rate not only different than the one - we sent, but also, better since the trading engine inside the exchange would match out orders + we sent, but also, better since the trading engine inside the exchange would match orders with the first orders best by price. In the case of Market Orders we don't even send a rate. For all those reason @@ -112,12 +118,13 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { */ if (order.average !== undefined) { /* - We will use the average whenever is available. + We will use the average whenever is available. As of today it is not 100% clear when this is + available, but it seems sometimes it is not. In those cases we use the price. */ tradingEngineOrder.orderStatistics.actualRate.value = order.average } else { /* - Otherwise we use the order.price. + We use the order.price when the average is not available. */ tradingEngineOrder.orderStatistics.actualRate.value = order.price } @@ -131,6 +138,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return } let previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value + recalculateActualSize() recalculateSizePlaced() @@ -145,7 +153,10 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { got the Order Size in Base Asset, either from direct imput from the user or calculated from the Order Size in Quoted Asset input it by the user. So here we go. */ - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = + tradingEngineOrder.orderBaseAsset.actualSize.value * + tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) } @@ -173,7 +184,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { switch (tradingEngineOrder.type) { case 'Market Order': { await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, + 'feesToBePaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker, @@ -183,7 +194,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { } case 'Limit Order': { await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, + 'feesToBePaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker, @@ -209,24 +220,27 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { async function percentageFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { /* - Percentage Filled Calculation: The only information we get from the exchange is the order.filled. - We know this field will go up until reaching the Size To Be Filled. Once it reaches that number + Percentage Filled Calculation: The only relevant information we get from the exchange is the order.filled. + We know this field will go up until reaching the Actual Size. Once it reaches that number the order will be 100% filled. */ - tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value) + tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / tradingEngineOrder.orderBaseAsset.actualSize.value tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10) } async function feesPaidCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { /* - The exchange fees are taken from the Base Asset or the Quoted Asset depending if we - are buying or selling. Within the order information received from the exchange we can not see the fees paid so we need to - calculate them ourselves. To do this it is needed to be used the Session Fees Parameters. + Within the order information received from the exchange we can not see the fees paid so we need to + calculate them ourselves. Knwowing the Fees Paid is critical to later update the balances correctly, + since at every trade we will receive less than just the Actual Size. In fact we will receive the Actual + Size minus the Fees Paid. + + The percentage at which the exchange will charge fees needs to be configured at the the Session Fees Parameters. */ switch (tradingEngineOrder.type) { case 'Market Order': { await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, + 'feesPaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker, @@ -236,7 +250,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { } case 'Limit Order': { await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, + 'feesPaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker, @@ -251,18 +265,16 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { /* CCXT returns order.filled with an amount denominated in Base Asset. We will take it from there for our Order Base Asset. The amount in order.filled does - not account for the fees, which is exactly what we need in this case. + not reflect the Fees Paid, even if we are paying the fees in Base Asset. */ tradingEngineOrder.orderBaseAsset.sizeFilled.value = order.filled tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) /* For Quoted Asset, CCXT does not return any field with the size filled, so we - need to calculate that by ourselves. Since we know the Size in Quoted Asset thet - is already adjusted with the Actual Rate and the Percentage Filled, we will use all - this in this formula to get the Size Filled in Quoted Asset. + need to calculate that by ourselves. */ tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + tradingEngineOrder.orderQuotedAsset.sizeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) } diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 51969100b..ec3eb78a2 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -665,64 +665,58 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* Balances Update */ switch (true) { /* - Let's remember that for Buy orders, the fees are paid in Base Asset.So we - need to substract from the balance of Quoted Asset both the size filled and - the fees, and we will receive at the balance of Base Asset just the size filled, - bacuase the exchange gets the fees in Base Asset for them. - */ - case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { - - /* Balance Base Asset: Undo the previous accounting */ - tradingEngine.current.episode.episodeBaseAsset.balance.value = - tradingEngine.current.episode.episodeBaseAsset.balance.value - - previousBaseAssetSizeFilled - - /* Balance Base Asset: Account the current filling and fees */ - tradingEngine.current.episode.episodeBaseAsset.balance.value = - tradingEngine.current.episode.episodeBaseAsset.balance.value + - tradingEngineOrder.orderBaseAsset.sizeFilled.value - - /* Balance Quoted Asset: Undo the previous accounting */ - tradingEngine.current.episode.episodeQuotedAsset.balance.value = - tradingEngine.current.episode.episodeQuotedAsset.balance.value + - previousQuotedAssetSizeFilled + - previousQuotedAssetFeesPaid - - /* Balance Quoted Asset: Account the current filling and fees */ - tradingEngine.current.episode.episodeQuotedAsset.balance.value = - tradingEngine.current.episode.episodeQuotedAsset.balance.value - - tradingEngineOrder.orderQuotedAsset.sizeFilled.value - - tradingEngineOrder.orderQuotedAsset.feesPaid.value - break - } - /* - For Sell orders, the fees are being paid in Quote Asset, so we will substract - from the Base Asset balance the size filled and the fees, and we will add - to the Quoted Asset balance only the size filled, because the exchange - get those fees. + For Sell orders, the fees are being paid in Quote Asset. */ case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { /* Balance Base Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeBaseAsset.balance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value + + previousBaseAssetSizeFilled + + /* Balance Base Asset: Account the current filling and fees */ + tradingEngine.current.episode.episodeBaseAsset.balance.value = + tradingEngine.current.episode.episodeBaseAsset.balance.value - + tradingEngineOrder.orderBaseAsset.sizeFilled.value + + /* Balance Quoted Asset: Undo the previous accounting */ + tradingEngine.current.episode.episodeQuotedAsset.balance.value = + tradingEngine.current.episode.episodeQuotedAsset.balance.value - + previousQuotedAssetSizeFilled + + previousQuotedAssetFeesPaid + + /* Balance Quoted Asset: Account the current filling and fees */ + tradingEngine.current.episode.episodeQuotedAsset.balance.value = + tradingEngine.current.episode.episodeQuotedAsset.balance.value + + tradingEngineOrder.orderQuotedAsset.sizeFilled.value - + tradingEngineOrder.orderQuotedAsset.feesPaid.value + break + } + /* + Let's remember that for Buy orders, the fees are paid in Base Asset. + */ + case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { + + /* Balance Base Asset: Undo the previous accounting */ + tradingEngine.current.episode.episodeBaseAsset.balance.value = + tradingEngine.current.episode.episodeBaseAsset.balance.value - previousBaseAssetSizeFilled + previousBaseAssetFeesPaid /* Balance Base Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeBaseAsset.balance.value = - tradingEngine.current.episode.episodeBaseAsset.balance.value - + tradingEngine.current.episode.episodeBaseAsset.balance.value + tradingEngineOrder.orderBaseAsset.sizeFilled.value - tradingEngineOrder.orderBaseAsset.feesPaid.value /* Balance Quoted Asset: Undo the previous accounting */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = - tradingEngine.current.episode.episodeQuotedAsset.balance.value - + tradingEngine.current.episode.episodeQuotedAsset.balance.value + previousQuotedAssetSizeFilled /* Balance Quoted Asset: Account the current filling and fees */ tradingEngine.current.episode.episodeQuotedAsset.balance.value = - tradingEngine.current.episode.episodeQuotedAsset.balance.value + + tradingEngine.current.episode.episodeQuotedAsset.balance.value - tradingEngineOrder.orderQuotedAsset.sizeFilled.value break } @@ -848,12 +842,19 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) } - async function applyFeePercentage(feesNode, tradingEngineOrder, tradingSystemOrder, feePercentage, percentageFilled) { + async function applyFeePercentage(feesNodePropertyName, tradingEngineOrder, tradingSystemOrder, feePercentage, percentageFilled) { + /* + The exchange fees are taken from the Base Asset or the Quoted Asset depending if we + are buying or selling. + */ + let feesNode + switch (true) { case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { /* In this case the fees are taken from the Base Asset you receive as a result of the trade at the exchange. */ + feesNode = tradingEngineOrder.orderBaseAsset[feesNodePropertyName] feesNode.value = tradingEngineOrder.orderBaseAsset.actualSize.value * feePercentage / 100 * @@ -864,6 +865,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* In this case the fees are taken from the Quoted Asset you receive as a result of the trade at the exchange. */ + feesNode = tradingEngineOrder.orderQuotedAsset[feesNodePropertyName] feesNode.value = tradingEngineOrder.orderQuotedAsset.actualSize.value * feePercentage / 100 * @@ -872,8 +874,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM } } - tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10) - tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10) + feesNode.value = global.PRECISE(feesNode.value, 10) + feesNode.value = global.PRECISE(feesNode.value, 10) } async function checkOrderEvent(event, order, executionAlgorithm, executionNode) { diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index 774e1c1d2..7c95d8ac7 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -888,14 +888,18 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM function checkIfStageNeedsToBeClosed(tradingEngineStage, tradingSystemStage, stageName) { /* - The Stage is closed when the placedSize reaches the targetSize, and the fillSize - + feesPaid reaches the placedSize. This depends on how the user defined the stage: - either on Base Asset or Quoted Asset. - - This can happens at any time when we update the sizeFilled and the feesPaid values - when we see at the exchange that orders were filled. - - Note that the comparison is made with a Rounding Factor in order to avoid rounding problems. + The Stage is closed when the fillSize + feesPaid reaches the targetSize. + + The Target Size is defined by the end user either in Base Asset or Quoted Asset. + Whatever the user chooses defines also the way we check if the Target was met. + Why? Because the counterparty target is estimated with the Target Rate and it + is not syncronized with the reality we later learn when the exchange tell us the + actual rate each of the orders where executed at. + + For the same reason that the user does not know exactly which is the rate the orders + will actually be filled, then we can not expect that the total filled will really + reach the target size, so we introduce a rounding factor so that when it is close + enough we will consider the target to have been reached. */ const ROUNDING_ERROR_CORRECTION_FACTOR = 1.001 @@ -903,8 +907,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM case 'Base Asset': { if ( ( - tradingEngineStage.stageBaseAsset.sizeFilled.value + - tradingEngineStage.stageBaseAsset.feesPaid.value + tradingEngineStage.stageBaseAsset.sizeFilled.value ) * ROUNDING_ERROR_CORRECTION_FACTOR >= @@ -919,8 +922,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM case 'Quoted Asset': { if ( ( - tradingEngineStage.stageQuotedAsset.sizeFilled.value + - tradingEngineStage.stageQuotedAsset.feesPaid.value + tradingEngineStage.stageQuotedAsset.sizeFilled.value ) * ROUNDING_ERROR_CORRECTION_FACTOR >= From 61ceb0feed2f8be3185dfceb7736b2100f06592c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 12:18:31 +0100 Subject: [PATCH 15/41] Calculations Ready --- LowFrequencyTrading/OrdersCalculations.js | 45 ++++++++++++++------- LowFrequencyTrading/OrdersSimulations.js | 49 +++++++++++++++-------- LowFrequencyTrading/TradingOrders.js | 8 ++-- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js index 4c985113c..d4f8e13d2 100644 --- a/LowFrequencyTrading/OrdersCalculations.js +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -8,7 +8,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { actualSizeCalculation: actualSizeCalculation, actualRateCalculation: actualRateCalculation, feesToBePaidCalculation: feesToBePaidCalculation, - sizeToBeFilledCalculation: sizeToBeFilledCalculation, + amountReceivedCalculation: amountReceivedCalculation, finapercentageFilledCalculationlize: percentageFilledCalculation, feesPaidCalculation: feesPaidCalculation, sizeFilledCalculation: sizeFilledCalculation, @@ -205,19 +205,6 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { } } - async function sizeToBeFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { - /* - The Size to Be Filled is the Actual Size minus the Fees to be Paid. - */ - tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = - tradingEngineOrder.orderBaseAsset.actualSize.value - - tradingEngineOrder.orderBaseAsset.feesToBePaid.value - - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = - tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineOrder.orderQuotedAsset.feesToBePaid.value - } - async function percentageFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { /* Percentage Filled Calculation: The only relevant information we get from the exchange is the order.filled. @@ -278,4 +265,34 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) } + + async function amountReceivedCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + This calculation is for informational purposes, so that users do not have to calculate + it by themselves. The Amount Received out of the trade depends if we are Buying or Selling + the Base Asset. If we are Buying, then the Amount Receiced will be in Base Asset. If we + are selling the it will be in Quoted Asset. + */ + + switch (true) { + case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { + /* + In this case the Amount Received is in Base Asset. + */ + tradingEngineOrder.orderBaseAsset.amountReceived.value.value = + tradingEngineOrder.orderBaseAsset.sizeFilled.value - + tradingEngineOrder.orderBaseAsset.feesPaid.value + break + } + case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { + /* + In this case the Amount Received is in Quoted Asset. + */ + tradingEngineOrder.orderQuotedAsset.amountReceived.value.value = + tradingEngineOrder.orderQuotedAsset.sizeFilled.value - + tradingEngineOrder.orderQuotedAsset.feesPaid.value + break + } + } + } } \ No newline at end of file diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 410f4d330..5e931f5f3 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -8,7 +8,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { actualSizeSimulation: actualSizeSimulation, actualRateSimulation: actualRateSimulation, feesToBePaidSimulation: feesToBePaidSimulation, - sizeToBeFilledSimulation: sizeToBeFilledSimulation, + amountReceivedSimulation: amountReceivedSimulation, percentageFilledSimulation: percentageFilledSimulation, feesPaidSimulation: feesPaidSimulation, sizeFilledSimulation: sizeFilledSimulation, @@ -184,19 +184,6 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { } } - async function sizeToBeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { - /* - The Size to Be Filled is the Actual Size minus the Fees to be Paid. - */ - tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value = - tradingEngineOrder.orderBaseAsset.actualSize.value - - tradingEngineOrder.orderBaseAsset.feesToBePaid.value - - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value = - tradingEngineOrder.orderQuotedAsset.actualSize.value - - tradingEngineOrder.orderQuotedAsset.feesToBePaid.value - } - function percentageFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { /* When we are live, it is the exchange who knows which order was filled and in @@ -324,14 +311,44 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { */ tradingEngineOrder.orderBaseAsset.sizeFilled.value = - tradingEngineOrder.orderBaseAsset.sizeToBeFilled.value * + tradingEngineOrder.orderBaseAsset.amountReceived.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.sizeToBeFilled.value * + tradingEngineOrder.orderQuotedAsset.amountReceived.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) } + + async function amountReceivedSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) { + /* + This simulation is for informational purposes, so that users do not have to calculate + it by themselves. The Amount Received out of the trade depends if we are Buying or Selling + the Base Asset. If we are Buying, then the Amount Receiced will be in Base Asset. If we + are selling the it will be in Quoted Asset. + */ + + switch (true) { + case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { + /* + In this case the Amount Received is in Base Asset. + */ + tradingEngineOrder.orderBaseAsset.amountReceived.value.value = + tradingEngineOrder.orderBaseAsset.sizeFilled.value - + tradingEngineOrder.orderBaseAsset.feesPaid.value + break + } + case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { + /* + In this case the Amount Received is in Quoted Asset. + */ + tradingEngineOrder.orderQuotedAsset.amountReceived.value.value = + tradingEngineOrder.orderQuotedAsset.sizeFilled.value - + tradingEngineOrder.orderQuotedAsset.feesPaid.value + break + } + } + } } \ No newline at end of file diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index ec3eb78a2..47c2044c8 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -237,14 +237,14 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (tradingEngineOrder.orderBaseAsset.sizeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.sizeFilled === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderBaseAsset.feesPaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.feesPaid === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderBaseAsset.actualSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.actualSize === undefined', tradingEngineOrder) } - if (tradingEngineOrder.orderBaseAsset.sizeToBeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.sizeToBeFilled === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderBaseAsset.amountReceived === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.amountReceived === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderBaseAsset.feesToBePaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderBaseAsset.feesToBePaid === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.size === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.size === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.sizeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.sizeFilled === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.feesPaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.feesPaid === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.actualSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.actualSize === undefined', tradingEngineOrder) } - if (tradingEngineOrder.orderQuotedAsset.sizeToBeFilled === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.sizeToBeFilled === undefined', tradingEngineOrder) } + if (tradingEngineOrder.orderQuotedAsset.amountReceived === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.amountReceived === undefined', tradingEngineOrder) } if (tradingEngineOrder.orderQuotedAsset.feesToBePaid === undefined) { badDefinitionUnhandledException(undefined, 'tradingEngineOrder.orderQuotedAsset.feesToBePaid === undefined', tradingEngineOrder) } } @@ -554,10 +554,10 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM ordersSimulationsModule.actualSizeSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) ordersSimulationsModule.actualRateSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) ordersSimulationsModule.feesToBePaidSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) - ordersSimulationsModule.sizeToBeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) ordersSimulationsModule.percentageFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) ordersSimulationsModule.feesPaidSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) ordersSimulationsModule.sizeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) + ordersSimulationsModule.amountReceivedSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) ordersSimulationsModule.finalize() @@ -586,10 +586,10 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM await ordersCalculationsModule.actualSizeCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) await ordersCalculationsModule.actualRateCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) await ordersCalculationsModule.feesToBePaidCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) - await ordersCalculationsModule.sizeToBeFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) await ordersCalculationsModule.percentageFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) await ordersCalculationsModule.feesPaidCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) await ordersCalculationsModule.sizeFilledCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) + await ordersCalculationsModule.amountReceivedCalculation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, order, applyFeePercentage) ordersCalculationsModule.finalize() From 1f81975c95af6ff29796498e11fe3cc4df3534f8 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 13:05:05 +0100 Subject: [PATCH 16/41] Review Complete --- LowFrequencyTrading/OrdersCalculations.js | 2 +- LowFrequencyTrading/OrdersSimulations.js | 128 ++++++++++++---------- 2 files changed, 72 insertions(+), 58 deletions(-) diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js index d4f8e13d2..bf334a6cd 100644 --- a/LowFrequencyTrading/OrdersCalculations.js +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -271,7 +271,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { This calculation is for informational purposes, so that users do not have to calculate it by themselves. The Amount Received out of the trade depends if we are Buying or Selling the Base Asset. If we are Buying, then the Amount Receiced will be in Base Asset. If we - are selling the it will be in Quoted Asset. + are selling then it will be in Quoted Asset. */ switch (true) { diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 5e931f5f3..948b2bab1 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -36,45 +36,36 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { async function actualSizeSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { /* - While backtesting and paper trading, we do not really need to change + The Actual Size is the size the exchange accepts for the order, + regarless of the size sent in the request to the exchange. + + While backtesting and paper trading, we do not really need to simulate an Actual Size. We will just copy to that field the size of the order. + + This calculation needs to happen only once, so as to set these values. */ + if (tradingEngineOrder.orderBaseAsset.actualSize.value !== tradingEngineOrder.orderBaseAsset.actualSize.config.initialValue) { return } + tradingEngineOrder.orderBaseAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.size.value tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderQuotedAsset.size.value - } function actualRateSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { /* Actual Rate Simulation */ - let calculatedBasedOnTradingSystem = false let previousQuotedAssetActualSize - basedOnTradingSystem() basedOnSessionParameters() + basedOnTradingSystem() considerBestMatchWithOrderBook() recalculateActualSize() recalculateSizePlaced() - function basedOnTradingSystem() { - /* Based on the Trading System Definition */ - if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) { - /* Calculate this only once for this order */ - if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) { - tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id) - if (tradingEngineOrder.orderStatistics.actualRate.value !== undefined) { - calculatedBasedOnTradingSystem = true - } - } - } - } - } - } - function basedOnSessionParameters() { - /* Based on the Session Parameters Definition */ + /* + The default way to calculate the Actual Rate is to apply the Slippage defined + at the Session Parameters Definition. + */ if (calculatedBasedOnTradingSystem === false) { switch (tradingEngineOrder.type) { case 'Market Order': { @@ -101,24 +92,48 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { } } + function basedOnTradingSystem() { + /* + If the user specified a Formula to simulate the Actual Rate for this order, + we use that formula here. Note that if this Formula exists, this will override + the calculation based on Slippage. + */ + if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) { + /* Calculate this only once for this order */ + if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) { + tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id) + } + } + } + } + } + function considerBestMatchWithOrderBook() { /* - Until here we have got the rate based on the formula definition or based on session parameters slippage. - THe last check is about watching what happened in the market. Let's remember that the exchange will + Until here we have got the Actual Rate based on the formula definition or based on session parameters slippage. + The last check is about watching what happened in the market. Let's remember that the exchange will fill the order with the best possible matches at it's order book. That means that if the rate we set for the order was too low (for a sale order) or too hight (for a buy order), the actual rate - should be better than expected. + should be better than expected. + + We don't know what it is at the order book, but whe know that the last candle includes trades that bounced between + the min an max of the candle. That could tell us the above the max and below the min, the order would be matched + with orders at the order book. For that reason, we do this: */ switch (true) { case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { if (tradingEngineOrder.orderStatistics.actualRate.value > tradingEngine.current.episode.candle.max.value) { tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.max.value + tradingSystem.warnings.push([tradingSystemOrder.id, 'Actual Rate was adjusted to what it might be at the order book.']) } break } case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { if (tradingEngineOrder.orderStatistics.actualRate.value < tradingEngine.current.episode.candle.min.value) { tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.min.value + tradingSystem.warnings.push([tradingSystemOrder.id, 'Actual Rate was adjusted to what it might be at the order book.']) } break } @@ -128,21 +143,25 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { function recalculateActualSize() { /* - The Quoted Asset Actual Size in a backtest is equal to the Quoted Asset Size, which is related to - the Base Asset Size by the Order Rate. Since now we have the Actual Rate, we will need to recalculate - the Quoted Asset Actual Size using the Actual Rate. + Now that we have an Actual Rate, we need to adjust the Actual Size in Quoted Asset, since + Base Asset is what the exchange accepts as input and give information at. Quotes Asset properties are always calculated. */ previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = + tradingEngineOrder.orderBaseAsset.actualSize.value * + tradingEngineOrder.orderStatistics.actualRate.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) } function recalculateSizePlaced() { /* - For the Quoted Asset, the Size Placed was previously calculated with the previous Actual Size (or Size when these were equal). - Since we have just recaclcualted the Actual Size in Quoted Asset, we need to fix the Size Placed for the stage which was - calculated with the previous value. + Since the Actual Rate might have changed, we need to recalculate the Size Placed, where we accumulate all the Size Placed of + all orders of a Stage. For Base Asset there is nothing to do, since the Actual Rate does not have an inpact on it. + + For Quoted Asset, we need to first unaccount what this same orded added before to Size Placed (with the precious Actual Size) and + account with the new Actual Size. */ tradingEngineStage.stageQuotedAsset.sizePlaced.value = tradingEngineStage.stageQuotedAsset.sizePlaced.value - @@ -163,7 +182,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { switch (tradingEngineOrder.type) { case 'Market Order': { await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, + 'feesToBePaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker, @@ -173,7 +192,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { } case 'Limit Order': { await applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesToBePaid, + 'feesToBePaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker, @@ -189,10 +208,10 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { When we are live, it is the exchange who knows which order was filled and in which amount. Since we are backtesting here, we need to simulate what happens at the exchange. We can not simulate that an order is filled if the candle we - can see did not touch the order. + see did not touch the order. - For buy orders we are going to check if the price was below the order actual rate, and - for sell orders we are going to check if the price was above the order actual rate. + For limit buy orders we are going to check if the price was below the order actual rate, and + for limit sell orders we are going to check if the price was above the order actual rate. If we have a partial filling defined specifically for this order, we are going to use it to estimate how much of the order we will consider filled. @@ -221,7 +240,12 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { } } if (orderWasHit === true) { - let calculatedBasedOnTradingSystem = false + /* + Initially, we will assume that 100% of the order was filled. This might be overwritten + if there is a configuration for this order that details how much is must be considered + filled each time we check this. + */ + tradingEngineOrder.orderStatistics.percentageFilled.value = 100 /* Based on the Trading System Definition */ if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { @@ -235,29 +259,20 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { } tradingEngineOrder.orderStatistics.percentageFilled.value = tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10) - calculatedBasedOnTradingSystem = true } } } - /* - If there is no definition for this at the trading system we will assume orders - are 100% filled every single time they are hit, which is the most likely thing to happen. - */ - if (calculatedBasedOnTradingSystem === false) { - /* We will assume that 100% of the order was filled */ - tradingEngineOrder.orderStatistics.percentageFilled.value = 100 - } } } function feesPaidSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { /* - The way the Fess Paid are calculated is as follows: From the order size we apply the % of fee, + The way the Fess Paid are calculated is as follows: From the order Actual Size we apply the % of fee, that would give us the total fees for that order. To that we apply the % filled of the orders, that give use the final result of the fees paid for the current % filled. But before that, we will see if there is an specific definition on how to simulate the fees for - this order, or if there is not, we use the fees session parameters definitions. + this order, or if there is not, we use the Fees Structure defined at the Session Parameters. */ /* Based on the Trading System Definition */ @@ -265,7 +280,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) { if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) { applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, + 'feesPaid', tradingEngineOrder, tradingSystemOrder, tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage, @@ -282,7 +297,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { switch (tradingEngineOrder.type) { case 'Market Order': { applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, + 'feesPaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.taker, @@ -292,7 +307,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { } case 'Limit Order': { applyFeePercentage( - tradingEngineOrder.orderBaseAsset.feesPaid, + 'feesPaid', tradingEngineOrder, tradingSystemOrder, sessionParameters.feeStructure.config.maker, @@ -306,16 +321,15 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { function sizeFilledSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { /* Size Filled is the amount of Base Asset or Quoted Asset that at the exchange matches - another counterparty order. We alredy have simulated the Percentage Filled and the - Size To Be Filled, which is the order Actual Size minus the Fees To Be Paid. + another counterparty order. We calculate this using the Percentage Filled we already simulated. */ tradingEngineOrder.orderBaseAsset.sizeFilled.value = - tradingEngineOrder.orderBaseAsset.amountReceived.value * + tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.amountReceived.value * + tradingEngineOrder.orderQuotedAsset.actualSize.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) @@ -327,7 +341,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { This simulation is for informational purposes, so that users do not have to calculate it by themselves. The Amount Received out of the trade depends if we are Buying or Selling the Base Asset. If we are Buying, then the Amount Receiced will be in Base Asset. If we - are selling the it will be in Quoted Asset. + are selling then it will be in Quoted Asset. */ switch (true) { From 03d4314a0f59257b0169be195b45da7def63e9f3 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 13:08:30 +0100 Subject: [PATCH 17/41] Update OrdersSimulations.js --- LowFrequencyTrading/OrdersSimulations.js | 38 +++++++++++------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 948b2bab1..8834ee614 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -66,28 +66,26 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { The default way to calculate the Actual Rate is to apply the Slippage defined at the Session Parameters Definition. */ - if (calculatedBasedOnTradingSystem === false) { - switch (tradingEngineOrder.type) { - case 'Market Order': { - /* Actual Rate is simulated based on the Session Paremeters */ - let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 - switch (tradingSystemOrder.type) { - case 'Market Sell Order': { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount - break - } - case 'Market Buy Order': { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount - break - } + switch (tradingEngineOrder.type) { + case 'Market Order': { + /* Actual Rate is simulated based on the Session Paremeters */ + let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 + switch (tradingSystemOrder.type) { + case 'Market Sell Order': { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount + break + } + case 'Market Buy Order': { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount + break } - break - } - case 'Limit Order': { - /* In Limit Orders the actual rate is the rate of the order, there is no slippage */ - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - break } + break + } + case 'Limit Order': { + /* In Limit Orders the actual rate is the rate of the order, there is no slippage */ + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + break } } } From 816a2b8c78e4f03d149d8e3c8b9d145b967914d5 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 17:29:19 +0100 Subject: [PATCH 18/41] Update TradingOrders.js --- LowFrequencyTrading/TradingOrders.js | 138 +++++++++++++++++++-------- 1 file changed, 97 insertions(+), 41 deletions(-) diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 47c2044c8..5b8798a61 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -314,53 +314,109 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM } async function calculateOrderSize() { - /* Validate that this config exists */ - if (executionAlgorithm.config.percentageOfStageTargetSize === undefined) { badDefinitionUnhandledException(undefined, 'executionAlgorithm.config.percentageOfStageTargetSize === undefined', executionAlgorithm) } - if (isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true) { badDefinitionUnhandledException(undefined, 'isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true', executionAlgorithm) } + let algorithmSizeInBaseAsset + let algorithmSizeInQuotedAsset - let algorithmSizeInBaseAsset = tradingEngineStage.stageBaseAsset.targetSize.value * executionAlgorithm.config.percentageOfStageTargetSize / 100 - let algorithmSizeInQuotedAsset = tradingEngineStage.stageQuotedAsset.targetSize.value * executionAlgorithm.config.percentageOfStageTargetSize / 100 + requiredConfigurationValidation() + notNegativeBalanceValidation() + notPassingTargetSizeValidation() - /* Validate that this config exists */ - if (tradingSystemOrder.config.percentageOfAlgorithmSize === undefined) { badDefinitionUnhandledException(undefined, 'tradingSystemOrder.config.percentageOfAlgorithmSize === undefined', tradingSystemOrder) } - if (isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true) { badDefinitionUnhandledException(undefined, 'isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true', tradingSystemOrder) } - - /* - The Size calculation depends on how the user defined the size of the position. - The user could have defined the size of the position in Base Asset or Quoted Asset. - */ - switch (tradingEngineStage.stageDefinedIn.value) { - case 'Base Asset': { - /* Size in Base Asset */ - tradingEngineOrder.orderBaseAsset.size.value = algorithmSizeInBaseAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100 - - /* Check that the Size calculated would not surpass Stage Target Size */ - if ( - tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageBaseAsset.sizePlaced.value > - tradingEngineStage.stageBaseAsset.targetSize.value - ) { - tradingEngineOrder.orderBaseAsset.size.value = tradingEngineStage.stageBaseAsset.targetSize.value - tradingEngineStage.stageBaseAsset.sizePlaced.value - } - - /* Size in Quoted Asset */ - tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value - break + function requiredConfigurationValidation() { + /* Validate that this config exists */ + if (executionAlgorithm.config.percentageOfStageTargetSize === undefined) { + badDefinitionUnhandledException(undefined, 'executionAlgorithm.config.percentageOfStageTargetSize === undefined', executionAlgorithm) + } + if (isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true) { + badDefinitionUnhandledException(undefined, 'isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true', executionAlgorithm) } - case 'Quoted Asset': { - /* Size in Quoted Asset */ - tradingEngineOrder.orderQuotedAsset.size.value = algorithmSizeInQuotedAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100 - /* Check that the Size calculated would not surpass Stage Target Size */ - if ( - tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value > - tradingEngineStage.stageQuotedAsset.targetSize.value - ) { - tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineStage.stageQuotedAsset.targetSize.value - tradingEngineStage.stageQuotedAsset.sizePlaced.value + algorithmSizeInBaseAsset = tradingEngineStage.stageBaseAsset.targetSize.value * executionAlgorithm.config.percentageOfStageTargetSize / 100 + algorithmSizeInQuotedAsset = tradingEngineStage.stageQuotedAsset.targetSize.value * executionAlgorithm.config.percentageOfStageTargetSize / 100 + + /* Validate that this config exists */ + if (tradingSystemOrder.config.percentageOfAlgorithmSize === undefined) { + badDefinitionUnhandledException(undefined, 'tradingSystemOrder.config.percentageOfAlgorithmSize === undefined', tradingSystemOrder) + } + if (isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true) { + badDefinitionUnhandledException(undefined, 'isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true', tradingSystemOrder) + } + } + + function notPassingTargetSizeValidation() { + /* + The Size calculation depends on how the user defined the size of the position. + The user could have defined the size of the position in Base Asset or Quoted Asset. + */ + switch (tradingEngineStage.stageDefinedIn.value) { + case 'Base Asset': { + /* Size in Base Asset */ + tradingEngineOrder.orderBaseAsset.size.value = algorithmSizeInBaseAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100 + + /* Check that the Size calculated would not surpass Stage Target Size */ + if ( + tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageBaseAsset.sizePlaced.value > + tradingEngineStage.stageBaseAsset.targetSize.value + ) { + tradingEngineOrder.orderBaseAsset.size.value = + tradingEngineStage.stageBaseAsset.targetSize.value - + tradingEngineStage.stageBaseAsset.sizePlaced.value + + tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Size Placed does not exceed the Target Size for the stage.']) + } + + /* Size in Quoted Asset */ + tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value + break } + case 'Quoted Asset': { + /* Size in Quoted Asset */ + tradingEngineOrder.orderQuotedAsset.size.value = algorithmSizeInQuotedAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100 - /* Size in Base Asset */ - tradingEngineOrder.orderBaseAsset.size.value = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value - break + /* Check that the Size calculated would not surpass Stage Target Size */ + if ( + tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value > + tradingEngineStage.stageQuotedAsset.targetSize.value + ) { + tradingEngineOrder.orderQuotedAsset.size.value = + tradingEngineStage.stageQuotedAsset.targetSize.value - + tradingEngineStage.stageQuotedAsset.sizePlaced.value + + tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Size Placed does not exceed the Target Size for the stage.']) + } + + /* Size in Base Asset */ + tradingEngineOrder.orderBaseAsset.size.value = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value + + break + } + } + } + + function notNegativeBalanceValidation() { + /* Check that the Size calculated would not leave a negative balance */ + switch (true) { + case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { + if ( + tradingEngine.current.episode.episodeQuotedAsset.balance.value - tradingEngineOrder.orderQuotedAsset.size.value < 0 + ) { + tradingEngineOrder.orderQuotedAsset.size.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value + tradingEngineOrder.orderBaseAsset.size.value = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value + + tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Balance does not drop below zero.']) + } + break + } + case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { + if ( + tradingEngine.current.episode.episodeBaseAsset.balance.value - tradingEngineOrder.orderBaseAsset.size.value < 0 + ) { + tradingEngineOrder.orderBaseAsset.size.value = tradingEngine.current.episode.episodeBaseAsset.balance.value + tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value + + tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Balance does not drop below zero.']) + } + break + } } } From ff4a689ff4cd8572b089540419d3585033530b84 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 25 Oct 2020 18:41:20 +0100 Subject: [PATCH 19/41] Message Improved --- LowFrequencyTrading/OrdersSimulations.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 8834ee614..29934ee88 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -123,15 +123,25 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { switch (true) { case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': { if (tradingEngineOrder.orderStatistics.actualRate.value > tradingEngine.current.episode.candle.max.value) { + tradingSystem.warnings.push( + [ + tradingSystemOrder.id, + 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') was adjusted to what it might be at the order book. Resulting value = ' + tradingEngine.current.episode.candle.max.value + ] + ) tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.max.value - tradingSystem.warnings.push([tradingSystemOrder.id, 'Actual Rate was adjusted to what it might be at the order book.']) } break } case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': { if (tradingEngineOrder.orderStatistics.actualRate.value < tradingEngine.current.episode.candle.min.value) { + tradingSystem.warnings.push( + [ + tradingSystemOrder.id, + 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') was adjusted to what it might be at the order book. Resulting value = ' + tradingEngine.current.episode.candle.min.value + ] + ) tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.min.value - tradingSystem.warnings.push([tradingSystemOrder.id, 'Actual Rate was adjusted to what it might be at the order book.']) } break } From 71a484eae976a75b15a2f9e035484f9185fb9564 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 26 Oct 2020 10:06:27 +0100 Subject: [PATCH 20/41] Warnings Improved --- LowFrequencyTrading/OrdersSimulations.js | 4 ++-- LowFrequencyTrading/TradingOrders.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 29934ee88..693ede008 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -126,7 +126,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ tradingSystemOrder.id, - 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') was adjusted to what it might be at the order book. Resulting value = ' + tradingEngine.current.episode.candle.max.value + 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too hight. Changed to candle.max (' + tradingEngine.current.episode.candle.max.value + ')' ] ) tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.max.value @@ -138,7 +138,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ tradingSystemOrder.id, - 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') was adjusted to what it might be at the order book. Resulting value = ' + tradingEngine.current.episode.candle.min.value + 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too low. Changed to candle.min (' + tradingEngine.current.episode.candle.max.value + ')' ] ) tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.min.value diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 5b8798a61..62eb4f1ba 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -318,8 +318,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM let algorithmSizeInQuotedAsset requiredConfigurationValidation() - notNegativeBalanceValidation() notPassingTargetSizeValidation() + notNegativeBalanceValidation() function requiredConfigurationValidation() { /* Validate that this config exists */ @@ -399,10 +399,10 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if ( tradingEngine.current.episode.episodeQuotedAsset.balance.value - tradingEngineOrder.orderQuotedAsset.size.value < 0 ) { + tradingSystem.warnings.push([tradingSystemOrder.id, 'Order Size Quoted Asset ('+ tradingEngineOrder.orderQuotedAsset.size.value +') changed to Balance Quoted Asset (' + tradingEngine.current.episode.episodeQuotedAsset.balance.value + ') so that the Balance does not drop below zero.']) + tradingEngineOrder.orderQuotedAsset.size.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value tradingEngineOrder.orderBaseAsset.size.value = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value - - tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Balance does not drop below zero.']) } break } @@ -410,10 +410,10 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if ( tradingEngine.current.episode.episodeBaseAsset.balance.value - tradingEngineOrder.orderBaseAsset.size.value < 0 ) { + tradingSystem.warnings.push([tradingSystemOrder.id, 'Order Size Base Asset ('+ tradingEngineOrder.orderBaseAsset.size.value +') changed to Balance Base Asset (' + tradingEngine.current.episode.episodeBaseAsset.balance.value + ') so that the Balance does not drop below zero.']) + tradingEngineOrder.orderBaseAsset.size.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value - - tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Balance does not drop below zero.']) } break } From 507219a5fc2779b3520103ad58274936f3fa1f3c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 26 Oct 2020 12:40:19 +0100 Subject: [PATCH 21/41] Update OrdersSimulations.js --- LowFrequencyTrading/OrdersSimulations.js | 29 ++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 693ede008..f387a08e0 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -73,10 +73,26 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { switch (tradingSystemOrder.type) { case 'Market Sell Order': { tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount + tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) + + tradingSystem.warnings.push( + [ + sessionParameters.slippage.id, + 'Slippage (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' + ] + ) break } case 'Market Buy Order': { tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount + tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) + + tradingSystem.warnings.push( + [ + sessionParameters.slippage.id, + 'Slippage (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' + ] + ) break } } @@ -101,7 +117,16 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) { /* Calculate this only once for this order */ if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) { - tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id) + let newValue = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id) + newValue = global.PRECISE(newValue, 10) + + tradingSystem.warnings.push( + [ + tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id, + 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') replaced by this Formula value (' + newValue + ')' + ] + ) + tradingEngineOrder.orderStatistics.actualRate.value = newValue } } } @@ -126,7 +151,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ tradingSystemOrder.id, - 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too hight. Changed to candle.max (' + tradingEngine.current.episode.candle.max.value + ')' + 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too high. Changed to candle.max (' + tradingEngine.current.episode.candle.max.value + ')' ] ) tradingEngineOrder.orderStatistics.actualRate.value = tradingEngine.current.episode.candle.max.value From 4b769a6b420f11e27ff99a59de6bc5922e5a41bc Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 26 Oct 2020 13:22:16 +0100 Subject: [PATCH 22/41] warnings --- LowFrequencyTrading/OrdersSimulations.js | 4 +- LowFrequencyTrading/TradingOrders.js | 92 +++++++++++++++++++----- 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index f387a08e0..6566adc39 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -78,7 +78,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ sessionParameters.slippage.id, - 'Slippage (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' + 'sessionParameters.slippage.config.positionRate Slippage Amount (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' ] ) break @@ -90,7 +90,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ sessionParameters.slippage.id, - 'Slippage (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' + 'sessionParameters.slippage.config.positionRate Slippage Amount (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' ] ) break diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 62eb4f1ba..872f07bc2 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -159,7 +159,12 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM need to cancel it, since we could end up with inconsisten information at the accounting level. */ if (tradingSystemOrder.cancelOrderEvent !== undefined) { - tradingSystem.warnings.push([tradingSystemOrder.cancelOrderEvent.id, 'Skipping Cancel Event Checking because cheking the order at the exchange failed.']) + tradingSystem.warnings.push( + [ + tradingSystemOrder.cancelOrderEvent.id, + 'Skipping Cancel Event Checking because cheking the order at the exchange failed.' + ] + ) } return } @@ -255,14 +260,36 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM /* Check Size: We are not going to create Orders which size is equal or less to zero. */ if (tradingEngineOrder.orderBaseAsset.size.value <= 0 || tradingEngineOrder.orderQuotedAsset.size.value <= 0) { - tradingSystem.warnings.push([tradingSystemOrder.id, 'Could not open this order because its size would be zero.']) + tradingSystem.warnings.push( + [ + tradingEngineOrder.orderBaseAsset.size.id, + 'Could not open this order because its size would be zero.' + ] + ) + tradingSystem.warnings.push( + [ + tradingEngineOrder.orderQuotedAsset.size.id, + 'Could not open this order because its size would be zero.' + ] + ) return } /* Place Order at the Exchange, if needed. */ let result = await createOrderAtExchange(tradingSystemOrder, tradingEngineOrder) if (result !== true) { - tradingSystem.warnings.push([tradingSystemOrder.id, 'Could not open this order because something failed placing the order at the Exchange.']) + tradingSystem.warnings.push( + [ + tradingSystemOrder.id, + 'Could not open this order because something failed placing the order at the Exchange.' + ] + ) + tradingSystem.warnings.push( + [ + tradingEngineOrder.id, + 'Could not open this order because something failed placing the order at the Exchange.' + ] + ) return } @@ -323,22 +350,22 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM function requiredConfigurationValidation() { /* Validate that this config exists */ - if (executionAlgorithm.config.percentageOfStageTargetSize === undefined) { - badDefinitionUnhandledException(undefined, 'executionAlgorithm.config.percentageOfStageTargetSize === undefined', executionAlgorithm) + if (executionAlgorithm.config.percentageOfStageTargetSize === undefined) { + badDefinitionUnhandledException(undefined, 'executionAlgorithm.config.percentageOfStageTargetSize === undefined', executionAlgorithm) } - if (isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true) { - badDefinitionUnhandledException(undefined, 'isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true', executionAlgorithm) + if (isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true) { + badDefinitionUnhandledException(undefined, 'isNaN(executionAlgorithm.config.percentageOfStageTargetSize) === true', executionAlgorithm) } algorithmSizeInBaseAsset = tradingEngineStage.stageBaseAsset.targetSize.value * executionAlgorithm.config.percentageOfStageTargetSize / 100 algorithmSizeInQuotedAsset = tradingEngineStage.stageQuotedAsset.targetSize.value * executionAlgorithm.config.percentageOfStageTargetSize / 100 /* Validate that this config exists */ - if (tradingSystemOrder.config.percentageOfAlgorithmSize === undefined) { - badDefinitionUnhandledException(undefined, 'tradingSystemOrder.config.percentageOfAlgorithmSize === undefined', tradingSystemOrder) + if (tradingSystemOrder.config.percentageOfAlgorithmSize === undefined) { + badDefinitionUnhandledException(undefined, 'tradingSystemOrder.config.percentageOfAlgorithmSize === undefined', tradingSystemOrder) } - if (isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true) { - badDefinitionUnhandledException(undefined, 'isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true', tradingSystemOrder) + if (isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true) { + badDefinitionUnhandledException(undefined, 'isNaN(tradingSystemOrder.config.percentageOfAlgorithmSize) === true', tradingSystemOrder) } } @@ -357,11 +384,17 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageBaseAsset.sizePlaced.value > tradingEngineStage.stageBaseAsset.targetSize.value ) { + let previousValue = tradingEngineOrder.orderBaseAsset.size.value tradingEngineOrder.orderBaseAsset.size.value = tradingEngineStage.stageBaseAsset.targetSize.value - tradingEngineStage.stageBaseAsset.sizePlaced.value - tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Size Placed does not exceed the Target Size for the stage.']) + tradingSystem.warnings.push( + [ + tradingEngineOrder.orderBaseAsset.size.id, + 'Order size (' + previousValue + ') shrinked (' + tradingEngineOrder.orderBaseAsset.size.value + ') so that the Size Placed (' + tradingEngineStage.stageBaseAsset.sizePlaced.value + ') does not exceed the Target Size (' + tradingEngineStage.stageBaseAsset.targetSize.value + ') for the stage.' + ] + ) } /* Size in Quoted Asset */ @@ -377,11 +410,17 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingEngineOrder.orderQuotedAsset.size.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value > tradingEngineStage.stageQuotedAsset.targetSize.value ) { + let previousValue = tradingEngineOrder.orderQuotedAsset.size.value tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineStage.stageQuotedAsset.targetSize.value - tradingEngineStage.stageQuotedAsset.sizePlaced.value - tradingSystem.warnings.push([tradingSystemOrder.id, 'Order size shrinked so that the Size Placed does not exceed the Target Size for the stage.']) + tradingSystem.warnings.push( + [ + tradingEngineOrder.orderQuotedAsset.size.id, + 'Order size (' + previousValue + ') shrinked (' + tradingEngineOrder.orderQuotedAsset.size.value + ') so that the Size Placed (' + tradingEngineStage.stageQuotedAsset.sizePlaced.value + ') does not exceed the Target Size (' + tradingEngineStage.stageQuotedAsset.targetSize.value + ') for the stage.' + ] + ) } /* Size in Base Asset */ @@ -399,7 +438,12 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if ( tradingEngine.current.episode.episodeQuotedAsset.balance.value - tradingEngineOrder.orderQuotedAsset.size.value < 0 ) { - tradingSystem.warnings.push([tradingSystemOrder.id, 'Order Size Quoted Asset ('+ tradingEngineOrder.orderQuotedAsset.size.value +') changed to Balance Quoted Asset (' + tradingEngine.current.episode.episodeQuotedAsset.balance.value + ') so that the Balance does not drop below zero.']) + tradingSystem.warnings.push( + [ + tradingEngineOrder.orderQuotedAsset.size.id, + 'Order Size Quoted Asset (' + tradingEngineOrder.orderQuotedAsset.size.value + ') changed to Balance Quoted Asset (' + tradingEngine.current.episode.episodeQuotedAsset.balance.value + ') so that the Balance does not drop below zero.' + ] + ) tradingEngineOrder.orderQuotedAsset.size.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value tradingEngineOrder.orderBaseAsset.size.value = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value @@ -410,7 +454,12 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if ( tradingEngine.current.episode.episodeBaseAsset.balance.value - tradingEngineOrder.orderBaseAsset.size.value < 0 ) { - tradingSystem.warnings.push([tradingSystemOrder.id, 'Order Size Base Asset ('+ tradingEngineOrder.orderBaseAsset.size.value +') changed to Balance Base Asset (' + tradingEngine.current.episode.episodeBaseAsset.balance.value + ') so that the Balance does not drop below zero.']) + tradingSystem.warnings.push( + [ + tradingEngineOrder.orderBaseAsset.size.id, + 'Order Size Base Asset (' + tradingEngineOrder.orderBaseAsset.size.value + ') changed to Balance Base Asset (' + tradingEngine.current.episode.episodeBaseAsset.balance.value + ') so that the Balance does not drop below zero.' + ] + ) tradingEngineOrder.orderBaseAsset.size.value = tradingEngine.current.episode.episodeBaseAsset.balance.value tradingEngineOrder.orderQuotedAsset.size.value = tradingEngineOrder.orderBaseAsset.size.value * tradingEngineOrder.rate.value @@ -472,7 +521,11 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM let order = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder) if (order === undefined) { - tradingSystem.warnings.push([tradingSystemOrder.id, 'Could not verify the status of this order at the exchange.']) + tradingSystem.warnings.push( + [ + tradingSystemOrder.id, 'Could not verify the status of this order at the exchange.' + ] + ) return false } @@ -846,7 +899,12 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM let order = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder) if (order === undefined) { - tradingSystem.warnings.push([tradingSystemOrder.id, 'Could not verify the status of this order at the exchange, and syncronize the accounting.']) + tradingSystem.warnings.push( + [ + tradingSystemOrder.id, + 'Could not verify the status of this order at the exchange, and syncronize the accounting.' + ] + ) return false } From 8b75eabce48cea6d324de5c9acd781623f5a4516 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 26 Oct 2020 16:03:36 +0100 Subject: [PATCH 23/41] Update OrdersSimulations.js --- LowFrequencyTrading/OrdersSimulations.js | 46 ++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 6566adc39..333896cff 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -70,30 +70,32 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { case 'Market Order': { /* Actual Rate is simulated based on the Session Paremeters */ let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 - switch (tradingSystemOrder.type) { - case 'Market Sell Order': { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount - tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) + if (slippageAmount !== 0) { + switch (tradingSystemOrder.type) { + case 'Market Sell Order': { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount + tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) - tradingSystem.warnings.push( - [ - sessionParameters.slippage.id, - 'sessionParameters.slippage.config.positionRate Slippage Amount (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' - ] - ) - break - } - case 'Market Buy Order': { - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount - tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) + tradingSystem.warnings.push( + [ + sessionParameters.slippage.id, + 'slippage.config.positionRate Slippage Amount (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' + ] + ) + break + } + case 'Market Buy Order': { + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount + tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10) - tradingSystem.warnings.push( - [ - sessionParameters.slippage.id, - 'sessionParameters.slippage.config.positionRate Slippage Amount (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' +tradingEngineOrder.orderStatistics.actualRate.value + ')' - ] - ) - break + tradingSystem.warnings.push( + [ + sessionParameters.slippage.id, + 'slippage.config.positionRate Slippage Amount (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' + ] + ) + break + } } } break From 39d141fc5f2d0b190d80746623d434cf359e95ec Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 26 Oct 2020 17:53:38 +0100 Subject: [PATCH 24/41] Update OrdersSimulations.js --- LowFrequencyTrading/OrdersSimulations.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 333896cff..521bfb127 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -68,6 +68,11 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { */ switch (tradingEngineOrder.type) { case 'Market Order': { + /* + The default value for the Actual Rate is the rate of the order. + */ + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + /* Actual Rate is simulated based on the Session Paremeters */ let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 if (slippageAmount !== 0) { @@ -152,7 +157,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { if (tradingEngineOrder.orderStatistics.actualRate.value > tradingEngine.current.episode.candle.max.value) { tradingSystem.warnings.push( [ - tradingSystemOrder.id, + tradingEngineOrder.orderStatistics.actualRate.id, 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too high. Changed to candle.max (' + tradingEngine.current.episode.candle.max.value + ')' ] ) @@ -164,7 +169,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { if (tradingEngineOrder.orderStatistics.actualRate.value < tradingEngine.current.episode.candle.min.value) { tradingSystem.warnings.push( [ - tradingSystemOrder.id, + tradingEngineOrder.orderStatistics.actualRate.id, 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too low. Changed to candle.min (' + tradingEngine.current.episode.candle.max.value + ')' ] ) From 91910b184ca853df4d60f82ddab995cba093cbe8 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 27 Oct 2020 09:00:59 +0100 Subject: [PATCH 25/41] Update TradingOrders.js --- LowFrequencyTrading/TradingOrders.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 872f07bc2..b553f665c 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -259,17 +259,21 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM await calculateOrderSize() /* Check Size: We are not going to create Orders which size is equal or less to zero. */ - if (tradingEngineOrder.orderBaseAsset.size.value <= 0 || tradingEngineOrder.orderQuotedAsset.size.value <= 0) { + if (tradingEngineOrder.orderBaseAsset.size.value <= 0) { tradingSystem.warnings.push( [ tradingEngineOrder.orderBaseAsset.size.id, - 'Could not open this order because its size would be zero.' + 'Could not open this order because its size is zero.' ] ) + return + } + + if (tradingEngineOrder.orderQuotedAsset.size.value <= 0) { tradingSystem.warnings.push( [ tradingEngineOrder.orderQuotedAsset.size.id, - 'Could not open this order because its size would be zero.' + 'Could not open this order because its size is zero.' ] ) return @@ -378,6 +382,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM case 'Base Asset': { /* Size in Base Asset */ tradingEngineOrder.orderBaseAsset.size.value = algorithmSizeInBaseAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100 + tradingEngineOrder.orderBaseAsset.size.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.size.value, 10) /* Check that the Size calculated would not surpass Stage Target Size */ if ( @@ -391,7 +396,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingSystem.warnings.push( [ - tradingEngineOrder.orderBaseAsset.size.id, + tradingEngineStage.stageBaseAsset.targetSize.id, 'Order size (' + previousValue + ') shrinked (' + tradingEngineOrder.orderBaseAsset.size.value + ') so that the Size Placed (' + tradingEngineStage.stageBaseAsset.sizePlaced.value + ') does not exceed the Target Size (' + tradingEngineStage.stageBaseAsset.targetSize.value + ') for the stage.' ] ) @@ -404,6 +409,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM case 'Quoted Asset': { /* Size in Quoted Asset */ tradingEngineOrder.orderQuotedAsset.size.value = algorithmSizeInQuotedAsset * tradingSystemOrder.config.percentageOfAlgorithmSize / 100 + tradingEngineOrder.orderQuotedAsset.size.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.size.value, 10) /* Check that the Size calculated would not surpass Stage Target Size */ if ( @@ -417,7 +423,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingSystem.warnings.push( [ - tradingEngineOrder.orderQuotedAsset.size.id, + tradingEngineStage.stageQuotedAsset.targetSize.id, 'Order size (' + previousValue + ') shrinked (' + tradingEngineOrder.orderQuotedAsset.size.value + ') so that the Size Placed (' + tradingEngineStage.stageQuotedAsset.sizePlaced.value + ') does not exceed the Target Size (' + tradingEngineStage.stageQuotedAsset.targetSize.value + ') for the stage.' ] ) @@ -440,7 +446,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM ) { tradingSystem.warnings.push( [ - tradingEngineOrder.orderQuotedAsset.size.id, + [tradingEngine.current.episode.episodeQuotedAsset.balance.id, tradingEngineOrder.orderQuotedAsset.size.id], 'Order Size Quoted Asset (' + tradingEngineOrder.orderQuotedAsset.size.value + ') changed to Balance Quoted Asset (' + tradingEngine.current.episode.episodeQuotedAsset.balance.value + ') so that the Balance does not drop below zero.' ] ) @@ -456,7 +462,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM ) { tradingSystem.warnings.push( [ - tradingEngineOrder.orderBaseAsset.size.id, + [tradingEngine.current.episode.episodeBaseAsset.balance.id, tradingEngineOrder.orderBaseAsset.size.id], 'Order Size Base Asset (' + tradingEngineOrder.orderBaseAsset.size.value + ') changed to Balance Base Asset (' + tradingEngine.current.episode.episodeBaseAsset.balance.value + ') so that the Balance does not drop below zero.' ] ) From fa871397a0ea9a61e96910a5777354a2cf2ed02b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 27 Oct 2020 09:44:33 +0100 Subject: [PATCH 26/41] Update OrdersSimulations.js --- LowFrequencyTrading/OrdersSimulations.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 521bfb127..435f4cd98 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -64,8 +64,14 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { function basedOnSessionParameters() { /* The default way to calculate the Actual Rate is to apply the Slippage defined - at the Session Parameters Definition. + at the Session Parameters Definition. We are not going to apply the default if there + is a definition for this order on how to calculate the actual rate. */ + if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { + return + } + } switch (tradingEngineOrder.type) { case 'Market Order': { /* From 64380a34f265a272e760a5dd5b8b787d199aabaa Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 27 Oct 2020 10:39:56 +0100 Subject: [PATCH 27/41] Warnings Improved --- LowFrequencyTrading/OrdersSimulations.js | 10 +++++----- LowFrequencyTrading/TradingOrders.js | 21 ++++++++------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 435f4cd98..7359d1ed5 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -89,7 +89,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ - sessionParameters.slippage.id, + [sessionParameters.slippage.id, tradingEngineOrder.rate.id, tradingEngineOrder.orderStatistics.actualRate.id], 'slippage.config.positionRate Slippage Amount (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' ] ) @@ -101,7 +101,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ - sessionParameters.slippage.id, + [sessionParameters.slippage.id, tradingEngineOrder.rate.id, tradingEngineOrder.orderStatistics.actualRate.id], 'slippage.config.positionRate Slippage Amount (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' ] ) @@ -136,7 +136,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id, - 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') replaced by this Formula value (' + newValue + ')' + 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') calculated with this Formula (' + newValue + ')' ] ) tradingEngineOrder.orderStatistics.actualRate.value = newValue @@ -163,7 +163,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { if (tradingEngineOrder.orderStatistics.actualRate.value > tradingEngine.current.episode.candle.max.value) { tradingSystem.warnings.push( [ - tradingEngineOrder.orderStatistics.actualRate.id, + [tradingEngineOrder.orderStatistics.actualRate.id, tradingEngine.current.episode.candle.max.id], 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too high. Changed to candle.max (' + tradingEngine.current.episode.candle.max.value + ')' ] ) @@ -175,7 +175,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { if (tradingEngineOrder.orderStatistics.actualRate.value < tradingEngine.current.episode.candle.min.value) { tradingSystem.warnings.push( [ - tradingEngineOrder.orderStatistics.actualRate.id, + [tradingEngineOrder.orderStatistics.actualRate.id, tradingEngine.current.episode.candle.min.id], 'Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') too low. Changed to candle.min (' + tradingEngine.current.episode.candle.max.value + ')' ] ) diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index b553f665c..9435c36cb 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -262,7 +262,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (tradingEngineOrder.orderBaseAsset.size.value <= 0) { tradingSystem.warnings.push( [ - tradingEngineOrder.orderBaseAsset.size.id, + [tradingEngineOrder.orderBaseAsset.size.id, tradingSystemOrder.id], 'Could not open this order because its size is zero.' ] ) @@ -272,7 +272,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (tradingEngineOrder.orderQuotedAsset.size.value <= 0) { tradingSystem.warnings.push( [ - tradingEngineOrder.orderQuotedAsset.size.id, + [tradingEngineOrder.orderQuotedAsset.size.id, tradingSystemOrder.id ], 'Could not open this order because its size is zero.' ] ) @@ -284,13 +284,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (result !== true) { tradingSystem.warnings.push( [ - tradingSystemOrder.id, - 'Could not open this order because something failed placing the order at the Exchange.' - ] - ) - tradingSystem.warnings.push( - [ - tradingEngineOrder.id, + [tradingSystemOrder.id, tradingEngineOrder], 'Could not open this order because something failed placing the order at the Exchange.' ] ) @@ -396,7 +390,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingSystem.warnings.push( [ - tradingEngineStage.stageBaseAsset.targetSize.id, + [tradingEngineStage.stageBaseAsset.targetSize.id, tradingEngineOrder.orderBaseAsset.size.id, tradingEngineStage.stageBaseAsset.sizePlaced.id], 'Order size (' + previousValue + ') shrinked (' + tradingEngineOrder.orderBaseAsset.size.value + ') so that the Size Placed (' + tradingEngineStage.stageBaseAsset.sizePlaced.value + ') does not exceed the Target Size (' + tradingEngineStage.stageBaseAsset.targetSize.value + ') for the stage.' ] ) @@ -423,7 +417,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM tradingSystem.warnings.push( [ - tradingEngineStage.stageQuotedAsset.targetSize.id, + [tradingEngineStage.stageQuotedAsset.targetSize.id, tradingEngineOrder.orderQuotedAsset.size.id, tradingEngineStage.stageQuotedAsset.sizePlaced.id], 'Order size (' + previousValue + ') shrinked (' + tradingEngineOrder.orderQuotedAsset.size.value + ') so that the Size Placed (' + tradingEngineStage.stageQuotedAsset.sizePlaced.value + ') does not exceed the Target Size (' + tradingEngineStage.stageQuotedAsset.targetSize.value + ') for the stage.' ] ) @@ -529,7 +523,8 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (order === undefined) { tradingSystem.warnings.push( [ - tradingSystemOrder.id, 'Could not verify the status of this order at the exchange.' + [tradingSystemOrder.id, tradingEngineOrder.id], + 'Could not verify the status of this order at the exchange.' ] ) return false @@ -907,7 +902,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM if (order === undefined) { tradingSystem.warnings.push( [ - tradingSystemOrder.id, + [tradingSystemOrder.id, tradingEngineOrder.id], 'Could not verify the status of this order at the exchange, and syncronize the accounting.' ] ) From 259479112425419c0aa2b222414b7e261c88b775 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 27 Oct 2020 18:31:49 +0100 Subject: [PATCH 28/41] More Warnings --- LowFrequencyTrading/OrdersCalculations.js | 18 ++++++++++++ LowFrequencyTrading/OrdersSimulations.js | 35 +++++++++++++++++------ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js index bf334a6cd..f46332fd6 100644 --- a/LowFrequencyTrading/OrdersCalculations.js +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -143,6 +143,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { recalculateSizePlaced() function recalculateActualSize() { + if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return } /* Now we know the Actual Rate at which the order was filled. Since the actual rate is not the same as the Rate we defined for the order, we need to syncronize @@ -153,14 +154,24 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { got the Order Size in Base Asset, either from direct imput from the user or calculated from the Order Size in Quoted Asset input it by the user. So here we go. */ + previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value + tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + + tradingSystem.warnings.push( + [ + [tradingEngineOrder.orderQuotedAsset.actualSize.id, tradingEngineOrder.orderStatistics.actualRate.id], + 'Actual Size (' + previousQuotedAssetActualSize + ') recalculated (' + tradingEngineOrder.orderQuotedAsset.actualSize.value + ') because the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') is different than the Order Rate (' + tradingEngineOrder.rate.value + '))' + ] + ) } function recalculateSizePlaced() { + let previousStageQuotedAssetSizePlaced = tradingEngineStage.stageQuotedAsset.sizePlaced.value /* Since we changed the Actual Size in Quoted Asset, we need to fix the Size Placed in Quoted Asset that was previously calculated with the previous value of actualSize. @@ -174,6 +185,13 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { tradingEngineOrder.orderQuotedAsset.actualSize.value tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + + tradingSystem.warnings.push( + [ + [tradingEngineStage.stageQuotedAsset.sizePlaced.id, tradingEngineOrder.orderStatistics.actualRate.id], + 'Size Placed (' + previousStageQuotedAssetSizePlaced + ') recalculated (' + tradingEngineStage.stageQuotedAsset.sizePlaced.value + ') because the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') is different than the Order Rate (' + tradingEngineOrder.rate.value + '))' + ] + ) } } diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 7359d1ed5..05e190bc5 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -53,7 +53,14 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { function actualRateSimulation(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, applyFeePercentage) { /* Actual Rate Simulation */ - let previousQuotedAssetActualSize + let previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value + + if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) { + /* + The default value for the Actual Rate is the rate of the order. + */ + tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + } basedOnSessionParameters() basedOnTradingSystem() @@ -68,16 +75,12 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { is a definition for this order on how to calculate the actual rate. */ if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { + if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { return } } switch (tradingEngineOrder.type) { case 'Market Order': { - /* - The default value for the Actual Rate is the rate of the order. - */ - tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value /* Actual Rate is simulated based on the Session Paremeters */ let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 @@ -188,20 +191,27 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { } function recalculateActualSize() { + if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return } /* Now that we have an Actual Rate, we need to adjust the Actual Size in Quoted Asset, since Base Asset is what the exchange accepts as input and give information at. Quotes Asset properties are always calculated. */ - previousQuotedAssetActualSize = tradingEngineOrder.orderQuotedAsset.actualSize.value - tradingEngineOrder.orderQuotedAsset.actualSize.value = tradingEngineOrder.orderBaseAsset.actualSize.value * tradingEngineOrder.orderStatistics.actualRate.value tradingEngineOrder.orderQuotedAsset.actualSize.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.actualSize.value, 10) + + tradingSystem.warnings.push( + [ + [tradingEngineOrder.orderQuotedAsset.actualSize.id, tradingEngineOrder.orderStatistics.actualRate.id], + 'Actual Size (' + previousQuotedAssetActualSize + ') recalculated (' + tradingEngineOrder.orderQuotedAsset.actualSize.value + ') because the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') is different than the Order Rate (' + tradingEngineOrder.rate.value + '))' + ] + ) } function recalculateSizePlaced() { + if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.rate.value) { return } /* Since the Actual Rate might have changed, we need to recalculate the Size Placed, where we accumulate all the Size Placed of all orders of a Stage. For Base Asset there is nothing to do, since the Actual Rate does not have an inpact on it. @@ -209,6 +219,8 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { For Quoted Asset, we need to first unaccount what this same orded added before to Size Placed (with the precious Actual Size) and account with the new Actual Size. */ + let previousStageQuotedAssetSizePlaced = tradingEngineStage.stageQuotedAsset.sizePlaced.value + tradingEngineStage.stageQuotedAsset.sizePlaced.value = tradingEngineStage.stageQuotedAsset.sizePlaced.value - previousQuotedAssetActualSize @@ -218,6 +230,13 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingEngineOrder.orderQuotedAsset.actualSize.value tradingEngineStage.stageQuotedAsset.sizePlaced.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizePlaced.value, 10) + + tradingSystem.warnings.push( + [ + [tradingEngineStage.stageQuotedAsset.sizePlaced.id, tradingEngineOrder.orderStatistics.actualRate.id], + 'Size Placed (' + previousStageQuotedAssetSizePlaced + ') recalculated (' + tradingEngineStage.stageQuotedAsset.sizePlaced.value + ') because the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ') is different than the Order Rate (' + tradingEngineOrder.rate.value + '))' + ] + ) } } From fb7a07c7a25e298b903fc74477604efbaaf44c12 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 29 Oct 2020 10:03:53 +0100 Subject: [PATCH 29/41] Update IndicatorOutput.js --- IndicatorOutput.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/IndicatorOutput.js b/IndicatorOutput.js index d7f258b6b..cc585a1db 100644 --- a/IndicatorOutput.js +++ b/IndicatorOutput.js @@ -40,8 +40,6 @@ try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); } - let products = {} let mainDependency = {} let processingDailyFiles From 1006847b7a2ac583606602f6cc0d2d2f38b4f634 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 29 Oct 2020 12:04:41 +0100 Subject: [PATCH 30/41] Performance Fix --- Commons.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Commons.js b/Commons.js index 050791f24..eaa28c2e1 100644 --- a/Commons.js +++ b/Commons.js @@ -380,12 +380,6 @@ let product = {} product[variableName] = {} - /* Here is how we know if we are processing Yesterday. */ - let positionedAtYesterday = false - if (processingDailyFiles) { - positionedAtYesterday = (record.current.end < currentDay.valueOf()) - } - /* This is Loop Code */ try { eval(dataBuildingProcedure.loop.javascriptCode.code) @@ -423,9 +417,9 @@ results.push(product[variableName]); } - /* While we are positioned at Yesterday, we keep updating this data structure. */ + /* At the last instant of yesterday, we update the yesterday object. */ if (processingDailyFiles) { - if (positionedAtYesterday) { + if (record.current.end + 1 === currentDay.valueOf()) { yesterday.variable = JSON.parse(JSON.stringify(variable)) } } From 4b05d1bb6a3f02657e2fd93d7e2055f0ef80ceae Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 31 Oct 2020 14:33:30 +0100 Subject: [PATCH 31/41] Bug Fixes --- LowFrequencyTrading/OrdersCalculations.js | 8 +++---- LowFrequencyTrading/OrdersSimulations.js | 4 ++-- LowFrequencyTrading/TradingSystem.js | 26 +++++------------------ 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js index f46332fd6..2728ecc24 100644 --- a/LowFrequencyTrading/OrdersCalculations.js +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -63,7 +63,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { function recalculateActualSize() { /* - We will recalculate the Quoted Asset Actual Size. This will also give to it an initial value the first time we do this. + We will recalculate the Quoted Asset Actual Size. This will also give to it an initial value. In the next formula, we are unsing the rate of the order because we dont know yet the Actual Rate. This rate might be replaced afteerwards for the Actual Rate when this is calculated again once the Actual Rate is known. @@ -279,7 +279,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { need to calculate that by ourselves. */ tradingEngineOrder.orderQuotedAsset.sizeFilled.value = - tradingEngineOrder.orderQuotedAsset.sizeFilled.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 + tradingEngineOrder.orderQuotedAsset.actualSize.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) } @@ -297,7 +297,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { /* In this case the Amount Received is in Base Asset. */ - tradingEngineOrder.orderBaseAsset.amountReceived.value.value = + tradingEngineOrder.orderBaseAsset.amountReceived.value = tradingEngineOrder.orderBaseAsset.sizeFilled.value - tradingEngineOrder.orderBaseAsset.feesPaid.value break @@ -306,7 +306,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { /* In this case the Amount Received is in Quoted Asset. */ - tradingEngineOrder.orderQuotedAsset.amountReceived.value.value = + tradingEngineOrder.orderQuotedAsset.amountReceived.value = tradingEngineOrder.orderQuotedAsset.sizeFilled.value - tradingEngineOrder.orderQuotedAsset.feesPaid.value break diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 05e190bc5..5aafb1908 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -414,7 +414,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { /* In this case the Amount Received is in Base Asset. */ - tradingEngineOrder.orderBaseAsset.amountReceived.value.value = + tradingEngineOrder.orderBaseAsset.amountReceived.value = tradingEngineOrder.orderBaseAsset.sizeFilled.value - tradingEngineOrder.orderBaseAsset.feesPaid.value break @@ -423,7 +423,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { /* In this case the Amount Received is in Quoted Asset. */ - tradingEngineOrder.orderQuotedAsset.amountReceived.value.value = + tradingEngineOrder.orderQuotedAsset.amountReceived.value = tradingEngineOrder.orderQuotedAsset.sizeFilled.value - tradingEngineOrder.orderQuotedAsset.feesPaid.value break diff --git a/LowFrequencyTrading/TradingSystem.js b/LowFrequencyTrading/TradingSystem.js index 2e3af7ee3..ec82f80a7 100644 --- a/LowFrequencyTrading/TradingSystem.js +++ b/LowFrequencyTrading/TradingSystem.js @@ -17,7 +17,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM let tradingSystem let tradingEngine let sessionParameters - let dynamicIndicators + let dynamicIndicators const TRADING_STAGES_MODULE = require('./TradingStages.js') let tradingStagesModule = TRADING_STAGES_MODULE.newTradingStages(bot, logger, tradingEngineModule) @@ -131,8 +131,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM for (let i = 0; i < tradingSystem.dynamicIndicators.indicatorFunctions.length; i++) { let indicatorFunction = tradingSystem.dynamicIndicators.indicatorFunctions[i] - if (indicatorFunction.formula === undefined) { return} - if (indicatorFunction.config.codeName === undefined) { return} + if (indicatorFunction.formula === undefined) { return } + if (indicatorFunction.config.codeName === undefined) { return } dynamicIndicators[indicatorFunction.config.codeName] = tradingSystem.formulas.get(indicatorFunction.formula.id) } } @@ -257,15 +257,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM will not be considered an error. */ value = false - - if (code.indexOf('previous') > -1 && err.message.indexOf('of undefined') > -1 - ) { - /* - 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 { - error = err.message - } + error = err.message } tradingSystem.conditions.set(node.id, value) @@ -297,15 +289,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM will not be considered an error. */ value = 0 - - if (node.code.indexOf('previous') > -1 && err.message.indexOf('of undefined') > -1 - ) { - /* - 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 { - error = err.message - } + error = err.message } if (error !== undefined) { From 2c5359bb54c34e507c2006a0d919101d492a8819 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 31 Oct 2020 21:02:22 +0100 Subject: [PATCH 32/41] Refactoring --- LowFrequencyTrading/OrdersSimulations.js | 6 +++--- Session.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 5aafb1908..5f6b5d12e 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -83,7 +83,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { case 'Market Order': { /* Actual Rate is simulated based on the Session Paremeters */ - let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.positionRate / 100 + let slippageAmount = tradingEngineOrder.rate.value * sessionParameters.slippage.config.marketOrderRate / 100 if (slippageAmount !== 0) { switch (tradingSystemOrder.type) { case 'Market Sell Order': { @@ -93,7 +93,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ [sessionParameters.slippage.id, tradingEngineOrder.rate.id, tradingEngineOrder.orderStatistics.actualRate.id], - 'slippage.config.positionRate Slippage Amount (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' + 'slippage.config.marketOrderRate Slippage Amount (' + slippageAmount + ') substracted from Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' ] ) break @@ -105,7 +105,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { tradingSystem.warnings.push( [ [sessionParameters.slippage.id, tradingEngineOrder.rate.id, tradingEngineOrder.orderStatistics.actualRate.id], - 'slippage.config.positionRate Slippage Amount (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' + 'slippage.config.marketOrderRate Slippage Amount (' + slippageAmount + ') added to Order Rate (' + tradingEngineOrder.rate.value + ') to get the Actual Rate (' + tradingEngineOrder.orderStatistics.actualRate.value + ')' ] ) break diff --git a/Session.js b/Session.js index e35d08c1b..a0ad50e64 100644 --- a/Session.js +++ b/Session.js @@ -326,14 +326,14 @@ name: 'Missing Slippage', type: 'Slippage', config: { - positionRate: 0, + marketOrderRate: 0, stopLoss: 0, takeProfit: 0 } } } - if (bot.SESSION.parameters.slippage.config.positionRate === undefined) { // if the positionRate is missing we create a default one. - bot.SESSION.parameters.slippage.config.positionRate = 0 + if (bot.SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. + bot.SESSION.parameters.slippage.config.marketOrderRate = 0 } if (bot.SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. bot.SESSION.parameters.slippage.config.stopLoss = 0 From 3b9dc370b90401f7f1e147d0cbdd202fcf59de49 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 2 Nov 2020 18:43:54 +0100 Subject: [PATCH 33/41] AI implementation --- ReinforcementLearning/AiBot.js | 685 +++++++++++++++ .../MarkovDecisionProcess.js | 218 +++++ ReinforcementLearning/TrainingProcess.js | 786 ++++++++++++++++++ TradingBot.js | 4 +- Session.js => TradingSession.js | 4 +- TrainingSession.js | 519 ++++++++++++ 6 files changed, 2212 insertions(+), 4 deletions(-) create mode 100644 ReinforcementLearning/AiBot.js create mode 100644 ReinforcementLearning/MarkovDecisionProcess.js create mode 100644 ReinforcementLearning/TrainingProcess.js rename Session.js => TradingSession.js (99%) create mode 100644 TrainingSession.js diff --git a/ReinforcementLearning/AiBot.js b/ReinforcementLearning/AiBot.js new file mode 100644 index 000000000..612b55f3e --- /dev/null +++ b/ReinforcementLearning/AiBot.js @@ -0,0 +1,685 @@ +exports.newAiBot = function newAiBot(bot, parentLogger) { + + const MODULE_NAME = "Ai Bot"; + const FULL_LOG = true; + + const TRADING_PROCESS_MODULE = require(global.ROOT_DIR + '/LowFrequencyTrading/MachineLearningProcess.js'); + const FILE_STORAGE = require('./FileStorage.js'); + const SESSION = require(global.ROOT_DIR + 'TradingSession'); + + let fileStorage = FILE_STORAGE.newFileStorage(parentLogger); + let session = SESSION.newTrainingSession(bot, parentLogger) + + const DEBUG_MODULE = require(global.ROOT_DIR + 'DebugLog'); + let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble. + + let nextLoopTimeoutHandle; + + let thisObject = { + initialize: initialize, + run: run + }; + + let processConfig; + + return thisObject; + + function initialize(pProcessConfig, callBackFunction) { + /* This function is exactly the same in the 3 modules representing the 2 different bot types loops. */ + try { + processConfig = pProcessConfig; + if (bot.definedByUI === true) { + /* The code of the bot is defined at the UI. No need to load a file with the code. */ + session.initialize(processConfig, callBackFunction) + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function run(callBackFunction) { + try { + /* Some initial values*/ + let fixedTimeLoopIntervalHandle; + bot.STOP_SESSION = true; + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] run -> Stopping the Session now. ') } + + /* Heartbeats sent to the UI */ + bot.processHeartBeat = processHeartBeat + + loop(); + + function loop() { + try { + processHeartBeat(undefined, undefined, "Running...") + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } + + /* For each loop we want to create a new log file. */ + if (logger !== undefined) { + logger.finalize() + } + logger = DEBUG_MODULE.newDebugLog(); + global.LOGGER_MAP.set(MODULE_NAME, logger) + logger.bot = bot; + logger.initialize(); + + bot.loopCounter++; + bot.loopStartTime = new Date().valueOf(); + + let nextWaitTime; + + /* We tell the UI that we are running. */ + processHeartBeat(undefined, undefined, "Running...") + + /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */ + const UTILITIES = require(global.ROOT_DIR + 'CloudUtilities'); + const STATUS_REPORT = require(global.ROOT_DIR + 'StatusReport'); + const STATUS_DEPENDENCIES = require(global.ROOT_DIR + 'StatusDependencies'); + const DATA_DEPENDENCIES = require(global.ROOT_DIR + 'DataDependencies'); + const DATA_SET = require(global.ROOT_DIR + 'DataSet'); + const PROCESS_EXECUTION_EVENTS = require(global.ROOT_DIR + 'ProcessExecutionEvents'); + const PROCESS_OUTPUT = require(global.ROOT_DIR + 'ProcessOutput'); + + /* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */ + bot.processDatetime = new Date(); // This will be considered the process date and time, so as to have it consistenly all over the execution. + + /* High level log entry */ + console.log(new Date().toISOString() + " " + pad(bot.exchange, 20) + " " + pad(bot.market.baseAsset + '/' + bot.market.quotedAsset, 10) + " " + pad(bot.codeName, 30) + " " + pad(bot.process, 30) + + " Main Loop # " + pad(Number(bot.loopCounter), 8) + " " + bot.processNode.session.type + " " + bot.processNode.session.name) + + /* Checking if we need to need to emit any event */ + if (bot.SESSION_STATUS === 'Idle' && bot.STOP_SESSION === false) { + bot.SESSION_STATUS = 'Running' + } + + if (bot.SESSION_STATUS === 'Running' && bot.STOP_SESSION === true) { + bot.SESSION_STATUS = 'Stopped' + } + + global.EMIT_SESSION_STATUS (bot.SESSION_STATUS, bot.sessionKey) + + /* Checking if we should process this loop or not.*/ + if (bot.STOP_SESSION === true) { + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be run."); } + + console.log(new Date().toISOString() + " " + pad(bot.codeName, 20) + " " + pad(bot.process, 30) + + " Waiting for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be run. "); + + nextWaitTime = 'Waiting for Session'; + loopControl(nextWaitTime); + return + } + + /* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */ + + let processExecutionEvents + let userBot; + let processFramework; + let statusDependencies; + let dataDependencies; + + initializeProcessExecutionEvents(); + + function initializeProcessExecutionEvents() { + try { + processExecutionEvents = PROCESS_EXECUTION_EVENTS.newProcessExecutionEvents(bot, logger) + processExecutionEvents.initialize(processConfig, onInizialized); + + function onInizialized(err) { + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well."); + startProcessExecutionEvents() + 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, "[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + 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] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process."); + global.unexpectedError = err.message + processStopped() + return + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function startProcessExecutionEvents() { + try { + processExecutionEvents.start(onStarted); + + function onStarted(err) { + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well."); + + if (global.STOP_TASK_GRACEFULLY === true) { + loopControl() + return + } + + initializeStatusDependencies(); + 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, "[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + 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] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process."); + global.unexpectedError = err.message + processStopped() + return + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function initializeStatusDependencies() { + try { + statusDependencies = STATUS_DEPENDENCIES.newStatusDependencies(bot, logger, STATUS_REPORT, UTILITIES, PROCESS_OUTPUT); + statusDependencies.initialize(onInizialized); + + function onInizialized(err) { + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well."); + initializeDataDependencies(); + 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, "[WARN] run -> loop -> initializeStatusDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + 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] run -> loop -> initializeStatusDependencies -> onInizialized -> Operation Failed. Aborting the process."); + global.unexpectedError = err.message + processStopped() + return + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeStatusDependencies -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function initializeDataDependencies() { + try { + dataDependencies = DATA_DEPENDENCIES.newDataDependencies(bot, logger, DATA_SET); + dataDependencies.initialize(onInizialized); + + function onInizialized(err) { + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); } + switch (processConfig.framework.name) { + case 'Low-Frequency-Trading-Process': { + processFramework = TRADING_PROCESS_MODULE.newMachineLearningProcess(bot, logger, UTILITIES); + intitializeProcessFramework(); + break; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework not Supported."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework Name = " + processConfig.framework.name); + global.unexpectedError = err.message + processStopped() + return + } + } + 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, "[WARN] run -> loop -> initializeDataDependencies -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + 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] run -> loop -> initializeDataDependencies -> onInizialized -> Operation Failed. Aborting the process."); + global.unexpectedError = err.message + processStopped() + return + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeDataDependencies -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function intitializeProcessFramework() { + try { + processFramework.initialize(processConfig, statusDependencies, dataDependencies, onInizialized); + + function onInizialized(err) { + try { + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Execution finished well."); + startProcessFramework(); + 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, "[WARN] run -> loop -> intitializeProcessFramework -> onInizialized -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + 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] run -> loop -> intitializeProcessFramework -> onInizialized -> Operation Failed. Aborting the process."); + global.unexpectedError = err.message + processStopped() + return + } + case global.CUSTOM_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized > Unhandled custom response received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> intitializeProcessFramework -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function startProcessFramework() { + try { + processFramework.start(onFinished); + + function onFinished(err) { + try { + processFramework.finalize() + processFramework = undefined + dataDependencies.finalize() + dataDependencies = undefined + statusDependencies.finalize() + statusDependencies = undefined + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + finishProcessExecutionEvents() + 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, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + 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] run -> loop -> startProcessFramework -> onFinished -> Operation Failed. Aborting the process."); + global.unexpectedError = err.message + processStopped() + return + } + case global.CUSTOM_OK_RESPONSE.result: { + + switch (err.message) { + case "Dependency does not exist.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency does not exist. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + case "Dependency not ready.": { + logger.write(MODULE_NAME, "[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency not ready. This Loop will go to sleep."); + nextWaitTime = 'Sleep'; + loopControl(nextWaitTime); + return; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled custom response received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> onFinished -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> startProcessFramework -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function finishProcessExecutionEvents() { + try { + processExecutionEvents.finish(onFinished); + + function onFinished(err) { + try { + processExecutionEvents.finalize() + processExecutionEvents = undefined + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write(MODULE_NAME, "[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well."); + nextWaitTime = 'Normal'; + loopControl(nextWaitTime); + 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, "[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry."); + nextWaitTime = 'Retry'; + loopControl(nextWaitTime); + 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] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process."); + global.unexpectedError = err.message + processStopped() + return + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function loopControl(nextWaitTime) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> nextWaitTime = " + nextWaitTime); } + + /* We show we reached the end of the loop. */ + processHeartBeat(undefined, undefined, "Running...") + + /* Here we check if we must stop the loop gracefully. */ + shallWeStop(onStop, onContinue); + + function onStop() { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> onStop -> Stopping the Loop Gracefully. See you next time!"); } + + if (global.WRITE_LOGS_TO_FILES === 'true') { + logger.persist(); + } + + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Stopped') + processStopped() + return; + } + + function onContinue() { + /* Indicator bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */ + switch (nextWaitTime) { + case 'Waiting for Session': { + let waitTime = processConfig.sessionRunWaitTime + nextLoopTimeoutHandle = setTimeout(loop, waitTime); + let waitingTime = waitTime / 1000 / 60 + let label = 'minute/s' + if (waitingTime < 1) { + waitingTime = waitTime / 1000 + label = 'seconds' + } + processHeartBeat(undefined, undefined, "Waiting " + waitingTime + " " + label + " for " + bot.processNode.session.type + " " + bot.processNode.session.name + " to be run. ") + logger.persist(); + } + break + case 'Normal': { + let waitTime + if (processConfig.waitsForExecutionFinishedEvent === true) { + waitTime = 0 + } else { + switch (bot.SESSION.type) { + case 'Live Trading Session': { + waitTime = bot.SESSION.parameters.timeFrame.config.value + break + } + case 'Fordward Tessting Session': { + waitTime = bot.SESSION.parameters.timeFrame.config.value + break + } + case 'Paper Trading Session': { + waitTime = bot.SESSION.parameters.timeFrame.config.value + break + } + case 'Backtesting Session': { + waitTime = 0 + break + } + } + } + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (waitTime / 1000 / 60) + " minute/s."); } + nextLoopTimeoutHandle = setTimeout(loop, waitTime); + let waitingTime = waitTime / 1000 / 60 + let label = 'minute/s' + if (waitingTime < 1) { + waitingTime = waitTime / 1000 + label = 'seconds' + } + processHeartBeat(undefined, undefined, "Waiting " + waitingTime + " " + label + " for next execution.") + logger.persist(); + } + break; + case 'Retry': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.retryWaitTime / 1000) + " seconds."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.retryWaitTime); + processHeartBeat(undefined, undefined, "Trying to recover from some problem. Waiting " + processConfig.retryWaitTime / 1000 + " seconds for next execution.") + logger.persist(); + } + break; + case 'Sleep': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.sleepWaitTime / 60000) + " minutes."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.sleepWaitTime); + processHeartBeat(undefined, undefined, "Waiting " + processConfig.sleepWaitTime / 60000 + " minutes for next execution.") + logger.persist(); + } + break; + case 'Coma': { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (processConfig.comaWaitTime / 3600000) + " hours."); } + nextLoopTimeoutHandle = setTimeout(loop, processConfig.comaWaitTime); + processHeartBeat(undefined, undefined, "Waiting " + processConfig.comaWaitTime / 3600000 + " hours for next execution.") + logger.persist(); + } + break; + } + } + } + + function shallWeStop(stopCallBack, continueCallBack) { + try { + /* IMPORTANT: This function is exactly the same on the 3 modules. */ + if (!global.STOP_TASK_GRACEFULLY) { + continueCallBack(); + } else { + stopCallBack(); + } + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> shallWeStop -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> loop -> err = " + err.stack); + global.unexpectedError = err.message + processStopped() + return + } + } + + function processHeartBeat(processingDate, percentage, status) { + let event = { + seconds: (new Date()).getSeconds(), + processingDate: processingDate, + percentage: percentage, + status: status + } + global.EVENT_SERVER_CLIENT.raiseEvent(bot.processKey, 'Heartbeat', event) + } + + function processStopped() { + if (global.unexpectedError !== undefined) { + global.PROCESS_ERROR(bot.processKey, undefined, "An unexpected error caused the Process to stop.") + } else { + global.EVENT_SERVER_CLIENT.raiseEvent(bot.processKey, 'Stopped') + } + sessionStopped() + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + if (global.unexpectedError !== undefined) { + callBackFunction(global.DEFAULT_FAIL_RESPONSE) + } else { + callBackFunction(global.DEFAULT_OK_RESPONSE) + } + } + + function sessionStopped() { + if (bot.SESSION_STATUS === 'Running') { + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Stopped') + bot.SESSION_STATUS = 'Stopped' + } + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] run -> err = " + err.stack); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; diff --git a/ReinforcementLearning/MarkovDecisionProcess.js b/ReinforcementLearning/MarkovDecisionProcess.js new file mode 100644 index 000000000..1ef453e5d --- /dev/null +++ b/ReinforcementLearning/MarkovDecisionProcess.js @@ -0,0 +1,218 @@ +exports.newMarkovDecisionProcess = function newMarkovDecisionProcess(bot, logger) { + /* + We call the Trading Engine to the data structure that is needed in order to exevute the + trading protocol with the specific rules defined at the Trading System. + */ + const MODULE_NAME = 'Markov Decision Process' + let thisObject = { + mantain: mantain, + reset: reset, + getNodeById: getNodeById, + cloneValues: cloneValues, + setCurrentCandle: setCurrentCandle, + setCurrentCycle: setCurrentCycle, + initializeNode: initializeNode, + initialize: initialize, + finalize: finalize + } + + let tradingEngine + let sessionParameters + let nodesMap = new Map() + + return thisObject + + function initialize() { + + tradingEngine = bot.simulationState.tradingEngine + sessionParameters = bot.SESSION.parameters + + initializeNodeMap(tradingEngine) + + if (bot.FIRST_EXECUTION === true) { + /* + Here we will go through all the nodes in the Trading Engine hiriarchy and + apply the initial value to the value property when needed + */ + initializeNode(tradingEngine) + } + } + + function finalize() { + tradingEngine = undefined + sessionParameters = undefined + nodesMap = undefined + } + + function mantain() { + } + + function reset() { + + } + + function getNodeById(NodeId) { + return nodesMap.get(NodeId) + } + + function setCurrentCandle(candle) { + tradingEngine.current.episode.candle.begin.value = candle.begin + tradingEngine.current.episode.candle.end.value = candle.end + tradingEngine.current.episode.candle.open.value = candle.open + tradingEngine.current.episode.candle.close.value = candle.close + tradingEngine.current.episode.candle.min.value = candle.min + tradingEngine.current.episode.candle.max.value = candle.max + } + + function setCurrentCycle(cycle) { + tradingEngine.current.episode.cycle.value = cycle + tradingEngine.current.episode.cycle.lastBegin.value = tradingEngine.current.episode.cycle.begin.value + tradingEngine.current.episode.cycle.lastEnd.value = tradingEngine.current.episode.cycle.end.value + switch (cycle) { + case 'First': { + tradingEngine.current.episode.cycle.begin.value = + tradingEngine.current.episode.candle.begin.value + + sessionParameters.timeFrame.config.value + tradingEngine.current.episode.cycle.end.value = + tradingEngine.current.episode.candle.begin.value + + sessionParameters.timeFrame.config.value + + sessionParameters.timeFrame.config.value / 2 - 1 + break + } + case 'Second': { + tradingEngine.current.episode.cycle.begin.value = + tradingEngine.current.episode.candle.begin.value + + sessionParameters.timeFrame.config.value + + sessionParameters.timeFrame.config.value / 2 + tradingEngine.current.episode.cycle.end.value = + tradingEngine.current.episode.candle.begin.value + + sessionParameters.timeFrame.config.value + + sessionParameters.timeFrame.config.value * 4 / 4 - 1 + break + } + } + } + + function cloneValues(originNode, destinationNode) { + if (originNode === undefined) { return } + if (destinationNode === undefined) { return } + + /* Here copy the node value */ + destinationNode.value = originNode.value + + /* Now we go down through all the children of the origin node, assuming the destination node has the same children structure*/ + let nodeDefinition = global.APP_SCHEMA_MAP.get(originNode.type) + if (nodeDefinition === undefined) { return } + + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (originNode[property.name] !== undefined && destinationNode[property.name] !== undefined) { + cloneValues(originNode[property.name], destinationNode[property.name]) + } + previousPropertyName = property.name + } + break + } + case 'array': { + if (originNode[property.name] !== undefined && destinationNode[property.name] !== undefined) { + let originNodePropertyArray = originNode[property.name] + let destinationNodePropertyArray = destinationNode[property.name] + for (let m = 0; m < originNodePropertyArray.length; m++) { + cloneValues(originNodePropertyArray[m], destinationNodePropertyArray[m]) + } + } + break + } + } + } + } + } + + function initializeNodeMap(node) { + if (node === undefined) { return } + /* Add to Node Map */ + nodesMap.set(node.id, node) + + /* Now we go down through all this node children */ + let nodeDefinition = global.APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition === undefined) { return } + + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + initializeNodeMap(node[property.name]) + } + previousPropertyName = property.name + } + break + } + case 'array': { + if (node[property.name] !== undefined) { + let nodePropertyArray = node[property.name] + for (let m = 0; m < nodePropertyArray.length; m++) { + initializeNodeMap(nodePropertyArray[m]) + } + } + break + } + } + } + } + } + + function initializeNode(node) { + if (node === undefined) { return } + + /* Here we initialize the node value */ + if (node.config !== undefined) { + if (node.config.initialValue !== undefined) { + node.value = node.config.initialValue + } + } + + /* Now we go down through all this node children */ + let nodeDefinition = global.APP_SCHEMA_MAP.get(node.type) + if (nodeDefinition === undefined) { return } + + if (nodeDefinition.properties !== undefined) { + let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children. + for (let i = 0; i < nodeDefinition.properties.length; i++) { + let property = nodeDefinition.properties[i] + + switch (property.type) { + case 'node': { + if (property.name !== previousPropertyName) { + if (node[property.name] !== undefined) { + initializeNode(node[property.name]) + } + previousPropertyName = property.name + } + break + } + case 'array': { + if (node[property.name] !== undefined) { + let nodePropertyArray = node[property.name] + for (let m = 0; m < nodePropertyArray.length; m++) { + initializeNode(nodePropertyArray[m]) + } + } + break + } + } + } + } + } +} + diff --git a/ReinforcementLearning/TrainingProcess.js b/ReinforcementLearning/TrainingProcess.js new file mode 100644 index 000000000..7b1643d0a --- /dev/null +++ b/ReinforcementLearning/TrainingProcess.js @@ -0,0 +1,786 @@ +exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) { + /* + This Module will load all the process data dependencies from files and send them downstream. + After execution, will save the time range and status report of the process. + */ + const FULL_LOG = true; + const MODULE_NAME = "Training Process"; + const GMT_SECONDS = ':00.000 GMT+0000'; + + thisObject = { + initialize: initialize, + finalize: finalize, + start: start + }; + + let utilities = UTILITIES.newCloudUtilities(logger); + + let statusDependencies; + let dataDependenciesModule; + let dataFiles = new Map(); + let multiPeriodDataFiles = new Map(); + + const FILE_STORAGE = require('../FileStorage.js'); + let fileStorage = FILE_STORAGE.newFileStorage(logger); + + const MARKOV_DECISION_PROCESS_MODULE = require('./MarkovDecisionProcess.js') + let markovDecisionProcessModule = MARKOV_DECISION_PROCESS_MODULE.newMarkovDecisionProcess(bot, logger) + + let ALGORITHM_MODULE = require("./Algorithm") + let algorithmModule = ALGORITHM_MODULE.newTradingOutput(bot, logger, markovDecisionProcessModule, UTILITIES, FILE_STORAGE) + + let processConfig; + + return thisObject; + + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, callBackFunction) { + try { + logger.fileName = MODULE_NAME + logger.initialize() + + statusDependencies = pStatusDependencies + dataDependenciesModule = pDataDependencies + processConfig = pProcessConfig + + algorithmModule.initialize() + + callBackFunction(global.DEFAULT_OK_RESPONSE) + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function finalize() { + + markovDecisionProcessModule.finalize() + markovDecisionProcessModule = undefined + + algorithmModule.finalize() + algorithmModule = undefined + + dataFiles = undefined + multiPeriodDataFiles = undefined + statusDependencies = undefined + dataDependenciesModule = undefined + fileStorage = undefined + processConfig = undefined + thisObject = undefined + } + + async function start(callBackFunction) { + try { + let currentTimeFrame + let currentTimeFrameLabel + + /* Context Variables */ + let contextVariables = { + lastFile: undefined, // Datetime of the last file files sucessfully produced by this process. + dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history. + dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process. + }; + + getContextVariables() + + if (bot.FIRST_EXECUTION === true && bot.RESUME === false) { + /* + Here is where the Trading Engine and Trading Systems received are moved to the simulation state. + */ + bot.simulationState.tradingEngine = bot.TRADING_ENGINE + bot.simulationState.tradingSystem = bot.TRADING_SYSTEM + } + + /* We set up the Trading Engine Module. */ + markovDecisionProcessModule.initialize() + + /* Initializing the Trading Process Date */ + if (bot.FIRST_EXECUTION === true && bot.RESUME === false) { + /* + This funcion is going to be called many times by the Trading Bot Loop. + Only during the first execution and when the User is not resuming the execution + of a stopped session / task; we are going to initialize the Process Date Time. + This variable tell us which day we are standing at, specially while working + with Daily Files. From this Date is that we are going to load the Daily Files. + */ + bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.SESSION.parameters.timeRange.config.initialDatetime).valueOf() + } + + /* + This is the Date that is going to be used across the execution of this Trading Process. + We need this because it has a different life cycle than the processData stored at the + Trading Engine data structure. This date has to remain the same during the whole execution + of the Trading Process until the end, inclusind the writting of Data Ranges and Status Reports. + The processDate of the Trading Engine data structure on the other hand can be changed during + the simulation loop, once we discover that all candles from a certain date have benn processed. + Here is the point where we sync one and the other. + */ + let TrainingProcessDate = global.REMOVE_TIME(bot.simulationState.tradingEngine.current.episode.processDate.value) + await processSingleFiles() + + if (await processMarketFiles() === false) { + bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') + callBackFunction(global.DEFAULT_RETRY_RESPONSE) + return + } + /* + This is the Data Structure used at the Simulation with all indicator data. + We start creating it right here. + */ + let chart = {} + /* + Here we check if we need to get Daily Files or not. As an optimization, when + we are running on a Time Frame of 1hs or above, we are not going to load + dependencies on Daily Files. The way we recognize that is by checking if + we alreaady set a value to currentTimeFrame. We are also not going to loop + through days if we are processing market files. + */ + if (currentTimeFrame) { + /* We are processing Market Files */ + /* + With all the indicators data files loaded, we will build the chart object + data structure that will be used in user-defied conditions and formulas. + */ + bot.sessionHeartBeat(undefined, undefined, 'Waking up') + buildCharts(chart) + + if (checkThereAreCandles(chart) === true) { + bot.sessionHeartBeat(undefined, undefined, 'Running') + await generateOutput(chart) + bot.sessionHeartBeat(undefined, undefined, 'Saving') + await writeProcessFiles() + bot.sessionHeartBeat(undefined, undefined, 'Sleeping') + } else { + bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date. No candles found at.') + callBackFunction(global.DEFAULT_RETRY_RESPONSE) + return + } + + } else { + /* We are processing Daily Files */ + do { + global.EMIT_SESSION_STATUS(bot.SESSION_STATUS, bot.sessionKey) + /* + We update the Trading Process Date with the date calculated at the simulation. + We will use this date to load indicator and output files. After that we will + use it to save Output Files and later the Data Ranges. This is the point where + the date calculated by the Simulation is applied at the Trading Process Level. + */ + TrainingProcessDate = global.REMOVE_TIME(bot.simulationState.tradingEngine.current.episode.processDate.value) + + if (checkStopTaskGracefully() === false) { break } + if (checkStopProcessing() === false) { break } + + bot.sessionHeartBeat(undefined, undefined, 'Waking up') + if (await processDailyFiles() === false) { + bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') + callBackFunction(global.DEFAULT_RETRY_RESPONSE) + return + } + /* + With all the indicators data files loaded, we will build the chart object + data structure that will be used in user-defied conditions and formulas. + */ + buildCharts(chart) + /* + The process of generating the output includes the trading simulation. + */ + if (checkThereAreCandles(chart) === true) { + bot.sessionHeartBeat(undefined, undefined, 'Running') + await generateOutput(chart) + bot.sessionHeartBeat(undefined, undefined, 'Saving') + await writeProcessFiles() + bot.sessionHeartBeat(undefined, undefined, 'Sleeping') + } else { + bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date') + callBackFunction(global.DEFAULT_RETRY_RESPONSE) + return + } + /* + If for any reason the session was stopped, we will break this loop and exit the process. + */ + if (bot.STOP_SESSION === true) { break } + /* + When we get to the end of the market, we need to break this process loop in order + to let time pass, new information be collected from the exchange, new data built + into indicators, and eventually a new execution of this process. + */ + if (checkStopHeadOfTheMarket() === false) { break } + + } + while (true) + } + + checkIfSessionMustStop() + /* + Everything worked as expected. We return an OK code and wait for + the Bot Loop to call us again later. + */ + callBackFunction(global.DEFAULT_OK_RESPONSE) + + function checkThereAreCandles(chart) { + let sessionParameters = bot.SESSION.parameters + let propertyName = 'at' + sessionParameters.timeFrame.config.label.replace('-', '') + let candles = chart[propertyName].candles + + if (candles.length === 0) { + return false + } else { + return true + } + } + + function getContextVariables() { + try { + let thisReport; + let statusReport; + + /* We are going to use the start date as beging of market date. */ + contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.SESSION.parameters.timeRange.config.initialDatetime) + /* + Here we get the status report from the bot who knows which is the end of the market. + */ + statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point") + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return + } + + thisReport = statusReport.file; + + if (thisReport.lastFile === undefined) { + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> thisReport = " + JSON.stringify(thisReport)); + + let customOK = { + result: global.CUSTOM_OK_RESPONSE.result, + message: "Dependency not ready." + } + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message); + callBackFunction(customOK); + return + } + + contextVariables.dateEndOfMarket = new Date(thisReport.lastFile.valueOf()); + + /* Finally we get our own Status Report. */ + statusReport = statusDependencies.reportsByMainUtility.get("Self Reference") + + if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month. + logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. "); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return + } + + if (statusReport.status === "Status Report is corrupt.") { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + thisReport = statusReport.file; + bot.simulationState = thisReport.simulationState; + if (bot.simulationState === undefined) { bot.simulationState = {} } // This should happen only when there is no status report + + if (thisReport.lastFile !== undefined) { + if (bot.RESUME !== true) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Starting from the begining because bot has just started and resume execution was true."); } + startFromBegining(); + return + } + contextVariables.lastFile = new Date(thisReport.lastFile); + return + + } else { + /* + We are here becuse either: + 1. There is no status report + 2. There is no Last File (this happens on Market Files) + */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Starting from the begining of the market because own status report not found or lastFile was undefined."); } + startFromBegining(); + return + } + + function startFromBegining() { + contextVariables.lastFile = new Date(contextVariables.dateBeginOfMarket.getUTCFullYear() + "-" + (contextVariables.dateBeginOfMarket.getUTCMonth() + 1) + "-" + contextVariables.dateBeginOfMarket.getUTCDate() + " " + "00:00" + GMT_SECONDS); + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> startFromBegining -> contextVariables.lastFile = " + contextVariables.lastFile); } + } + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack); + if (err.message === "Cannot read property 'file' of undefined") { + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. "); + logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys)); + } + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + async function processSingleFiles() { + dataFiles = new Map(); + + for (let dependencyIndex = 0; dependencyIndex < dataDependenciesModule.nodeArray.length; dependencyIndex++) { + let dependency = dataDependenciesModule.nodeArray[dependencyIndex]; + let datasetModule = dataDependenciesModule.dataSetsModulesArray[dependencyIndex]; + + if (datasetModule.node.config.codeName !== "Single-File") { + continue + } + + if (dataDependenciesModule.isItADepenency('atAnyTimeFrame', datasetModule.node.parentNode.config.singularVariableName) !== true) { + continue + } + + let fileName = "Data.json"; + let filePath = datasetModule.node.parentNode.config.codeName + '/' + datasetModule.node.config.codeName; + + /* We cut the async calls via callBacks at this point, so as to have a clearer code upstream */ + let response = await asyncGetDatasetFile(datasetModule, filePath, fileName) + + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw (response.err) + } + + let dataFile = JSON.parse(response.text); + dataFiles.set(dependency.id, dataFile); + } + + let mapKey = "Single Files" + multiPeriodDataFiles.set(mapKey, dataFiles) + } + + async function processMarketFiles() { + /* + We do market files first since if the simulation is run on daily files, there will + be a loop to get each of those files and we do not need that loop to reload market files. + + We will iterate through all posible timeFrames. + */ + for (let n = 0; n < global.marketFilesPeriods.length; n++) { + const timeFrame = global.marketFilesPeriods[n][0]; + const timeFrameLabel = global.marketFilesPeriods[n][1]; + + dataFiles = new Map(); + + if (bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + currentTimeFrame = global.marketFilesPeriods[n][0]; + currentTimeFrameLabel = global.marketFilesPeriods[n][1]; + } + for (let dependencyIndex = 0; dependencyIndex < dataDependenciesModule.nodeArray.length; dependencyIndex++) { + let dependency = dataDependenciesModule.nodeArray[dependencyIndex]; + let datasetModule = dataDependenciesModule.dataSetsModulesArray[dependencyIndex]; + + if (dependency.referenceParent.config.codeName !== "Multi-Period-Market") { + continue + } + + if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { + if (!(bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + continue + } + } + + let fileName = "Data.json"; + let filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + timeFrameLabel; + + /* We cut the async calls via callBacks at this point, so as to have a clearer code upstream */ + let response = await asyncGetDatasetFile(datasetModule, filePath, fileName) + + if (response.err.message === 'File does not exist.') { + return false + } + + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw (response.err) + } + + let dataFile = JSON.parse(response.text); + let trimmedDataFile = trimDataFile(dataFile, datasetModule.node.parentNode.record) + dataFiles.set(dependency.id, trimmedDataFile); + + function trimDataFile(dataFile, recordDefinition) { + /* + Here we will discard all the records in a file that are outside of the current time range. + We will include the las element previous to the begining of the time range. This is needed + because during the simulation, the current period is not the open one, but the previous to + the open, and if we do not include the previous to the initial datetime there will be no + current objects at the begining of the simulation for many time frames. + */ + let beginIndex + let endIndex + let result = [] + for (let i = 0; i < recordDefinition.properties.length; i++) { + let property = recordDefinition.properties[i] + if (property.config.codeName === 'begin') { + beginIndex = i + } + if (property.config.codeName === 'end') { + endIndex = i + } + } + for (let i = 0; i < dataFile.length; i++) { + let dataRecord = dataFile[i] + let begin = dataRecord[beginIndex] + let end = dataRecord[endIndex] + if (end + timeFrame < bot.SESSION.parameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element + if (begin > bot.SESSION.parameters.timeRange.config.finalDatetime) { continue } + result.push(dataRecord) + } + return result + } + } + + let mapKey = global.marketFilesPeriods[n][1]; + multiPeriodDataFiles.set(mapKey, dataFiles) + } + return true + } + + async function processDailyFiles() { + /* Telling the world we are alive and doing well and which date we are processing right now. */ + let processingDateString = TrainingProcessDate.getUTCFullYear() + '-' + utilities.pad(TrainingProcessDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(TrainingProcessDate.getUTCDate(), 2); + bot.processHeartBeat(processingDateString, undefined, "Running...") + + /* + We will iterate through all posible timeFrames. + */ + for (let n = 0; n < global.dailyFilePeriods.length; n++) { + const timeFrame = global.dailyFilePeriods[n][0]; + const timeFrameLabel = global.dailyFilePeriods[n][1]; + + if (processConfig.framework.validtimeFrames !== undefined) { + let validPeriod = false; + for (let i = 0; i < processConfig.framework.validtimeFrames.length; i++) { + let period = processConfig.framework.validtimeFrames[i]; + if (period === timeFrameLabel) { validPeriod = true } + } + if (validPeriod === false) { + continue + } + } + + if (bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + currentTimeFrame = global.dailyFilePeriods[n][0]; + currentTimeFrameLabel = global.dailyFilePeriods[n][1]; + } + + dataFiles = new Map(); + + /* + We will iterate through all dependencies, in order to load the + files that later will end up at the chart data structure. + */ + for (let dependencyIndex = 0; dependencyIndex < dataDependenciesModule.nodeArray.length; dependencyIndex++) { + let dependency = dataDependenciesModule.nodeArray[dependencyIndex]; + let datasetModule = dataDependenciesModule.dataSetsModulesArray[dependencyIndex]; + + if (dependency.referenceParent.config.codeName !== "Multi-Period-Daily") { + continue + } + + if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { + if (!(bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + continue + } + } + + let dateForPath = TrainingProcessDate.getUTCFullYear() + '/' + utilities.pad(TrainingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(TrainingProcessDate.getUTCDate(), 2); + let filePath + if (dependency.referenceParent.config.codeName === "Multi-Period-Daily") { + filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + timeFrameLabel + "/" + dateForPath; + } else { + filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + dateForPath; + } + let fileName = "Data.json"; + + /* We cut the async calls via callBacks at this point, so as to have a clearer code upstream */ + let response = await asyncGetDatasetFile(datasetModule, filePath, fileName) + + if (response.err.message === 'File does not exist.') { + return false + } + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw (response.err) + } + + let dataFile = JSON.parse(response.text); + dataFiles.set(dependency.id, dataFile); + } + + let mapKey = global.dailyFilePeriods[n][1]; + multiPeriodDataFiles.set(mapKey, dataFiles) + } + return true + } + + function checkStopHeadOfTheMarket() { + /* + We need to check if we have reached the head of the market in order to know + when to break the Daily Files Process loop and give time for a new candles / + indicators to be built and continue the processing once this process is called + again. + */ + if (bot.simulationState.tradingEngine.current.episode.headOfTheMarket.value === true) { + return false + } + } + + function checkStopTaskGracefully() { + /* Validation that we dont need to stop. */ + if (global.STOP_TASK_GRACEFULLY === true) { + return false + } + } + + function checkStopProcessing() { + /* Validation that we dont need to stop. */ + if (bot.STOP_SESSION === true) { + return false + } + } + + function buildCharts(chart) { + const COMMONS = require('../Commons.js') + let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE) + + let mainDependency = {} + + /* The first phase here is about checking that we have everything we need at the definition level. */ + let dataDependencies = global.NODE_BRANCH_TO_ARRAY(bot.processNode.referenceParent.processDependencies, 'Data Dependency') + /* + We will filter ourt declared dependencies that are not present in the workspace. + This will allow the user to have less Data Mines loaded at the workspace + that the ones that a Trading Mine depends on. + */ + dataDependencies = global.FILTER_OUT_NODES_WITHOUT_REFERENCE_PARENT_FROM_NODE_ARRAY(dataDependencies) + + if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) { return } + + let outputDatasets = global.NODE_BRANCH_TO_ARRAY(bot.processNode.referenceParent.processOutput, 'Output Dataset') + if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return } + + /* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */ + for (let j = 0; j < global.marketFilesPeriods.length; j++) { + let timeFrameLabel = marketFilesPeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(timeFrameLabel) + let products = {} + + if (dataFiles !== undefined) { + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, currentTimeFrame) + + let propertyName = 'at' + timeFrameLabel.replace('-', '') + chart[propertyName] = products + } + } + + for (let j = 0; j < global.dailyFilePeriods.length; j++) { + let timeFrameLabel = global.dailyFilePeriods[j][1] + let dataFiles = multiPeriodDataFiles.get(timeFrameLabel) + let products = {} + + if (dataFiles !== undefined) { + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, currentTimeFrame) + + let propertyName = 'at' + timeFrameLabel.replace('-', '') + chart[propertyName] = products + } + } + + /* Single Files */ + let dataFiles = multiPeriodDataFiles.get('Single Files') + let products = {} + + if (dataFiles !== undefined) { + commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, currentTimeFrame) + + let propertyName = 'atAnyTimeFrame' + chart[propertyName] = products + } + } + + async function generateOutput(chart) { + await algorithmModule.start( + chart, + currentTimeFrame, + currentTimeFrameLabel, + TrainingProcessDate + ) + + /* + From here on, all other loops executions wont be the first execution and also + we will consider that it is not resuming a previous execution as well. + */ + bot.FIRST_EXECUTION = false + bot.RESUME = false + } + + async function writeProcessFiles() { + let outputDatasets = global.NODE_BRANCH_TO_ARRAY(bot.processNode.referenceParent.processOutput, 'Output Dataset') + + await writeTimeFramesFiles(currentTimeFrame, currentTimeFrameLabel) + await writeDataRanges() + + if (currentTimeFrame > global.dailyFilePeriods[0][0]) { + await writeMarketStatusReport(currentTimeFrameLabel) + } else { + await writeDailyStatusReport(currentTimeFrameLabel) + } + + async function writeDataRanges() { + for ( + let outputDatasetIndex = 0; + outputDatasetIndex < outputDatasets.length; + outputDatasetIndex++ + ) { + let productCodeName = outputDatasets[outputDatasetIndex].referenceParent.parentNode.config.codeName; + await writeDataRange(productCodeName); + } + + async function writeDataRange(productCodeName) { + let dataRange = { + begin: contextVariables.dateBeginOfMarket.valueOf(), + end: TrainingProcessDate.valueOf() + global.ONE_DAY_IN_MILISECONDS + } + + let fileContent = JSON.stringify(dataRange); + let fileName = '/Data.Range.json'; + let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName; + + let response = await fileStorage.asyncCreateTextFile(filePath, fileContent + '\n') + + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw (response.err) + } + + /* + Raise the event that the Data Range was Updated. + */ + let key = bot.dataMine + "-" + bot.codeName + "-" + productCodeName + "-" + bot.exchange + "-" + bot.market.baseAsset + '/' + bot.market.quotedAsset + let event = { + dateRange: dataRange + } + + global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Data Range Updated', event) + } + } + + async function writeTimeFramesFiles(currentTimeFrame, currentTimeFrameLabel) { + + for ( + let outputDatasetIndex = 0; + outputDatasetIndex < outputDatasets.length; + outputDatasetIndex++ + ) { + let productCodeName = outputDatasets[outputDatasetIndex].referenceParent.parentNode.config.codeName; + await writeTimeFramesFile(currentTimeFrameLabel, productCodeName, 'Multi-Period-Daily') + await writeTimeFramesFile(currentTimeFrameLabel, productCodeName, 'Multi-Period-Market') + + async function writeTimeFramesFile(currentTimeFrameLabel, productCodeName, processType) { + + let timeFramesArray = [] + timeFramesArray.push(currentTimeFrameLabel) + + let fileContent = JSON.stringify(timeFramesArray) + let fileName = '/Time.Frames.json'; + + let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + processType + fileName; + + let response = await fileStorage.asyncCreateTextFile(filePath, fileContent + '\n') + if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { + throw (response.err) + } + } + } + } + + async function writeDailyStatusReport(currentTimeFrameLabel) { + let reportKey = bot.dataMine + "-" + bot.codeName + "-" + bot.processNode.referenceParent.config.codeName + let thisReport = statusDependencies.statusReports.get(reportKey) + + thisReport.file.lastExecution = bot.currentDaytime + thisReport.file.lastFile = TrainingProcessDate + thisReport.file.simulationState = bot.simulationState + thisReport.file.timeFrames = currentTimeFrameLabel + await thisReport.asyncSave() + } + + async function writeMarketStatusReport(currentTimeFrameLabel) { + let reportKey = bot.dataMine + "-" + bot.codeName + "-" + bot.processNode.referenceParent.config.codeName + let thisReport = statusDependencies.statusReports.get(reportKey); + + thisReport.file.lastExecution = bot.processDatetime + thisReport.file.simulationState = bot.simulationState + thisReport.file.timeFrames = currentTimeFrameLabel + + logger.newInternalLoop(bot.codeName, bot.process, TrainingProcessDate) + await thisReport.asyncSave() + } + } + + function checkIfSessionMustStop() { + + if (bot.SESSION.type === 'Backtesting Session') { + /* + Backtests needs only one execution of this process to complete. + */ + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] checkIfSessionMustStop -> Backtesting Session Finished. Stopping the Session now. ') } + bot.SESSION.stop('Backtesting Session Finished.') + } + } + } + catch (err) { + /* An unhandled exception occured. in this case we return Fail and log the stack. */ + if (err.stack) { + logger.write(MODULE_NAME, "[ERROR] start -> Unhandled Exception. Will Abort this process. err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + + /* Some expected file was not found. We will return a RETRY code and move on. */ + if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') { + logger.write(MODULE_NAME, "[ERROR] File not Found. Will Retry the Process Loop.") + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + return + } + + /* Some other handled exception occured. We return Fail and move on. */ + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write(MODULE_NAME, "[ERROR] start -> Handled Exception. Will Abort this process. err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } + } + + function pad(str, max) { + str = str.toString(); + return str.length < max ? pad(" " + str, max) : str; + } + + async function asyncGetDatasetFile(datasetModule, filePath, fileName) { + + let promise = new Promise((resolve, reject) => { + + datasetModule.getTextFile(filePath, fileName, onFileReceived); + function onFileReceived(err, text) { + + let response = { + err: err, + text: text + } + resolve(response) + } + }); + + return promise + } +} diff --git a/TradingBot.js b/TradingBot.js index c5a5d05cb..4c98553e1 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -5,10 +5,10 @@ const TRADING_PROCESS_MODULE = require(global.ROOT_DIR + '/LowFrequencyTrading/TradingProcess.js'); const FILE_STORAGE = require('./FileStorage.js'); - const SESSION = require(global.ROOT_DIR + 'Session'); + const SESSION = require(global.ROOT_DIR + 'TradingSession'); let fileStorage = FILE_STORAGE.newFileStorage(parentLogger); - let session = SESSION.newSession(bot, parentLogger) + let session = SESSION.newTradingSession(bot, parentLogger) const DEBUG_MODULE = require(global.ROOT_DIR + 'DebugLog'); let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble. diff --git a/Session.js b/TradingSession.js similarity index 99% rename from Session.js rename to TradingSession.js index a0ad50e64..0bd371928 100644 --- a/Session.js +++ b/TradingSession.js @@ -1,6 +1,6 @@ -exports.newSession = function newSession(bot, parentLogger) { +exports.newTradingSession = function newTradingSession(bot, parentLogger) { - const MODULE_NAME = "Session" + const MODULE_NAME = "Trading Session" const FULL_LOG = true; let thisObject = { diff --git a/TrainingSession.js b/TrainingSession.js new file mode 100644 index 000000000..e7d78f6c0 --- /dev/null +++ b/TrainingSession.js @@ -0,0 +1,519 @@ +exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { + + const MODULE_NAME = "Training Session" + const FULL_LOG = true; + + let thisObject = { + initialize: initialize + } + + const SOCIAL_BOTS_MODULE = require('./SocialBots.js') + let socialBotsModule = SOCIAL_BOTS_MODULE.newSocialBots(bot, parentLogger) + + return thisObject; + + function initialize(pProcessConfig, callBackFunction) { + try { + /* Initialize this info so that everything is logged propeerly */ + bot.SESSION = { + name: bot.processNode.session.name, + id: bot.processNode.session.id + } + + /* Set the folderName for early logging */ + if (bot.processNode.session.config.folderName === undefined) { + bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id + } else { + bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + } + + /* Check if there is a session */ + if (bot.processNode.session === undefined) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> Cannot run without a Session."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + /* Listen to event to start or stop the session. */ + bot.sessionKey = bot.processNode.session.name + '-' + bot.processNode.session.type + '-' + bot.processNode.session.id + global.SESSION_MAP.set(bot.sessionKey, bot.sessionKey) + + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Session Status', undefined, bot.sessionKey, undefined, onSessionStatus) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Run Session', undefined, bot.sessionKey, undefined, onSessionRun) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Stop Session', undefined, bot.sessionKey, undefined, onSessionStop) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Resume Session', undefined, bot.sessionKey, undefined, onSessionResume) + + /* Connect this here so that it is accesible from other places */ + bot.sessionError = sessionError + bot.sessionWarning = sessionWarning + bot.sessionInfo = sessionInfo + + /* Heartbeats sent to the UI */ + bot.sessionHeartBeat = sessionHeartBeat + + callBackFunction(global.DEFAULT_OK_RESPONSE) + return + + function onSessionStatus() { + if (bot.SESSION_STATUS === 'Running') { + let event = { + status: 'Session Runnning' + } + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Status Response', event) + } else { + let event = { + status: 'Not Session Runnning' + } + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Status Response', event) + } + } + + function onSessionRun(message) { + try { + /* This happens when the UI is reloaded, the session was running and tries to run it again. */ + if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { + parentLogger.write(MODULE_NAME, "[WARN] onSessionRun -> Event received to run the Session while it was already running. ") + return + } + + /* We are going to run the Definition comming at the event. */ + bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem) + bot.TRADING_ENGINE = JSON.parse(message.event.tradingEngine) + bot.SESSION = JSON.parse(message.event.session) + bot.DEPENDENCY_FILTER = JSON.parse(message.event.dependencyFilter) + bot.RESUME = false + bot.FIRST_EXECUTION = true + bot.SESSION.stop = stopSession // stop function + + setUpSessionFolderName() + + /* We validate all parameters received and complete some that might be missing if needed. */ + if (checkParemeters() === false) { return } + + socialBotsModule.initialize() + + let allGood + switch (bot.SESSION.type) { + case 'Backtesting Session': { + allGood = startBackTesting(message) + break + } + case 'Live Trading Session': { + allGood = startLiveTrading(message) + break + } + case 'Fordward Testing Session': { + allGood = startFordwardTesting(message) + break + } + case 'Paper Trading Session': { + allGood = startPaperTrading(message) + break + } + } + if (allGood === true) { + bot.SESSION_STATUS = 'Idle' + bot.STOP_SESSION = false + } else { + bot.STOP_SESSION = true + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] onSessionRun -> Stopping the Session now. ') } + } + + socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is starting.") + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionRun -> err = " + err.stack); + } + } + + function onSessionStop() { + stopSession('Session Stopped From the User Interface.') + } + + function onSessionResume(message) { + try { + if (bot.SESSION.stop === undefined) { + parentLogger.write(MODULE_NAME, "[WARN] onSessionResume -> Event received to resume the Session that have never be ran before. ") + return + } + + /* This happens when the UI is reloaded, the session was running and tries to run it again. */ + if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { + parentLogger.write(MODULE_NAME, "[WARN] onSessionResume -> Event received to resume the Session while it was already running. ") + return + } + + bot.RESUME = true + bot.STOP_SESSION = false + + socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is resuming.") + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionResume -> err = " + err.stack); + } + } + + function stopSession(commandOrigin) { + + socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is stopping " + commandOrigin) + socialBotsModule.finalize() + bot.STOP_SESSION = true + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] stopSession -> Stopping the Session now. ') } + sessionInfo(bot.SESSION, commandOrigin) + } + + function setUpSessionFolderName() { + /* + The session object is overwritten when the session is run. For that reason we + need to setup again the folder name at the Session level. + Set the folderName for logging, reports, context and data output + */ + let config + if (bot.SESSION.config !== undefined) { + config = bot.SESSION.config + if (config.folderName === undefined) { + bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id + } else { + bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + } + } + } + + function checkParemeters() { + /* + Here we check all the Session Parameters received. If something critical is missing we abort returning false. If something + non critical is missing, we complete it with a default value. + */ + + if (bot.SESSION.parameters === undefined) { + let errorMessage = "Session Node with no Parameters." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION, errorMessage) + return false + } + + /* Time Range */ + if (bot.SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. + bot.SESSION.parameters.timeRange = { + name: 'Missing Time Range', + type: 'Time Range', + config: { + initialDatetime: (new Date()).valueOf(), + finalDatetime: (new Date()).valueOf() + global.ONE_YEAR_IN_MILISECONDS + } + } + } else { + /* Check that we received valid dates */ + if (bot.SESSION.type === 'Backtesting Session') { + if (isNaN(new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { + let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters, errorMessage) + return false + } + } + if (isNaN(new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { + let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters, errorMessage) + return false + } + } + + /* Session Type Forced Values */ + let today = (new Date()).valueOf() + let aYearAgo = today - global.ONE_YEAR_IN_MILISECONDS + let aYearFromNow = today + global.ONE_YEAR_IN_MILISECONDS + switch (bot.SESSION.type) { + case 'Backtesting Session': { + useDefaultDatetimes(aYearAgo, today) + break + } + case 'Live Trading Session': { + useDefaultDatetimes(today, aYearFromNow) + break + } + case 'Fordward Testing Session': { + useDefaultDatetimes(today, aYearFromNow) + break + } + case 'Paper Trading Session': { + useDefaultDatetimes(today, aYearFromNow) + break + } + } + + function useDefaultDatetimes(initialDefault, finalDefault) { + /* + Note that inside the system, we are going to deal with these + dates in their numeric value representation. + */ + + /* Initial Datetime */ + if (bot.SESSION.type === 'Backtesting Session') { + if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined) { + bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault + } else { + bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + } + } else { + /* Non backtest session can start from the past only if explicitly configured that way */ + if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { + bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault + } else { + bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + } + } + + /* Final Datetime */ + if (bot.SESSION.parameters.timeRange.config.finalDatetime === undefined) { + bot.SESSION.parameters.timeRange.config.finalDatetime = finalDefault + } else { + bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf() + } + } + + /* Time Frame */ + if (bot.SESSION.parameters.timeFrame === undefined) { + let errorMessage = "Session Parameters Node with no Time Frame." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters, errorMessage) + return false + } + if (bot.SESSION.parameters.timeFrame.config.label === undefined) { + let errorMessage = "Session Parameters Node with no Time Frame Label configuration." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters.timeFrame, errorMessage) + return false + } + bot.SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.SESSION.parameters.timeFrame.config.label) + if (bot.SESSION.parameters.timeFrame.config.value === undefined) { + let errorMessage = "Config error: label value not recognized. Try 01-min or 01-hs for example." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters.timeFrame, errorMessage) + return false + } + + /* Session Base Asset */ + if (bot.SESSION.parameters.sessionBaseAsset === undefined) { + let errorMessage = "Session Parameters Node with no Session Base Asset." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters, errorMessage) + return false + } + if (bot.SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { + let errorMessage = "Session Parameters Session Base Asset with no initialBalance configuration." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters.sessionBaseAsset, errorMessage) + return false + } + + /* Session Quoted Asset */ + if (bot.SESSION.parameters.sessionQuotedAsset === undefined) { + let errorMessage = "Session Parameters Node with no Session Quoted Asset." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters, errorMessage) + return false + } + if (bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { + let errorMessage = "Session Parameters Session Quoted Asset with no initialBalance configuration." + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) + bot.sessionError(bot.SESSION.parameters.sessionQuotedAsset, errorMessage) + return false + } + + /* Slippage */ + if (bot.SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. + bot.SESSION.parameters.slippage = { + name: 'Missing Slippage', + type: 'Slippage', + config: { + marketOrderRate: 0, + stopLoss: 0, + takeProfit: 0 + } + } + } + if (bot.SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. + bot.SESSION.parameters.slippage.config.marketOrderRate = 0 + } + if (bot.SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. + bot.SESSION.parameters.slippage.config.stopLoss = 0 + } + if (bot.SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. + bot.SESSION.parameters.slippage.config.takeProfit = 0 + } + + /* Fee Structure */ + if (bot.SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. + bot.SESSION.parameters.feeStructure = { + name: 'Missing Fee Structure', + type: 'Fee Structure', + config: { + maker: 0, + taker: 0 + } + } + } + if (bot.SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. + bot.SESSION.parameters.feeStructure.config.maker = 0 + } + if (bot.SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. + bot.SESSION.parameters.feeStructure.config.taker = 0 + } + + return true + } + + function startBackTesting(message) { + return true + } + + function startLiveTrading(message) { + if (bot.KEY === undefined || bot.SECRET === undefined) { + let errorMessage = "Key 'codeName' or 'secret' not provided. Plese check that and try again." + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> " + errorMessage); } + bot.sessionError(bot.SESSION, errorMessage) + return false + } + return true + } + + function startFordwardTesting(message) { + if (bot.KEY === undefined || bot.SECRET === undefined) { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startFordwardTesting -> Key name or Secret not provided, not possible to run the process in Forward Testing mode."); } + console.log("Key 'codeName' or 'secret' not provided. Plese check that and try again.") + return false + } + + /* Reduce the balance */ + let balancePercentage = 1 // This is the default value + + if (bot.SESSION.config.balancePercentage !== undefined) { + balancePercentage = bot.SESSION.config.balancePercentage + } + + bot.SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 + bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 + + bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 + bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 + + bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 + bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 + + pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value + + return true + } + + function startPaperTrading(message) { + return true + } + + function getTimeFrameFromLabel(timeFrameLabel) { + + for (let i = 0; i < global.marketFilesPeriods.length; i++) { + let value = global.marketFilesPeriods[i][0] + let label = global.marketFilesPeriods[i][1] + + if (timeFrameLabel === label) { + return value + } + } + + for (let i = 0; i < global.dailyFilePeriods.length; i++) { + let value = global.dailyFilePeriods[i][0] + let label = global.dailyFilePeriods[i][1] + + if (timeFrameLabel === label) { + return value + } + } + } + + function sessionHeartBeat(processingDate, percentage, status) { + let event = { + seconds: (new Date()).getSeconds(), + processingDate: processingDate, + percentage: percentage, + status: status + } + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Heartbeat', event) + + if (global.STOP_TASK_GRACEFULLY === true) { + bot.STOP_SESSION = true + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] sessionHeartBeat -> Stopping the Session now. ') } + } + } + + function sessionError(node, errorMessage) { + let event + if (node !== undefined) { + event = { + nodeName: node.name, + nodeType: node.type, + nodeId: node.id, + errorMessage: errorMessage + } + } else { + event = { + errorMessage: errorMessage + } + } + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Error', event) + + if (global.STOP_TASK_GRACEFULLY === true) { + bot.STOP_SESSION = true + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] sessionError -> Stopping the Session now. ') } + } + } + + function sessionWarning(node, warningMessage) { + let event + if (node !== undefined) { + event = { + nodeName: node.name, + nodeType: node.type, + nodeId: node.id, + warningMessage: warningMessage + } + } else { + event = { + warningMessage: warningMessage + } + } + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Warning', event) + + if (global.STOP_TASK_GRACEFULLY === true) { + bot.STOP_SESSION = true + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] sessionWarning -> Stopping the Session now. ') } + } + } + + function sessionInfo(node, infoMessage) { + let event + if (node !== undefined) { + event = { + nodeName: node.name, + nodeType: node.type, + nodeId: node.id, + infoMessage: infoMessage + } + } else { + event = { + infoMessage: infoMessage + } + } + global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Info', event) + + if (global.STOP_TASK_GRACEFULLY === true) { + bot.STOP_SESSION = true + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] sessionInfo -> Stopping the Session now. ') } + } + } + + } catch (err) { + parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; From b2ed704745ff3fdbd2bf977324aa0106e622c58b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 3 Nov 2020 12:16:59 +0100 Subject: [PATCH 34/41] Ai Bot Node implemented --- Commons.js | 3 + DebugLog.js | 10 +- LowFrequencyTrading/Announcements.js | 6 +- LowFrequencyTrading/OrdersCalculations.js | 2 +- LowFrequencyTrading/OrdersSimulations.js | 2 +- LowFrequencyTrading/Snapshots.js | 4 +- LowFrequencyTrading/TradingEngine.js | 2 +- LowFrequencyTrading/TradingEpisode.js | 2 +- LowFrequencyTrading/TradingExecution.js | 2 +- LowFrequencyTrading/TradingOrders.js | 12 +- LowFrequencyTrading/TradingOutput.js | 10 +- LowFrequencyTrading/TradingPosition.js | 2 +- LowFrequencyTrading/TradingProcess.js | 52 ++--- LowFrequencyTrading/TradingRecords.js | 2 +- LowFrequencyTrading/TradingSimulation.js | 12 +- LowFrequencyTrading/TradingStages.js | 10 +- LowFrequencyTrading/TradingStrategy.js | 2 +- LowFrequencyTrading/TradingSystem.js | 2 +- ProcessOutput.js | 6 + ReinforcementLearning/AiBot.js | 8 +- .../MarkovDecisionProcess.js | 2 +- ReinforcementLearning/TrainingProcess.js | 52 ++--- Root.js | 77 +++++++ SocialBots.js | 32 +-- StatusReport.js | 10 +- TelegramBot.js | 2 +- TradingBot.js | 26 +-- TradingSession.js | 188 +++++++++--------- TrainingSession.js | 148 +++++++------- server.js | 4 +- 30 files changed, 393 insertions(+), 299 deletions(-) diff --git a/Commons.js b/Commons.js index eaa28c2e1..eff4397be 100644 --- a/Commons.js +++ b/Commons.js @@ -99,6 +99,9 @@ if (botNode === undefined) { botNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Trading Bot') } + if (botNode === undefined) { + botNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'AI Bot') + } if (botNode === undefined) { logger.write(MODULE_NAME, "[ERROR] start -> Product Definition not attached to a Bot. Product Definition = "); callBackFunction(global.DEFAULT_FAIL_RESPONSE); diff --git a/DebugLog.js b/DebugLog.js index a1ef69a37..3b6f25570 100644 --- a/DebugLog.js +++ b/DebugLog.js @@ -80,10 +80,14 @@ exports.newDebugLog = function newDebugLog() { let filePath = thisObject.bot.filePathRoot + "/Logs/" + thisObject.bot.process + "/" - if (thisObject.bot.SESSION !== undefined) { - filePath = filePath + thisObject.bot.SESSION.folderName + "/" + executionDatetime; + if (thisObject.bot.TRADING_SESSION !== undefined) { + filePath = filePath + thisObject.bot.TRADING_SESSION.folderName + "/" + executionDatetime; } else { - filePath = filePath + executionDatetime; + if (thisObject.bot.AI_SESSION !== undefined) { + filePath = filePath + thisObject.bot.AI_SESSION.folderName + "/" + executionDatetime; + } else { + filePath = filePath + executionDatetime; + } } if (thisObject.bot.debug.year !== undefined) { diff --git a/LowFrequencyTrading/Announcements.js b/LowFrequencyTrading/Announcements.js index 2c63c88d1..a5bff8a8f 100644 --- a/LowFrequencyTrading/Announcements.js +++ b/LowFrequencyTrading/Announcements.js @@ -21,7 +21,7 @@ exports.newAnnouncements = function newAnnouncements(bot, logger) { function finalize() { tradingSystem = undefined tradingEngine = undefined - bot.SESSION.messagesSent = undefined + bot.TRADING_SESSION.messagesSent = undefined } function makeAnnoucements(node) { @@ -44,8 +44,8 @@ exports.newAnnouncements = function newAnnouncements(bot, logger) { text = tradingSystem.formulas.get(announcement.formula.id) } - if (bot.SESSION.socialBots !== undefined) { - bot.SESSION.socialBots.announce(text) + if (bot.TRADING_SESSION.socialBots !== undefined) { + bot.TRADING_SESSION.socialBots.announce(text) tradingSystem.announcements.push([announcement.id, text]) } else { tradingSystem.errors.push([announcement.id, 'Could not announce because session does not have Social Bots.']) diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js index 2728ecc24..4d24e4887 100644 --- a/LowFrequencyTrading/OrdersCalculations.js +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -23,7 +23,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { return thisObject function initialize() { - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem } diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 5f6b5d12e..1432a822f 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -23,7 +23,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { return thisObject function initialize() { - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem } diff --git a/LowFrequencyTrading/Snapshots.js b/LowFrequencyTrading/Snapshots.js index 6f1b02c5a..afa2c83e7 100644 --- a/LowFrequencyTrading/Snapshots.js +++ b/LowFrequencyTrading/Snapshots.js @@ -45,7 +45,7 @@ exports.newSnapshots = function newSnapshots(bot, logger) { function initialize() { tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters } function finalize() { @@ -322,7 +322,7 @@ exports.newSnapshots = function newSnapshots(bot, logger) { fileContent = "" + fileContent + ""; let fileName = pFileName + '.csv'; - let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + SNAPSHOTS_FOLDER_NAME; + let filePath = bot.filePathRoot + "/Output/" + bot.TRADING_SESSION.folderName + "/" + SNAPSHOTS_FOLDER_NAME; filePath += '/' + fileName fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated); diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index 20f158fe3..966c4213d 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -25,7 +25,7 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { function initialize() { tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters initializeNodeMap(tradingEngine) diff --git a/LowFrequencyTrading/TradingEpisode.js b/LowFrequencyTrading/TradingEpisode.js index ff08c22a1..56d6d289c 100644 --- a/LowFrequencyTrading/TradingEpisode.js +++ b/LowFrequencyTrading/TradingEpisode.js @@ -23,7 +23,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters } function finalize() { diff --git a/LowFrequencyTrading/TradingExecution.js b/LowFrequencyTrading/TradingExecution.js index 7e6619ec3..ecc3d6f35 100644 --- a/LowFrequencyTrading/TradingExecution.js +++ b/LowFrequencyTrading/TradingExecution.js @@ -24,7 +24,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters tradingOrdersModule.initialize() } diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 9435c36cb..651905e9c 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -27,7 +27,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters exchangeAPIModule.initialize() announcementsModule.initialize() @@ -476,7 +476,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM async function createOrderAtExchange(tradingSystemOrder, tradingEngineOrder) { /* Filter by Session Type */ - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Backtesting Session': { return true } @@ -503,7 +503,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM async function checkExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) { /* Filter by Session Type */ - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Backtesting Session': { return true } @@ -613,7 +613,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM function simulateCheckExchangeEvents(tradingEngineStage, tradingSystemOrder, tradingEngineOrder) { /* Filter by Session Type */ - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Backtesting Session': { break } @@ -839,7 +839,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM function simulateCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, exitType) { /* Filter by Session Type */ - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Backtesting Session': { break } @@ -870,7 +870,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM async function exchangeCancelOrder(tradingEngineStage, tradingSystemOrder, tradingEngineOrder, exitType) { /* Filter by Session Type */ - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Backtesting Session': { return } diff --git a/LowFrequencyTrading/TradingOutput.js b/LowFrequencyTrading/TradingOutput.js index 1f582d090..7cd76c81c 100644 --- a/LowFrequencyTrading/TradingOutput.js +++ b/LowFrequencyTrading/TradingOutput.js @@ -3,7 +3,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, tradingEngineM This module will load if necesary all the data outputs so that they can be appended with new records if needed. After running the simulation, it will save all the data outputs. */ - const MODULE_NAME = 'Trading Bot' + const MODULE_NAME = 'Trading Output' let thisObject = { initialize: initialize, @@ -133,7 +133,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, tradingEngineM if (dataset.config.type === 'Market Files') { let fileName = 'Data.json' - let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + let filePath = bot.filePathRoot + '/Output/' + bot.TRADING_SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel await readOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } @@ -149,7 +149,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, tradingEngineM let dateForPath = tradingProcessDate.getUTCFullYear() + '/' + utilities.pad(tradingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(tradingProcessDate.getUTCDate(), 2); let fileName = 'Data.json' - let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath + let filePath = bot.filePathRoot + '/Output/' + bot.TRADING_SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath await readOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } @@ -190,7 +190,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, tradingEngineM if (dataset.config.type === 'Market Files') { let fileName = 'Data.json' - let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + let filePath = bot.filePathRoot + '/Output/' + bot.TRADING_SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel await writeOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } @@ -206,7 +206,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, tradingEngineM let dateForPath = tradingProcessDate.getUTCFullYear() + '/' + utilities.pad(tradingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(tradingProcessDate.getUTCDate(), 2); let fileName = 'Data.json' - let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath + let filePath = bot.filePathRoot + '/Output/' + bot.TRADING_SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath await writeOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index 1dcf49727..a0448f1b6 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -31,7 +31,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters } function finalize() { diff --git a/LowFrequencyTrading/TradingProcess.js b/LowFrequencyTrading/TradingProcess.js index 17aca909c..95c076e1e 100644 --- a/LowFrequencyTrading/TradingProcess.js +++ b/LowFrequencyTrading/TradingProcess.js @@ -103,7 +103,7 @@ This variable tell us which day we are standing at, specially while working with Daily Files. From this Date is that we are going to load the Daily Files. */ - bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.SESSION.parameters.timeRange.config.initialDatetime).valueOf() + bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime).valueOf() } /* @@ -119,7 +119,7 @@ await processSingleFiles() if (await processMarketFiles() === false) { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -141,17 +141,17 @@ With all the indicators data files loaded, we will build the chart object data structure that will be used in user-defied conditions and formulas. */ - bot.sessionHeartBeat(undefined, undefined, 'Waking up') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waking up') buildCharts(chart) if (checkThereAreCandles(chart) === true) { - bot.sessionHeartBeat(undefined, undefined, 'Running') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Running') await generateOutput(chart) - bot.sessionHeartBeat(undefined, undefined, 'Saving') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Saving') await writeProcessFiles() - bot.sessionHeartBeat(undefined, undefined, 'Sleeping') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Sleeping') } else { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date. No candles found at.') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date. No candles found at.') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -159,7 +159,7 @@ } else { /* We are processing Daily Files */ do { - global.EMIT_SESSION_STATUS(bot.SESSION_STATUS, bot.sessionKey) + global.EMIT_SESSION_STATUS(bot.TRADING_SESSION_STATUS, bot.TRADING_SESSIONKey) /* We update the Trading Process Date with the date calculated at the simulation. We will use this date to load indicator and output files. After that we will @@ -171,9 +171,9 @@ if (checkStopTaskGracefully() === false) { break } if (checkStopProcessing() === false) { break } - bot.sessionHeartBeat(undefined, undefined, 'Waking up') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waking up') if (await processDailyFiles() === false) { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -186,13 +186,13 @@ The process of generating the output includes the trading simulation. */ if (checkThereAreCandles(chart) === true) { - bot.sessionHeartBeat(undefined, undefined, 'Running') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Running') await generateOutput(chart) - bot.sessionHeartBeat(undefined, undefined, 'Saving') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Saving') await writeProcessFiles() - bot.sessionHeartBeat(undefined, undefined, 'Sleeping') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Sleeping') } else { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -219,7 +219,7 @@ callBackFunction(global.DEFAULT_OK_RESPONSE) function checkThereAreCandles(chart) { - let sessionParameters = bot.SESSION.parameters + let sessionParameters = bot.TRADING_SESSION.parameters let propertyName = 'at' + sessionParameters.timeFrame.config.label.replace('-', '') let candles = chart[propertyName].candles @@ -236,7 +236,7 @@ let statusReport; /* We are going to use the start date as beging of market date. */ - contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.SESSION.parameters.timeRange.config.initialDatetime) + contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime) /* Here we get the status report from the bot who knows which is the end of the market. */ @@ -371,7 +371,7 @@ dataFiles = new Map(); - if (bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.marketFilesPeriods[n][0]; currentTimeFrameLabel = global.marketFilesPeriods[n][1]; } @@ -384,7 +384,7 @@ } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } @@ -431,8 +431,8 @@ let dataRecord = dataFile[i] let begin = dataRecord[beginIndex] let end = dataRecord[endIndex] - if (end + timeFrame < bot.SESSION.parameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element - if (begin > bot.SESSION.parameters.timeRange.config.finalDatetime) { continue } + if (end + timeFrame < bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element + if (begin > bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime) { continue } result.push(dataRecord) } return result @@ -468,7 +468,7 @@ } } - if (bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.dailyFilePeriods[n][0]; currentTimeFrameLabel = global.dailyFilePeriods[n][1]; } @@ -488,7 +488,7 @@ } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } @@ -653,7 +653,7 @@ let fileContent = JSON.stringify(dataRange); let fileName = '/Data.Range.json'; - let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName; + let filePath = bot.filePathRoot + "/Output/" + bot.TRADING_SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName; let response = await fileStorage.asyncCreateTextFile(filePath, fileContent + '\n') @@ -692,7 +692,7 @@ let fileContent = JSON.stringify(timeFramesArray) let fileName = '/Time.Frames.json'; - let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + processType + fileName; + let filePath = bot.filePathRoot + "/Output/" + bot.TRADING_SESSION.folderName + "/" + productCodeName + "/" + processType + fileName; let response = await fileStorage.asyncCreateTextFile(filePath, fileContent + '\n') if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { @@ -728,12 +728,12 @@ function checkIfSessionMustStop() { - if (bot.SESSION.type === 'Backtesting Session') { + if (bot.TRADING_SESSION.type === 'Backtesting Session') { /* Backtests needs only one execution of this process to complete. */ if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] checkIfSessionMustStop -> Backtesting Session Finished. Stopping the Session now. ') } - bot.SESSION.stop('Backtesting Session Finished.') + bot.TRADING_SESSION.stop('Backtesting Session Finished.') } } } diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index 0c2a2bfcc..3ae05a2bf 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -20,7 +20,7 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { function initialize(pOutputDatasetsMap) { tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters outputDatasetsMap = pOutputDatasetsMap // These are the files turned into arrays, stored in a Map by Product codeName. } diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index 87a64b504..25f9aa694 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -4,7 +4,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin the execution at each loop cycle of the Trading System Protocol. */ const FULL_LOG = true - const MODULE_NAME = 'Trading Simulation -> ' + bot.SESSION.name + const MODULE_NAME = 'Trading Simulation -> ' + bot.TRADING_SESSION.name let thisObject = { finalize: finalize, @@ -28,7 +28,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin let tradingSystem = bot.simulationState.tradingSystem let tradingEngine = bot.simulationState.tradingEngine - let sessionParameters = bot.SESSION.parameters + let sessionParameters = bot.TRADING_SESSION.parameters if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime) } if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime) } @@ -71,7 +71,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin This will happen when the sessionParameters.timeRange.config.initialDatetime is beyond the last candle available, meaning that the dataSet needs to be updated with more up-to-date data. */ - bot.SESSION.stop('Data is not up-to-date enough. Please start the Masters Data Mining Operation.') + bot.TRADING_SESSION.stop('Data is not up-to-date enough. Please start the Masters Data Mining Operation.') if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Data is not up-to-date enough. Stopping the Session now. ') } return } @@ -210,7 +210,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin if (checkFinalDatetime() === false) { closeEpisode('Final Datetime Reached') breakLoop = true - bot.SESSION.stop('Final Datetime Reached') + bot.TRADING_SESSION.stop('Final Datetime Reached') if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Final Datetime Reached. Stopping the Session now. ') } return } @@ -218,7 +218,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin if (checkMinimunAndMaximunBalance() === false) { closeEpisode('Min or Max Balance Reached') breakLoop = true - bot.SESSION.stop('Min or Max Balance Reached') + bot.TRADING_SESSION.stop('Min or Max Balance Reached') if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] runSimulation -> Min or Max Balance Reached. Stopping the Session now. ') } return } @@ -276,7 +276,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin let processingDate = heartBeatDate.getUTCFullYear() + '-' + utilities.pad(heartBeatDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(heartBeatDate.getUTCDate(), 2) - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Simulation ' + bot.sessionKey + ' Loop # ' + tradingEngine.current.episode.candle.index.value + ' @ ' + processingDate) } + if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Simulation ' + bot.TRADING_SESSIONKey + ' Loop # ' + tradingEngine.current.episode.candle.index.value + ' @ ' + processingDate) } /* Logging to console and disk */ if (global.areEqualDates(currentDate, new Date()) === false) { diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index 7c95d8ac7..9c25e8992 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -47,7 +47,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters tradingStrategyModule.initialize() tradingPositionModule.initialize() @@ -160,7 +160,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM announcementsModule.makeAnnoucements(triggerStage.triggerOn) announcementsModule.makeAnnoucements(triggerStage) - if (bot.SESSION.type === 'Backtesting Session') { + if (bot.TRADING_SESSION.type === 'Backtesting Session') { if (sessionParameters.snapshots !== undefined) { if (sessionParameters.snapshots.config.strategy === true) { snapshotsModule.strategyEntry() @@ -245,7 +245,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM announcementsModule.makeAnnoucements(triggerStage.takePosition) announcementsModule.makeAnnoucements(strategy.openStage) - if (bot.SESSION.type === 'Backtesting Session') { + if (bot.TRADING_SESSION.type === 'Backtesting Session') { if (sessionParameters.snapshots !== undefined) { if (sessionParameters.snapshots.config.position === true) { snapshotsModule.positionEntry() @@ -856,7 +856,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM function closePositionAndStrategy() { /* Taking Position Snapshot */ - if (bot.SESSION.type === 'Backtesting Session') { + if (bot.TRADING_SESSION.type === 'Backtesting Session') { if (sessionParameters.snapshots !== undefined) { if (sessionParameters.snapshots.config.position === true) { snapshotsModule.positionExit() @@ -865,7 +865,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM } /* Taking Strategy Snapshot */ - if (bot.SESSION.type === 'Backtesting Session') { + if (bot.TRADING_SESSION.type === 'Backtesting Session') { if (sessionParameters.snapshots !== undefined) { if (sessionParameters.snapshots.config.strategy === true) { snapshotsModule.strategyExit() diff --git a/LowFrequencyTrading/TradingStrategy.js b/LowFrequencyTrading/TradingStrategy.js index 9e9b8b8ce..3c11a59c0 100644 --- a/LowFrequencyTrading/TradingStrategy.js +++ b/LowFrequencyTrading/TradingStrategy.js @@ -19,7 +19,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng function initialize() { tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters } function finalize() { diff --git a/LowFrequencyTrading/TradingSystem.js b/LowFrequencyTrading/TradingSystem.js index ec82f80a7..58c3135c5 100644 --- a/LowFrequencyTrading/TradingSystem.js +++ b/LowFrequencyTrading/TradingSystem.js @@ -28,7 +28,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.TRADING_SESSION.parameters tradingSystem.conditions = new Map() tradingSystem.formulas = new Map() diff --git a/ProcessOutput.js b/ProcessOutput.js index 6075ee9e7..8af65ef2a 100644 --- a/ProcessOutput.js +++ b/ProcessOutput.js @@ -32,6 +32,9 @@ if (botNode === undefined) { botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'Sensor Bot') } + if (botNode === undefined) { + botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'AI Bot') + } /* Some validations to verify that everything is in place. */ if (outputDataset.referenceParent !== undefined) { @@ -119,6 +122,9 @@ if (botNode === undefined) { botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'Sensor Bot') } + if (botNode === undefined) { + botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'AI Bot') + } /* Some validations to verify that everything is in place. */ if (outputDataset.referenceParent !== undefined) { diff --git a/ReinforcementLearning/AiBot.js b/ReinforcementLearning/AiBot.js index 612b55f3e..01e9c90aa 100644 --- a/ReinforcementLearning/AiBot.js +++ b/ReinforcementLearning/AiBot.js @@ -560,17 +560,17 @@ exports.newAiBot = function newAiBot(bot, parentLogger) { if (processConfig.waitsForExecutionFinishedEvent === true) { waitTime = 0 } else { - switch (bot.SESSION.type) { + switch (bot.AI_SESSION.type) { case 'Live Trading Session': { - waitTime = bot.SESSION.parameters.timeFrame.config.value + waitTime = bot.AI_SESSION.parameters.timeFrame.config.value break } case 'Fordward Tessting Session': { - waitTime = bot.SESSION.parameters.timeFrame.config.value + waitTime = bot.AI_SESSION.parameters.timeFrame.config.value break } case 'Paper Trading Session': { - waitTime = bot.SESSION.parameters.timeFrame.config.value + waitTime = bot.AI_SESSION.parameters.timeFrame.config.value break } case 'Backtesting Session': { diff --git a/ReinforcementLearning/MarkovDecisionProcess.js b/ReinforcementLearning/MarkovDecisionProcess.js index 1ef453e5d..6b4e1cd1f 100644 --- a/ReinforcementLearning/MarkovDecisionProcess.js +++ b/ReinforcementLearning/MarkovDecisionProcess.js @@ -25,7 +25,7 @@ exports.newMarkovDecisionProcess = function newMarkovDecisionProcess(bot, logger function initialize() { tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.SESSION.parameters + sessionParameters = bot.AI_SESSION.parameters initializeNodeMap(tradingEngine) diff --git a/ReinforcementLearning/TrainingProcess.js b/ReinforcementLearning/TrainingProcess.js index 7b1643d0a..e7dc6c62e 100644 --- a/ReinforcementLearning/TrainingProcess.js +++ b/ReinforcementLearning/TrainingProcess.js @@ -103,7 +103,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) This variable tell us which day we are standing at, specially while working with Daily Files. From this Date is that we are going to load the Daily Files. */ - bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.SESSION.parameters.timeRange.config.initialDatetime).valueOf() + bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime).valueOf() } /* @@ -119,7 +119,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) await processSingleFiles() if (await processMarketFiles() === false) { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -141,17 +141,17 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) With all the indicators data files loaded, we will build the chart object data structure that will be used in user-defied conditions and formulas. */ - bot.sessionHeartBeat(undefined, undefined, 'Waking up') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waking up') buildCharts(chart) if (checkThereAreCandles(chart) === true) { - bot.sessionHeartBeat(undefined, undefined, 'Running') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Running') await generateOutput(chart) - bot.sessionHeartBeat(undefined, undefined, 'Saving') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Saving') await writeProcessFiles() - bot.sessionHeartBeat(undefined, undefined, 'Sleeping') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Sleeping') } else { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date. No candles found at.') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date. No candles found at.') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -159,7 +159,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) } else { /* We are processing Daily Files */ do { - global.EMIT_SESSION_STATUS(bot.SESSION_STATUS, bot.sessionKey) + global.EMIT_SESSION_STATUS(bot.TRADING_SESSION_STATUS, bot.TRADING_SESSIONKey) /* We update the Trading Process Date with the date calculated at the simulation. We will use this date to load indicator and output files. After that we will @@ -171,9 +171,9 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) if (checkStopTaskGracefully() === false) { break } if (checkStopProcessing() === false) { break } - bot.sessionHeartBeat(undefined, undefined, 'Waking up') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waking up') if (await processDailyFiles() === false) { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be run') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -186,13 +186,13 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) The process of generating the output includes the trading simulation. */ if (checkThereAreCandles(chart) === true) { - bot.sessionHeartBeat(undefined, undefined, 'Running') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Running') await generateOutput(chart) - bot.sessionHeartBeat(undefined, undefined, 'Saving') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Saving') await writeProcessFiles() - bot.sessionHeartBeat(undefined, undefined, 'Sleeping') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Sleeping') } else { - bot.sessionHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date') + bot.TRADING_SESSIONHeartBeat(undefined, undefined, 'Waiting for Data Mining to be up to date') callBackFunction(global.DEFAULT_RETRY_RESPONSE) return } @@ -219,7 +219,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) callBackFunction(global.DEFAULT_OK_RESPONSE) function checkThereAreCandles(chart) { - let sessionParameters = bot.SESSION.parameters + let sessionParameters = bot.TRADING_SESSION.parameters let propertyName = 'at' + sessionParameters.timeFrame.config.label.replace('-', '') let candles = chart[propertyName].candles @@ -236,7 +236,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) let statusReport; /* We are going to use the start date as beging of market date. */ - contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.SESSION.parameters.timeRange.config.initialDatetime) + contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime) /* Here we get the status report from the bot who knows which is the end of the market. */ @@ -371,7 +371,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) dataFiles = new Map(); - if (bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.marketFilesPeriods[n][0]; currentTimeFrameLabel = global.marketFilesPeriods[n][1]; } @@ -384,7 +384,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } @@ -431,8 +431,8 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) let dataRecord = dataFile[i] let begin = dataRecord[beginIndex] let end = dataRecord[endIndex] - if (end + timeFrame < bot.SESSION.parameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element - if (begin > bot.SESSION.parameters.timeRange.config.finalDatetime) { continue } + if (end + timeFrame < bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element + if (begin > bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime) { continue } result.push(dataRecord) } return result @@ -468,7 +468,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) } } - if (bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.dailyFilePeriods[n][0]; currentTimeFrameLabel = global.dailyFilePeriods[n][1]; } @@ -488,7 +488,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } @@ -653,7 +653,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) let fileContent = JSON.stringify(dataRange); let fileName = '/Data.Range.json'; - let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName; + let filePath = bot.filePathRoot + "/Output/" + bot.TRADING_SESSION.folderName + "/" + productCodeName + "/" + 'Multi-Period-Daily' + fileName; let response = await fileStorage.asyncCreateTextFile(filePath, fileContent + '\n') @@ -692,7 +692,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) let fileContent = JSON.stringify(timeFramesArray) let fileName = '/Time.Frames.json'; - let filePath = bot.filePathRoot + "/Output/" + bot.SESSION.folderName + "/" + productCodeName + "/" + processType + fileName; + let filePath = bot.filePathRoot + "/Output/" + bot.TRADING_SESSION.folderName + "/" + productCodeName + "/" + processType + fileName; let response = await fileStorage.asyncCreateTextFile(filePath, fileContent + '\n') if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) { @@ -728,12 +728,12 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) function checkIfSessionMustStop() { - if (bot.SESSION.type === 'Backtesting Session') { + if (bot.TRADING_SESSION.type === 'Backtesting Session') { /* Backtests needs only one execution of this process to complete. */ if (FULL_LOG === true) { logger.write(MODULE_NAME, '[IMPORTANT] checkIfSessionMustStop -> Backtesting Session Finished. Stopping the Session now. ') } - bot.SESSION.stop('Backtesting Session Finished.') + bot.TRADING_SESSION.stop('Backtesting Session Finished.') } } } diff --git a/Root.js b/Root.js index 10ac254d5..0511dccad 100644 --- a/Root.js +++ b/Root.js @@ -266,6 +266,10 @@ runTradingBot(botConfig, processConfig); break; } + case 'AI Bot': { + runAIBot(botConfig, processConfig); + break; + } default: { console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); } @@ -296,6 +300,10 @@ runTradingBot(botConfig, processConfig); break; } + case 'AI Bot': { + runAIBot(botConfig, processConfig); + break; + } default: { console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); } @@ -339,6 +347,10 @@ runTradingBot(botConfig, processConfig); break; } + case 'AI Bot': { + runAIBot(botConfig, processConfig); + break; + } default: { console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> Unexpected bot type. -> botConfig.type = " + botConfig.type); } @@ -540,6 +552,71 @@ setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } + + function runAIBot(pBotConfig, pProcessConfig) { + + global.TOTAL_PROCESS_INSTANCES_CREATED++ + + try { + const DEBUG_MODULE = require(global.ROOT_DIR + 'DebugLog'); + let logger; + + logger = DEBUG_MODULE.newDebugLog(); + global.LOGGER_MAP.set('runAIBot', logger) + logger.bot = pBotConfig; + + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runAIBot -> Entering function."); } + + let aiBot = TRADING_BOT_MODULE.newAIBot(pBotConfig, logger); + aiBot.initialize(pProcessConfig, onInitializeReady); + + function onInitializeReady(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + aiBot.run(whenRunFinishes); + + function whenRunFinishes(err) { + + pBotConfig.loopCounter = 0; + + let botId; + + botId = pBotConfig.dataMine + "." + pBotConfig.codeName + "." + pBotConfig.process; + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + + logger.persist(); + + } else { + + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.persist(); + } + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + + } else { + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> err = " + err.message); + logger.persist(); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } + } + catch (err) { + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runAIBot -> err = " + err.stack); + setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) + } + } } catch (err) { console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> err = " + err.stack); diff --git a/SocialBots.js b/SocialBots.js index 9ae190727..4676a912f 100644 --- a/SocialBots.js +++ b/SocialBots.js @@ -13,10 +13,10 @@ exports.newSocialBots = function newSocialBots(bot, logger) { return thisObject function initialize() { - if (bot.SESSION.socialBots !== undefined) { - if (bot.SESSION.socialBots.bots !== undefined) { - for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) { - let socialBot = bot.SESSION.socialBots.bots[i] + if (bot.TRADING_SESSION.socialBots !== undefined) { + if (bot.TRADING_SESSION.socialBots.bots !== undefined) { + for (let i = 0; i < bot.TRADING_SESSION.socialBots.bots.length; i++) { + let socialBot = bot.TRADING_SESSION.socialBots.bots[i] if (socialBot.type === "Telegram Bot") { let config = socialBot.config socialBot.botInstance = TELEGRAM_BOT_MODULE.newTelegramBot(bot, logger) @@ -26,9 +26,9 @@ exports.newSocialBots = function newSocialBots(bot, logger) { } function announce(text) { - if (bot.SESSION.socialBots.bots !== undefined) { - for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) { - let socialBot = bot.SESSION.socialBots.bots[i] + if (bot.TRADING_SESSION.socialBots.bots !== undefined) { + for (let i = 0; i < bot.TRADING_SESSION.socialBots.bots.length; i++) { + let socialBot = bot.TRADING_SESSION.socialBots.bots[i] try { if (socialBot.type === "Telegram Bot") { socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, text).catch(err => logger.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> Telegram API error -> err = " + err)) @@ -40,15 +40,15 @@ exports.newSocialBots = function newSocialBots(bot, logger) { } } - bot.SESSION.socialBots.announce = announce + bot.TRADING_SESSION.socialBots.announce = announce } } function finalize() { - if (bot.SESSION.socialBots === undefined) { return } - if (bot.SESSION.socialBots.bots !== undefined) { - for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) { - let socialBot = bot.SESSION.socialBots.bots[i] + if (bot.TRADING_SESSION.socialBots === undefined) { return } + if (bot.TRADING_SESSION.socialBots.bots !== undefined) { + for (let i = 0; i < bot.TRADING_SESSION.socialBots.bots.length; i++) { + let socialBot = bot.TRADING_SESSION.socialBots.bots[i] if (socialBot.type === "Telegram Bot") { socialBot.botInstance.finalize() } @@ -57,10 +57,10 @@ exports.newSocialBots = function newSocialBots(bot, logger) { } function sendMessage(message) { - if (bot.SESSION.socialBots !== undefined) { - if (bot.SESSION.socialBots.bots !== undefined) { - for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) { - let socialBot = bot.SESSION.socialBots.bots[i] + if (bot.TRADING_SESSION.socialBots !== undefined) { + if (bot.TRADING_SESSION.socialBots.bots !== undefined) { + for (let i = 0; i < bot.TRADING_SESSION.socialBots.bots.length; i++) { + let socialBot = bot.TRADING_SESSION.socialBots.bots[i] if (socialBot.type === "Telegram Bot") { socialBot.botInstance.sendMessage(message) } diff --git a/StatusReport.js b/StatusReport.js index 21089be6d..db34accaa 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -100,8 +100,12 @@ } } - if (bot.SESSION !== undefined && statusDependencyNode.bottype === "Trading Bot") { - sessionPath = bot.SESSION.folderName + "/" + if (bot.TRADING_SESSION !== undefined && statusDependencyNode.bottype === "Trading Bot") { + sessionPath = bot.TRADING_SESSION.folderName + "/" + } + + if (bot.AI_SESSION !== undefined && statusDependencyNode.bottype === "AI Bot") { + sessionPath = bot.AI_SESSION.folderName + "/" } /* Now we will see where do we need to fetch this status report from. */ @@ -264,7 +268,7 @@ On the contraty, if we are running a Sensor bot or an Indicator bot, we might, if necesary, use a previous version of a Status Report since there will be no big impact, just some reprocessing. */ - if (bot.SESSION !== undefined) { + if (bot.TRADING_SESSION !== undefined || bot.AI_SESSION !== undefined) { canUserPrevious = false } else { canUserPrevious = true diff --git a/TelegramBot.js b/TelegramBot.js index 9e2383ed1..79b7c5212 100644 --- a/TelegramBot.js +++ b/TelegramBot.js @@ -23,7 +23,7 @@ exports.newTelegramBot = function newTelegramBot(bot, logger) { thisObject.telegramBot = new Telegraf(botToken) thisObject.telegramBot.start((ctx) => ctx.reply('Hi! I am the Telegram Bot that will tell you what happens with your trading session.')) thisObject.telegramBot.help((ctx) => ctx.reply('I can stop the session if you type STOP.')) - thisObject.telegramBot.hears('STOP', (ctx) => bot.SESSION.stop(' by STOP command at Telegram.')) + thisObject.telegramBot.hears('STOP', (ctx) => bot.TRADING_SESSION.stop(' by STOP command at Telegram.')) thisObject.telegramBot.launch() const Telegram = require('telegraf/telegram') diff --git a/TradingBot.js b/TradingBot.js index 4c98553e1..deccc008b 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -93,15 +93,15 @@ + " Main Loop # " + pad(Number(bot.loopCounter), 8) + " " + bot.processNode.session.type + " " + bot.processNode.session.name) /* Checking if we need to need to emit any event */ - if (bot.SESSION_STATUS === 'Idle' && bot.STOP_SESSION === false) { - bot.SESSION_STATUS = 'Running' + if (bot.TRADING_SESSION_STATUS === 'Idle' && bot.STOP_SESSION === false) { + bot.TRADING_SESSION_STATUS = 'Running' } - if (bot.SESSION_STATUS === 'Running' && bot.STOP_SESSION === true) { - bot.SESSION_STATUS = 'Stopped' + if (bot.TRADING_SESSION_STATUS === 'Running' && bot.STOP_SESSION === true) { + bot.TRADING_SESSION_STATUS = 'Stopped' } - global.EMIT_SESSION_STATUS (bot.SESSION_STATUS, bot.sessionKey) + global.EMIT_SESSION_STATUS (bot.TRADING_SESSION_STATUS, bot.TRADING_SESSIONKey) /* Checking if we should process this loop or not.*/ if (bot.STOP_SESSION === true) { @@ -534,7 +534,7 @@ logger.persist(); } - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Stopped') + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Stopped') processStopped() return; } @@ -560,17 +560,17 @@ if (processConfig.waitsForExecutionFinishedEvent === true) { waitTime = 0 } else { - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Live Trading Session': { - waitTime = bot.SESSION.parameters.timeFrame.config.value + waitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value break } case 'Fordward Tessting Session': { - waitTime = bot.SESSION.parameters.timeFrame.config.value + waitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value break } case 'Paper Trading Session': { - waitTime = bot.SESSION.parameters.timeFrame.config.value + waitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value break } case 'Backtesting Session': { @@ -669,9 +669,9 @@ } function sessionStopped() { - if (bot.SESSION_STATUS === 'Running') { - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Stopped') - bot.SESSION_STATUS = 'Stopped' + if (bot.TRADING_SESSION_STATUS === 'Running') { + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Stopped') + bot.TRADING_SESSION_STATUS = 'Stopped' } } diff --git a/TradingSession.js b/TradingSession.js index 0bd371928..25f2907b4 100644 --- a/TradingSession.js +++ b/TradingSession.js @@ -15,16 +15,16 @@ function initialize(pProcessConfig, callBackFunction) { try { /* Initialize this info so that everything is logged propeerly */ - bot.SESSION = { + bot.TRADING_SESSION = { name: bot.processNode.session.name, id: bot.processNode.session.id } /* Set the folderName for early logging */ if (bot.processNode.session.config.folderName === undefined) { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id + bot.TRADING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.TRADING_SESSION.id } else { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + bot.TRADING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName } /* Check if there is a session */ @@ -35,43 +35,43 @@ } /* Listen to event to start or stop the session. */ - bot.sessionKey = bot.processNode.session.name + '-' + bot.processNode.session.type + '-' + bot.processNode.session.id - global.SESSION_MAP.set(bot.sessionKey, bot.sessionKey) + bot.TRADING_SESSIONKey = bot.processNode.session.name + '-' + bot.processNode.session.type + '-' + bot.processNode.session.id + global.SESSION_MAP.set(bot.TRADING_SESSIONKey, bot.TRADING_SESSIONKey) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Session Status', undefined, bot.sessionKey, undefined, onSessionStatus) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Run Session', undefined, bot.sessionKey, undefined, onSessionRun) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Stop Session', undefined, bot.sessionKey, undefined, onSessionStop) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Resume Session', undefined, bot.sessionKey, undefined, onSessionResume) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Session Status', undefined, bot.TRADING_SESSIONKey, undefined, onSessionStatus) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Run Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionRun) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Stop Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionStop) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Resume Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionResume) /* Connect this here so that it is accesible from other places */ - bot.sessionError = sessionError - bot.sessionWarning = sessionWarning - bot.sessionInfo = sessionInfo + bot.TRADING_SESSIONError = sessionError + bot.TRADING_SESSIONWarning = sessionWarning + bot.TRADING_SESSIONInfo = sessionInfo /* Heartbeats sent to the UI */ - bot.sessionHeartBeat = sessionHeartBeat + bot.TRADING_SESSIONHeartBeat = sessionHeartBeat callBackFunction(global.DEFAULT_OK_RESPONSE) return function onSessionStatus() { - if (bot.SESSION_STATUS === 'Running') { + if (bot.TRADING_SESSION_STATUS === 'Running') { let event = { status: 'Session Runnning' } - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Status Response', event) + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Status Response', event) } else { let event = { status: 'Not Session Runnning' } - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Status Response', event) + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Status Response', event) } } function onSessionRun(message) { try { /* This happens when the UI is reloaded, the session was running and tries to run it again. */ - if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { + if (bot.TRADING_SESSION_STATUS === 'Idle' || bot.TRADING_SESSION_STATUS === 'Running') { parentLogger.write(MODULE_NAME, "[WARN] onSessionRun -> Event received to run the Session while it was already running. ") return } @@ -79,11 +79,11 @@ /* We are going to run the Definition comming at the event. */ bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem) bot.TRADING_ENGINE = JSON.parse(message.event.tradingEngine) - bot.SESSION = JSON.parse(message.event.session) + bot.TRADING_SESSION = JSON.parse(message.event.session) bot.DEPENDENCY_FILTER = JSON.parse(message.event.dependencyFilter) bot.RESUME = false bot.FIRST_EXECUTION = true - bot.SESSION.stop = stopSession // stop function + bot.TRADING_SESSION.stop = stopSession // stop function setUpSessionFolderName() @@ -93,7 +93,7 @@ socialBotsModule.initialize() let allGood - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Backtesting Session': { allGood = startBackTesting(message) break @@ -112,14 +112,14 @@ } } if (allGood === true) { - bot.SESSION_STATUS = 'Idle' + bot.TRADING_SESSION_STATUS = 'Idle' bot.STOP_SESSION = false } else { bot.STOP_SESSION = true if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] onSessionRun -> Stopping the Session now. ') } } - socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is starting.") + socialBotsModule.sendMessage(bot.TRADING_SESSION.type + " '" + bot.TRADING_SESSION.name + "' is starting.") } catch (err) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionRun -> err = " + err.stack); } @@ -131,13 +131,13 @@ function onSessionResume(message) { try { - if (bot.SESSION.stop === undefined) { + if (bot.TRADING_SESSION.stop === undefined) { parentLogger.write(MODULE_NAME, "[WARN] onSessionResume -> Event received to resume the Session that have never be ran before. ") return } /* This happens when the UI is reloaded, the session was running and tries to run it again. */ - if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { + if (bot.TRADING_SESSION_STATUS === 'Idle' || bot.TRADING_SESSION_STATUS === 'Running') { parentLogger.write(MODULE_NAME, "[WARN] onSessionResume -> Event received to resume the Session while it was already running. ") return } @@ -145,7 +145,7 @@ bot.RESUME = true bot.STOP_SESSION = false - socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is resuming.") + socialBotsModule.sendMessage(bot.TRADING_SESSION.type + " '" + bot.TRADING_SESSION.name + "' is resuming.") } catch (err) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionResume -> err = " + err.stack); } @@ -153,11 +153,11 @@ function stopSession(commandOrigin) { - socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is stopping " + commandOrigin) + socialBotsModule.sendMessage(bot.TRADING_SESSION.type + " '" + bot.TRADING_SESSION.name + "' is stopping " + commandOrigin) socialBotsModule.finalize() bot.STOP_SESSION = true if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] stopSession -> Stopping the Session now. ') } - sessionInfo(bot.SESSION, commandOrigin) + sessionInfo(bot.TRADING_SESSION, commandOrigin) } function setUpSessionFolderName() { @@ -167,12 +167,12 @@ Set the folderName for logging, reports, context and data output */ let config - if (bot.SESSION.config !== undefined) { - config = bot.SESSION.config + if (bot.TRADING_SESSION.config !== undefined) { + config = bot.TRADING_SESSION.config if (config.folderName === undefined) { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id + bot.TRADING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.TRADING_SESSION.id } else { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + bot.TRADING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName } } } @@ -183,16 +183,16 @@ non critical is missing, we complete it with a default value. */ - if (bot.SESSION.parameters === undefined) { + if (bot.TRADING_SESSION.parameters === undefined) { let errorMessage = "Session Node with no Parameters." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION, errorMessage) return false } /* Time Range */ - if (bot.SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. - bot.SESSION.parameters.timeRange = { + if (bot.TRADING_SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. + bot.TRADING_SESSION.parameters.timeRange = { name: 'Missing Time Range', type: 'Time Range', config: { @@ -202,18 +202,18 @@ } } else { /* Check that we received valid dates */ - if (bot.SESSION.type === 'Backtesting Session') { - if (isNaN(new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { + if (bot.TRADING_SESSION.type === 'Backtesting Session') { + if (isNaN(new Date(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) return false } } - if (isNaN(new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { + if (isNaN(new Date(bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) return false } } @@ -222,7 +222,7 @@ let today = (new Date()).valueOf() let aYearAgo = today - global.ONE_YEAR_IN_MILISECONDS let aYearFromNow = today + global.ONE_YEAR_IN_MILISECONDS - switch (bot.SESSION.type) { + switch (bot.TRADING_SESSION.type) { case 'Backtesting Session': { useDefaultDatetimes(aYearAgo, today) break @@ -248,81 +248,81 @@ */ /* Initial Datetime */ - if (bot.SESSION.type === 'Backtesting Session') { - if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined) { - bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.TRADING_SESSION.type === 'Backtesting Session') { + if (bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime === undefined) { + bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault } else { - bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() } } else { /* Non backtest session can start from the past only if explicitly configured that way */ - if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { - bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.TRADING_SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { + bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault } else { - bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() } } /* Final Datetime */ - if (bot.SESSION.parameters.timeRange.config.finalDatetime === undefined) { - bot.SESSION.parameters.timeRange.config.finalDatetime = finalDefault + if (bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime === undefined) { + bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime = finalDefault } else { - bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf() + bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf() } } /* Time Frame */ - if (bot.SESSION.parameters.timeFrame === undefined) { + if (bot.TRADING_SESSION.parameters.timeFrame === undefined) { let errorMessage = "Session Parameters Node with no Time Frame." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) return false } - if (bot.SESSION.parameters.timeFrame.config.label === undefined) { + if (bot.TRADING_SESSION.parameters.timeFrame.config.label === undefined) { let errorMessage = "Session Parameters Node with no Time Frame Label configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.timeFrame, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.timeFrame, errorMessage) return false } - bot.SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.SESSION.parameters.timeFrame.config.label) - if (bot.SESSION.parameters.timeFrame.config.value === undefined) { + bot.TRADING_SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.TRADING_SESSION.parameters.timeFrame.config.label) + if (bot.TRADING_SESSION.parameters.timeFrame.config.value === undefined) { let errorMessage = "Config error: label value not recognized. Try 01-min or 01-hs for example." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.timeFrame, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.timeFrame, errorMessage) return false } /* Session Base Asset */ - if (bot.SESSION.parameters.sessionBaseAsset === undefined) { + if (bot.TRADING_SESSION.parameters.sessionBaseAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Base Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) return false } - if (bot.SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { + if (bot.TRADING_SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Base Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.sessionBaseAsset, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.sessionBaseAsset, errorMessage) return false } /* Session Quoted Asset */ - if (bot.SESSION.parameters.sessionQuotedAsset === undefined) { + if (bot.TRADING_SESSION.parameters.sessionQuotedAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Quoted Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) return false } - if (bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { + if (bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Quoted Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.sessionQuotedAsset, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.sessionQuotedAsset, errorMessage) return false } /* Slippage */ - if (bot.SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. - bot.SESSION.parameters.slippage = { + if (bot.TRADING_SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. + bot.TRADING_SESSION.parameters.slippage = { name: 'Missing Slippage', type: 'Slippage', config: { @@ -332,19 +332,19 @@ } } } - if (bot.SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. - bot.SESSION.parameters.slippage.config.marketOrderRate = 0 + if (bot.TRADING_SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. + bot.TRADING_SESSION.parameters.slippage.config.marketOrderRate = 0 } - if (bot.SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. - bot.SESSION.parameters.slippage.config.stopLoss = 0 + if (bot.TRADING_SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. + bot.TRADING_SESSION.parameters.slippage.config.stopLoss = 0 } - if (bot.SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. - bot.SESSION.parameters.slippage.config.takeProfit = 0 + if (bot.TRADING_SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. + bot.TRADING_SESSION.parameters.slippage.config.takeProfit = 0 } /* Fee Structure */ - if (bot.SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. - bot.SESSION.parameters.feeStructure = { + if (bot.TRADING_SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. + bot.TRADING_SESSION.parameters.feeStructure = { name: 'Missing Fee Structure', type: 'Fee Structure', config: { @@ -353,11 +353,11 @@ } } } - if (bot.SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. - bot.SESSION.parameters.feeStructure.config.maker = 0 + if (bot.TRADING_SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. + bot.TRADING_SESSION.parameters.feeStructure.config.maker = 0 } - if (bot.SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. - bot.SESSION.parameters.feeStructure.config.taker = 0 + if (bot.TRADING_SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. + bot.TRADING_SESSION.parameters.feeStructure.config.taker = 0 } return true @@ -371,7 +371,7 @@ if (bot.KEY === undefined || bot.SECRET === undefined) { let errorMessage = "Key 'codeName' or 'secret' not provided. Plese check that and try again." if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> " + errorMessage); } - bot.sessionError(bot.SESSION, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION, errorMessage) return false } return true @@ -387,20 +387,20 @@ /* Reduce the balance */ let balancePercentage = 1 // This is the default value - if (bot.SESSION.config.balancePercentage !== undefined) { - balancePercentage = bot.SESSION.config.balancePercentage + if (bot.TRADING_SESSION.config.balancePercentage !== undefined) { + balancePercentage = bot.TRADING_SESSION.config.balancePercentage } - bot.SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 + bot.TRADING_SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.TRADING_SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 + bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 + bot.TRADING_SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.TRADING_SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 + bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 + bot.TRADING_SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.TRADING_SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 + bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 - pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value + pProcessConfig.normalWaitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value return true } @@ -437,7 +437,7 @@ percentage: percentage, status: status } - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Heartbeat', event) + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Heartbeat', event) if (global.STOP_TASK_GRACEFULLY === true) { bot.STOP_SESSION = true @@ -459,7 +459,7 @@ errorMessage: errorMessage } } - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Error', event) + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Error', event) if (global.STOP_TASK_GRACEFULLY === true) { bot.STOP_SESSION = true @@ -481,7 +481,7 @@ warningMessage: warningMessage } } - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Warning', event) + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Warning', event) if (global.STOP_TASK_GRACEFULLY === true) { bot.STOP_SESSION = true @@ -503,7 +503,7 @@ infoMessage: infoMessage } } - global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Info', event) + global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Info', event) if (global.STOP_TASK_GRACEFULLY === true) { bot.STOP_SESSION = true diff --git a/TrainingSession.js b/TrainingSession.js index e7d78f6c0..cb5a15256 100644 --- a/TrainingSession.js +++ b/TrainingSession.js @@ -15,16 +15,16 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { function initialize(pProcessConfig, callBackFunction) { try { /* Initialize this info so that everything is logged propeerly */ - bot.SESSION = { + bot.AI_SESSION = { name: bot.processNode.session.name, id: bot.processNode.session.id } /* Set the folderName for early logging */ if (bot.processNode.session.config.folderName === undefined) { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id + bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.AI_SESSION.id } else { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName } /* Check if there is a session */ @@ -79,11 +79,11 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { /* We are going to run the Definition comming at the event. */ bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem) bot.TRADING_ENGINE = JSON.parse(message.event.tradingEngine) - bot.SESSION = JSON.parse(message.event.session) + bot.AI_SESSION = JSON.parse(message.event.session) bot.DEPENDENCY_FILTER = JSON.parse(message.event.dependencyFilter) bot.RESUME = false bot.FIRST_EXECUTION = true - bot.SESSION.stop = stopSession // stop function + bot.AI_SESSION.stop = stopSession // stop function setUpSessionFolderName() @@ -93,7 +93,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { socialBotsModule.initialize() let allGood - switch (bot.SESSION.type) { + switch (bot.AI_SESSION.type) { case 'Backtesting Session': { allGood = startBackTesting(message) break @@ -119,7 +119,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] onSessionRun -> Stopping the Session now. ') } } - socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is starting.") + socialBotsModule.sendMessage(bot.AI_SESSION.type + " '" + bot.AI_SESSION.name + "' is starting.") } catch (err) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionRun -> err = " + err.stack); } @@ -131,7 +131,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { function onSessionResume(message) { try { - if (bot.SESSION.stop === undefined) { + if (bot.AI_SESSION.stop === undefined) { parentLogger.write(MODULE_NAME, "[WARN] onSessionResume -> Event received to resume the Session that have never be ran before. ") return } @@ -145,7 +145,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { bot.RESUME = true bot.STOP_SESSION = false - socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is resuming.") + socialBotsModule.sendMessage(bot.AI_SESSION.type + " '" + bot.AI_SESSION.name + "' is resuming.") } catch (err) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionResume -> err = " + err.stack); } @@ -153,11 +153,11 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { function stopSession(commandOrigin) { - socialBotsModule.sendMessage(bot.SESSION.type + " '" + bot.SESSION.name + "' is stopping " + commandOrigin) + socialBotsModule.sendMessage(bot.AI_SESSION.type + " '" + bot.AI_SESSION.name + "' is stopping " + commandOrigin) socialBotsModule.finalize() bot.STOP_SESSION = true if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] stopSession -> Stopping the Session now. ') } - sessionInfo(bot.SESSION, commandOrigin) + sessionInfo(bot.AI_SESSION, commandOrigin) } function setUpSessionFolderName() { @@ -167,12 +167,12 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { Set the folderName for logging, reports, context and data output */ let config - if (bot.SESSION.config !== undefined) { - config = bot.SESSION.config + if (bot.AI_SESSION.config !== undefined) { + config = bot.AI_SESSION.config if (config.folderName === undefined) { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id + bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.AI_SESSION.id } else { - bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName } } } @@ -183,16 +183,16 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { non critical is missing, we complete it with a default value. */ - if (bot.SESSION.parameters === undefined) { + if (bot.AI_SESSION.parameters === undefined) { let errorMessage = "Session Node with no Parameters." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION, errorMessage) + bot.sessionError(bot.AI_SESSION, errorMessage) return false } /* Time Range */ - if (bot.SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. - bot.SESSION.parameters.timeRange = { + if (bot.AI_SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. + bot.AI_SESSION.parameters.timeRange = { name: 'Missing Time Range', type: 'Time Range', config: { @@ -202,18 +202,18 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { } } else { /* Check that we received valid dates */ - if (bot.SESSION.type === 'Backtesting Session') { - if (isNaN(new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { + if (bot.AI_SESSION.type === 'Backtesting Session') { + if (isNaN(new Date(bot.AI_SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters, errorMessage) return false } } - if (isNaN(new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { + if (isNaN(new Date(bot.AI_SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters, errorMessage) return false } } @@ -222,7 +222,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { let today = (new Date()).valueOf() let aYearAgo = today - global.ONE_YEAR_IN_MILISECONDS let aYearFromNow = today + global.ONE_YEAR_IN_MILISECONDS - switch (bot.SESSION.type) { + switch (bot.AI_SESSION.type) { case 'Backtesting Session': { useDefaultDatetimes(aYearAgo, today) break @@ -248,81 +248,81 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { */ /* Initial Datetime */ - if (bot.SESSION.type === 'Backtesting Session') { - if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined) { - bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.AI_SESSION.type === 'Backtesting Session') { + if (bot.AI_SESSION.parameters.timeRange.config.initialDatetime === undefined) { + bot.AI_SESSION.parameters.timeRange.config.initialDatetime = initialDefault } else { - bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.AI_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.AI_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() } } else { /* Non backtest session can start from the past only if explicitly configured that way */ - if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { - bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.AI_SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.AI_SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { + bot.AI_SESSION.parameters.timeRange.config.initialDatetime = initialDefault } else { - bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.AI_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.AI_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() } } /* Final Datetime */ - if (bot.SESSION.parameters.timeRange.config.finalDatetime === undefined) { - bot.SESSION.parameters.timeRange.config.finalDatetime = finalDefault + if (bot.AI_SESSION.parameters.timeRange.config.finalDatetime === undefined) { + bot.AI_SESSION.parameters.timeRange.config.finalDatetime = finalDefault } else { - bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf() + bot.AI_SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.AI_SESSION.parameters.timeRange.config.finalDatetime)).valueOf() } } /* Time Frame */ - if (bot.SESSION.parameters.timeFrame === undefined) { + if (bot.AI_SESSION.parameters.timeFrame === undefined) { let errorMessage = "Session Parameters Node with no Time Frame." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters, errorMessage) return false } - if (bot.SESSION.parameters.timeFrame.config.label === undefined) { + if (bot.AI_SESSION.parameters.timeFrame.config.label === undefined) { let errorMessage = "Session Parameters Node with no Time Frame Label configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.timeFrame, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters.timeFrame, errorMessage) return false } - bot.SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.SESSION.parameters.timeFrame.config.label) - if (bot.SESSION.parameters.timeFrame.config.value === undefined) { + bot.AI_SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.AI_SESSION.parameters.timeFrame.config.label) + if (bot.AI_SESSION.parameters.timeFrame.config.value === undefined) { let errorMessage = "Config error: label value not recognized. Try 01-min or 01-hs for example." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.timeFrame, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters.timeFrame, errorMessage) return false } /* Session Base Asset */ - if (bot.SESSION.parameters.sessionBaseAsset === undefined) { + if (bot.AI_SESSION.parameters.sessionBaseAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Base Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters, errorMessage) return false } - if (bot.SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { + if (bot.AI_SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Base Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.sessionBaseAsset, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters.sessionBaseAsset, errorMessage) return false } /* Session Quoted Asset */ - if (bot.SESSION.parameters.sessionQuotedAsset === undefined) { + if (bot.AI_SESSION.parameters.sessionQuotedAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Quoted Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters, errorMessage) return false } - if (bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { + if (bot.AI_SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Quoted Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.SESSION.parameters.sessionQuotedAsset, errorMessage) + bot.sessionError(bot.AI_SESSION.parameters.sessionQuotedAsset, errorMessage) return false } /* Slippage */ - if (bot.SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. - bot.SESSION.parameters.slippage = { + if (bot.AI_SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. + bot.AI_SESSION.parameters.slippage = { name: 'Missing Slippage', type: 'Slippage', config: { @@ -332,19 +332,19 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { } } } - if (bot.SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. - bot.SESSION.parameters.slippage.config.marketOrderRate = 0 + if (bot.AI_SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. + bot.AI_SESSION.parameters.slippage.config.marketOrderRate = 0 } - if (bot.SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. - bot.SESSION.parameters.slippage.config.stopLoss = 0 + if (bot.AI_SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. + bot.AI_SESSION.parameters.slippage.config.stopLoss = 0 } - if (bot.SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. - bot.SESSION.parameters.slippage.config.takeProfit = 0 + if (bot.AI_SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. + bot.AI_SESSION.parameters.slippage.config.takeProfit = 0 } /* Fee Structure */ - if (bot.SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. - bot.SESSION.parameters.feeStructure = { + if (bot.AI_SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. + bot.AI_SESSION.parameters.feeStructure = { name: 'Missing Fee Structure', type: 'Fee Structure', config: { @@ -353,11 +353,11 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { } } } - if (bot.SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. - bot.SESSION.parameters.feeStructure.config.maker = 0 + if (bot.AI_SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. + bot.AI_SESSION.parameters.feeStructure.config.maker = 0 } - if (bot.SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. - bot.SESSION.parameters.feeStructure.config.taker = 0 + if (bot.AI_SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. + bot.AI_SESSION.parameters.feeStructure.config.taker = 0 } return true @@ -371,7 +371,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { if (bot.KEY === undefined || bot.SECRET === undefined) { let errorMessage = "Key 'codeName' or 'secret' not provided. Plese check that and try again." if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> " + errorMessage); } - bot.sessionError(bot.SESSION, errorMessage) + bot.sessionError(bot.AI_SESSION, errorMessage) return false } return true @@ -387,20 +387,20 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { /* Reduce the balance */ let balancePercentage = 1 // This is the default value - if (bot.SESSION.config.balancePercentage !== undefined) { - balancePercentage = bot.SESSION.config.balancePercentage + if (bot.AI_SESSION.config.balancePercentage !== undefined) { + balancePercentage = bot.AI_SESSION.config.balancePercentage } - bot.SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 + bot.AI_SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.AI_SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 + bot.AI_SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.AI_SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 + bot.AI_SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.AI_SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 + bot.AI_SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.AI_SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 - bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 + bot.AI_SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.AI_SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 + bot.AI_SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.AI_SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 - pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value + pProcessConfig.normalWaitTime = bot.AI_SESSION.parameters.timeFrame.config.value return true } diff --git a/server.js b/server.js index 1fd442a8b..e693dcad8 100644 --- a/server.js +++ b/server.js @@ -30,8 +30,8 @@ global.ROOT_DIR = './'; global.ONE_YEAR_IN_MILISECONDS = 365 * 24 * 60 * 60 * 1000 global.ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000 global.ONE_MIN_IN_MILISECONDS = 60 * 1000 -global.LOGGER_MAP = new Map() -global.SESSION_MAP = new Map() +global.LOGGER_MAP = new Map() // We will put all the loggers in a map, so that we can eventually finalize them. +global.SESSION_MAP = new Map() // We will put all the sessions in a map, so that we can eventually finalize them. global.UNIQUE_ID = function () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { From 4ebdb86a6f82d7083a157009dbeb9426afb2b69b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 3 Nov 2020 15:08:45 +0100 Subject: [PATCH 35/41] Intel Mine code implementation --- Commons.js | 15 ++++++++++++--- DataDependencies.js | 4 ++-- ProcessOutput.js | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Commons.js b/Commons.js index eff4397be..19c917f82 100644 --- a/Commons.js +++ b/Commons.js @@ -118,9 +118,18 @@ if (dataMineNode === undefined) { let tradingMineNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Trading Mine') if (tradingMineNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Data Mine or a Trading Mine."); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return + let intelMineNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Intel Mine') + if (intelMineNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Data Mine, Trading Mine or Intel Mine."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } else { + if (tradingMineNode.config.codeName === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Intel Mine witn no codeName defined."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return + } + } } else { if (tradingMineNode.config.codeName === undefined) { logger.write(MODULE_NAME, "[ERROR] start -> Trading Mine witn no codeName defined."); diff --git a/DataDependencies.js b/DataDependencies.js index 54eb5916d..361fc3307 100644 --- a/DataDependencies.js +++ b/DataDependencies.js @@ -36,7 +36,7 @@ thisObject.nodeArray = global.FILTER_OUT_NODES_WITHOUT_REFERENCE_PARENT_FROM_NODE_ARRAY(thisObject.nodeArray) } else { - logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible to not have process dependencies, which means not data dependencies."); + logger.write(MODULE_NAME, "[ERROR] initialize -> onInitilized -> It is not possible not to have process dependencies, which means not data dependencies."); callBackFunction(global.DEFAULT_OK_RESPONSE) return } @@ -65,7 +65,7 @@ let skipCount = 0 /* - The current nodeArray that we have includes all the dependencies daclared in the Data Mine / Trading Mine + The current nodeArray that we have includes all the dependencies daclared in the Data Mine | Trading Mine | Intel Mine process dependencies, minus the ones without reference parent, meaning that references Data Mines that are not present at the workspace. From all the remaining dependencies there will be others that we need to filter out, and they are the ones which the user does not have a data product anywhere on the network where diff --git a/ProcessOutput.js b/ProcessOutput.js index 8af65ef2a..48d008c7d 100644 --- a/ProcessOutput.js +++ b/ProcessOutput.js @@ -144,11 +144,11 @@ } if (botNode.parentNode !== undefined) { if (botNode.parentNode.config.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Data Mine not defined. Data Mine = " + JSON.stringify(botNode.parentNode)); + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Mine not defined. Mine = " + JSON.stringify(botNode.parentNode)); throw (global.DEFAULT_FAIL_RESPONSE); } } else { - logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Data Mine or Trading Mine. Bot = " + JSON.stringify(botNode)); + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Data Mine | Trading Mine | Intel Mine. Bot = " + JSON.stringify(botNode)); throw (global.DEFAULT_FAIL_RESPONSE); } } else { From 38244f4fc11a1ad1c46c27aa17cd1991128bf36c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 3 Nov 2020 15:55:05 +0100 Subject: [PATCH 36/41] Refactoring out of Intel and AI --- Commons.js | 10 +- DataDependencies.js | 2 +- DebugLog.js | 4 +- TrainingSession.js => LearningSession.js | 152 +++++++++--------- ProcessOutput.js | 6 +- .../{AiBot.js => LearningBot.js} | 14 +- ...{TrainingProcess.js => LearningProcess.js} | 20 +-- .../MarkovDecisionProcess.js | 2 +- Root.js | 46 +++--- StatusReport.js | 6 +- 10 files changed, 131 insertions(+), 131 deletions(-) rename TrainingSession.js => LearningSession.js (69%) rename ReinforcementLearning/{AiBot.js => LearningBot.js} (98%) rename ReinforcementLearning/{TrainingProcess.js => LearningProcess.js} (97%) diff --git a/Commons.js b/Commons.js index 19c917f82..7e32f50cb 100644 --- a/Commons.js +++ b/Commons.js @@ -100,7 +100,7 @@ botNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Trading Bot') } if (botNode === undefined) { - botNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'AI Bot') + botNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Learning Bot') } if (botNode === undefined) { logger.write(MODULE_NAME, "[ERROR] start -> Product Definition not attached to a Bot. Product Definition = "); @@ -118,14 +118,14 @@ if (dataMineNode === undefined) { let tradingMineNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Trading Mine') if (tradingMineNode === undefined) { - let intelMineNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Intel Mine') - if (intelMineNode === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Data Mine, Trading Mine or Intel Mine."); + let learningMineNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Learning Mine') + if (learningMineNode === undefined) { + logger.write(MODULE_NAME, "[ERROR] start -> Bot not attached to a Data Mine, Trading Mine or Learning Mine."); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } else { if (tradingMineNode.config.codeName === undefined) { - logger.write(MODULE_NAME, "[ERROR] start -> Intel Mine witn no codeName defined."); + logger.write(MODULE_NAME, "[ERROR] start -> Learning Mine witn no codeName defined."); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } diff --git a/DataDependencies.js b/DataDependencies.js index 361fc3307..bc6cbf79e 100644 --- a/DataDependencies.js +++ b/DataDependencies.js @@ -65,7 +65,7 @@ let skipCount = 0 /* - The current nodeArray that we have includes all the dependencies daclared in the Data Mine | Trading Mine | Intel Mine + The current nodeArray that we have includes all the dependencies daclared in the Data Mine | Trading Mine | Learning Mine process dependencies, minus the ones without reference parent, meaning that references Data Mines that are not present at the workspace. From all the remaining dependencies there will be others that we need to filter out, and they are the ones which the user does not have a data product anywhere on the network where diff --git a/DebugLog.js b/DebugLog.js index 3b6f25570..91b318fe7 100644 --- a/DebugLog.js +++ b/DebugLog.js @@ -83,8 +83,8 @@ exports.newDebugLog = function newDebugLog() { if (thisObject.bot.TRADING_SESSION !== undefined) { filePath = filePath + thisObject.bot.TRADING_SESSION.folderName + "/" + executionDatetime; } else { - if (thisObject.bot.AI_SESSION !== undefined) { - filePath = filePath + thisObject.bot.AI_SESSION.folderName + "/" + executionDatetime; + if (thisObject.bot.LEARNING_SESSION !== undefined) { + filePath = filePath + thisObject.bot.LEARNING_SESSION.folderName + "/" + executionDatetime; } else { filePath = filePath + executionDatetime; } diff --git a/TrainingSession.js b/LearningSession.js similarity index 69% rename from TrainingSession.js rename to LearningSession.js index cb5a15256..6b6a5f438 100644 --- a/TrainingSession.js +++ b/LearningSession.js @@ -1,6 +1,6 @@ -exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { +exports.newLearningSession = function newLearningSession(bot, parentLogger) { - const MODULE_NAME = "Training Session" + const MODULE_NAME = "Learning Session" const FULL_LOG = true; let thisObject = { @@ -15,16 +15,16 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { function initialize(pProcessConfig, callBackFunction) { try { /* Initialize this info so that everything is logged propeerly */ - bot.AI_SESSION = { + bot.LEARNING_SESSION = { name: bot.processNode.session.name, id: bot.processNode.session.id } /* Set the folderName for early logging */ if (bot.processNode.session.config.folderName === undefined) { - bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.AI_SESSION.id + bot.LEARNING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.LEARNING_SESSION.id } else { - bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + bot.LEARNING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName } /* Check if there is a session */ @@ -79,11 +79,11 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { /* We are going to run the Definition comming at the event. */ bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem) bot.TRADING_ENGINE = JSON.parse(message.event.tradingEngine) - bot.AI_SESSION = JSON.parse(message.event.session) + bot.LEARNING_SESSION = JSON.parse(message.event.session) bot.DEPENDENCY_FILTER = JSON.parse(message.event.dependencyFilter) bot.RESUME = false bot.FIRST_EXECUTION = true - bot.AI_SESSION.stop = stopSession // stop function + bot.LEARNING_SESSION.stop = stopSession // stop function setUpSessionFolderName() @@ -93,7 +93,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { socialBotsModule.initialize() let allGood - switch (bot.AI_SESSION.type) { + switch (bot.LEARNING_SESSION.type) { case 'Backtesting Session': { allGood = startBackTesting(message) break @@ -119,7 +119,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] onSessionRun -> Stopping the Session now. ') } } - socialBotsModule.sendMessage(bot.AI_SESSION.type + " '" + bot.AI_SESSION.name + "' is starting.") + socialBotsModule.sendMessage(bot.LEARNING_SESSION.type + " '" + bot.LEARNING_SESSION.name + "' is starting.") } catch (err) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionRun -> err = " + err.stack); } @@ -131,7 +131,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { function onSessionResume(message) { try { - if (bot.AI_SESSION.stop === undefined) { + if (bot.LEARNING_SESSION.stop === undefined) { parentLogger.write(MODULE_NAME, "[WARN] onSessionResume -> Event received to resume the Session that have never be ran before. ") return } @@ -145,7 +145,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { bot.RESUME = true bot.STOP_SESSION = false - socialBotsModule.sendMessage(bot.AI_SESSION.type + " '" + bot.AI_SESSION.name + "' is resuming.") + socialBotsModule.sendMessage(bot.LEARNING_SESSION.type + " '" + bot.LEARNING_SESSION.name + "' is resuming.") } catch (err) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> onSessionResume -> err = " + err.stack); } @@ -153,11 +153,11 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { function stopSession(commandOrigin) { - socialBotsModule.sendMessage(bot.AI_SESSION.type + " '" + bot.AI_SESSION.name + "' is stopping " + commandOrigin) + socialBotsModule.sendMessage(bot.LEARNING_SESSION.type + " '" + bot.LEARNING_SESSION.name + "' is stopping " + commandOrigin) socialBotsModule.finalize() bot.STOP_SESSION = true if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, '[IMPORTANT] stopSession -> Stopping the Session now. ') } - sessionInfo(bot.AI_SESSION, commandOrigin) + sessionInfo(bot.LEARNING_SESSION, commandOrigin) } function setUpSessionFolderName() { @@ -167,12 +167,12 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { Set the folderName for logging, reports, context and data output */ let config - if (bot.AI_SESSION.config !== undefined) { - config = bot.AI_SESSION.config + if (bot.LEARNING_SESSION.config !== undefined) { + config = bot.LEARNING_SESSION.config if (config.folderName === undefined) { - bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.AI_SESSION.id + bot.LEARNING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.LEARNING_SESSION.id } else { - bot.AI_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName + bot.LEARNING_SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName } } } @@ -183,16 +183,16 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { non critical is missing, we complete it with a default value. */ - if (bot.AI_SESSION.parameters === undefined) { + if (bot.LEARNING_SESSION.parameters === undefined) { let errorMessage = "Session Node with no Parameters." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION, errorMessage) + bot.sessionError(bot.LEARNING_SESSION, errorMessage) return false } /* Time Range */ - if (bot.AI_SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. - bot.AI_SESSION.parameters.timeRange = { + if (bot.LEARNING_SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. + bot.LEARNING_SESSION.parameters.timeRange = { name: 'Missing Time Range', type: 'Time Range', config: { @@ -202,18 +202,18 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { } } else { /* Check that we received valid dates */ - if (bot.AI_SESSION.type === 'Backtesting Session') { - if (isNaN(new Date(bot.AI_SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { + if (bot.LEARNING_SESSION.type === 'Backtesting Session') { + if (isNaN(new Date(bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) return false } } - if (isNaN(new Date(bot.AI_SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { + if (isNaN(new Date(bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) return false } } @@ -222,7 +222,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { let today = (new Date()).valueOf() let aYearAgo = today - global.ONE_YEAR_IN_MILISECONDS let aYearFromNow = today + global.ONE_YEAR_IN_MILISECONDS - switch (bot.AI_SESSION.type) { + switch (bot.LEARNING_SESSION.type) { case 'Backtesting Session': { useDefaultDatetimes(aYearAgo, today) break @@ -248,81 +248,81 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { */ /* Initial Datetime */ - if (bot.AI_SESSION.type === 'Backtesting Session') { - if (bot.AI_SESSION.parameters.timeRange.config.initialDatetime === undefined) { - bot.AI_SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.LEARNING_SESSION.type === 'Backtesting Session') { + if (bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime === undefined) { + bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault } else { - bot.AI_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.AI_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() } } else { /* Non backtest session can start from the past only if explicitly configured that way */ - if (bot.AI_SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.AI_SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { - bot.AI_SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.LEARNING_SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { + bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault } else { - bot.AI_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.AI_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() } } /* Final Datetime */ - if (bot.AI_SESSION.parameters.timeRange.config.finalDatetime === undefined) { - bot.AI_SESSION.parameters.timeRange.config.finalDatetime = finalDefault + if (bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime === undefined) { + bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime = finalDefault } else { - bot.AI_SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.AI_SESSION.parameters.timeRange.config.finalDatetime)).valueOf() + bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf() } } /* Time Frame */ - if (bot.AI_SESSION.parameters.timeFrame === undefined) { + if (bot.LEARNING_SESSION.parameters.timeFrame === undefined) { let errorMessage = "Session Parameters Node with no Time Frame." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) return false } - if (bot.AI_SESSION.parameters.timeFrame.config.label === undefined) { + if (bot.LEARNING_SESSION.parameters.timeFrame.config.label === undefined) { let errorMessage = "Session Parameters Node with no Time Frame Label configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters.timeFrame, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters.timeFrame, errorMessage) return false } - bot.AI_SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.AI_SESSION.parameters.timeFrame.config.label) - if (bot.AI_SESSION.parameters.timeFrame.config.value === undefined) { + bot.LEARNING_SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.LEARNING_SESSION.parameters.timeFrame.config.label) + if (bot.LEARNING_SESSION.parameters.timeFrame.config.value === undefined) { let errorMessage = "Config error: label value not recognized. Try 01-min or 01-hs for example." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters.timeFrame, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters.timeFrame, errorMessage) return false } /* Session Base Asset */ - if (bot.AI_SESSION.parameters.sessionBaseAsset === undefined) { + if (bot.LEARNING_SESSION.parameters.sessionBaseAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Base Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) return false } - if (bot.AI_SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { + if (bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Base Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters.sessionBaseAsset, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters.sessionBaseAsset, errorMessage) return false } /* Session Quoted Asset */ - if (bot.AI_SESSION.parameters.sessionQuotedAsset === undefined) { + if (bot.LEARNING_SESSION.parameters.sessionQuotedAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Quoted Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) return false } - if (bot.AI_SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { + if (bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Quoted Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.AI_SESSION.parameters.sessionQuotedAsset, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.parameters.sessionQuotedAsset, errorMessage) return false } /* Slippage */ - if (bot.AI_SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. - bot.AI_SESSION.parameters.slippage = { + if (bot.LEARNING_SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. + bot.LEARNING_SESSION.parameters.slippage = { name: 'Missing Slippage', type: 'Slippage', config: { @@ -332,19 +332,19 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { } } } - if (bot.AI_SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. - bot.AI_SESSION.parameters.slippage.config.marketOrderRate = 0 + if (bot.LEARNING_SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. + bot.LEARNING_SESSION.parameters.slippage.config.marketOrderRate = 0 } - if (bot.AI_SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. - bot.AI_SESSION.parameters.slippage.config.stopLoss = 0 + if (bot.LEARNING_SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. + bot.LEARNING_SESSION.parameters.slippage.config.stopLoss = 0 } - if (bot.AI_SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. - bot.AI_SESSION.parameters.slippage.config.takeProfit = 0 + if (bot.LEARNING_SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. + bot.LEARNING_SESSION.parameters.slippage.config.takeProfit = 0 } /* Fee Structure */ - if (bot.AI_SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. - bot.AI_SESSION.parameters.feeStructure = { + if (bot.LEARNING_SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. + bot.LEARNING_SESSION.parameters.feeStructure = { name: 'Missing Fee Structure', type: 'Fee Structure', config: { @@ -353,11 +353,11 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { } } } - if (bot.AI_SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. - bot.AI_SESSION.parameters.feeStructure.config.maker = 0 + if (bot.LEARNING_SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. + bot.LEARNING_SESSION.parameters.feeStructure.config.maker = 0 } - if (bot.AI_SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. - bot.AI_SESSION.parameters.feeStructure.config.taker = 0 + if (bot.LEARNING_SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. + bot.LEARNING_SESSION.parameters.feeStructure.config.taker = 0 } return true @@ -371,7 +371,7 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { if (bot.KEY === undefined || bot.SECRET === undefined) { let errorMessage = "Key 'codeName' or 'secret' not provided. Plese check that and try again." if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> " + errorMessage); } - bot.sessionError(bot.AI_SESSION, errorMessage) + bot.sessionError(bot.LEARNING_SESSION, errorMessage) return false } return true @@ -387,20 +387,20 @@ exports.newTrainingSession = function newTrainingSession(bot, parentLogger) { /* Reduce the balance */ let balancePercentage = 1 // This is the default value - if (bot.AI_SESSION.config.balancePercentage !== undefined) { - balancePercentage = bot.AI_SESSION.config.balancePercentage + if (bot.LEARNING_SESSION.config.balancePercentage !== undefined) { + balancePercentage = bot.LEARNING_SESSION.config.balancePercentage } - bot.AI_SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.AI_SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 - bot.AI_SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.AI_SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 + bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 + bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 - bot.AI_SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.AI_SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 - bot.AI_SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.AI_SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 - bot.AI_SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.AI_SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 - bot.AI_SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.AI_SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 - pProcessConfig.normalWaitTime = bot.AI_SESSION.parameters.timeFrame.config.value + pProcessConfig.normalWaitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value return true } diff --git a/ProcessOutput.js b/ProcessOutput.js index 48d008c7d..c4751f557 100644 --- a/ProcessOutput.js +++ b/ProcessOutput.js @@ -33,7 +33,7 @@ botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'Sensor Bot') } if (botNode === undefined) { - botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'AI Bot') + botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'Learning Bot') } /* Some validations to verify that everything is in place. */ @@ -123,7 +123,7 @@ botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'Sensor Bot') } if (botNode === undefined) { - botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'AI Bot') + botNode = global.FIND_NODE_IN_NODE_MESH(outputDataset, 'Learning Bot') } /* Some validations to verify that everything is in place. */ @@ -148,7 +148,7 @@ throw (global.DEFAULT_FAIL_RESPONSE); } } else { - logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Data Mine | Trading Mine | Intel Mine. Bot = " + JSON.stringify(botNode)); + logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Data Mine | Trading Mine | Learning Mine. Bot = " + JSON.stringify(botNode)); throw (global.DEFAULT_FAIL_RESPONSE); } } else { diff --git a/ReinforcementLearning/AiBot.js b/ReinforcementLearning/LearningBot.js similarity index 98% rename from ReinforcementLearning/AiBot.js rename to ReinforcementLearning/LearningBot.js index 01e9c90aa..f12046828 100644 --- a/ReinforcementLearning/AiBot.js +++ b/ReinforcementLearning/LearningBot.js @@ -1,6 +1,6 @@ -exports.newAiBot = function newAiBot(bot, parentLogger) { +exports.newLearningBot = function newLearningBot(bot, parentLogger) { - const MODULE_NAME = "Ai Bot"; + const MODULE_NAME = "Learning Bot"; const FULL_LOG = true; const TRADING_PROCESS_MODULE = require(global.ROOT_DIR + '/LowFrequencyTrading/MachineLearningProcess.js'); @@ -8,7 +8,7 @@ exports.newAiBot = function newAiBot(bot, parentLogger) { const SESSION = require(global.ROOT_DIR + 'TradingSession'); let fileStorage = FILE_STORAGE.newFileStorage(parentLogger); - let session = SESSION.newTrainingSession(bot, parentLogger) + let session = SESSION.newLearningSession(bot, parentLogger) const DEBUG_MODULE = require(global.ROOT_DIR + 'DebugLog'); let logger; // We need this here in order for the loopHealth function to work and be able to rescue the loop when it gets in trouble. @@ -560,17 +560,17 @@ exports.newAiBot = function newAiBot(bot, parentLogger) { if (processConfig.waitsForExecutionFinishedEvent === true) { waitTime = 0 } else { - switch (bot.AI_SESSION.type) { + switch (bot.LEARNING_SESSION.type) { case 'Live Trading Session': { - waitTime = bot.AI_SESSION.parameters.timeFrame.config.value + waitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value break } case 'Fordward Tessting Session': { - waitTime = bot.AI_SESSION.parameters.timeFrame.config.value + waitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value break } case 'Paper Trading Session': { - waitTime = bot.AI_SESSION.parameters.timeFrame.config.value + waitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value break } case 'Backtesting Session': { diff --git a/ReinforcementLearning/TrainingProcess.js b/ReinforcementLearning/LearningProcess.js similarity index 97% rename from ReinforcementLearning/TrainingProcess.js rename to ReinforcementLearning/LearningProcess.js index e7dc6c62e..0ebbd1bb3 100644 --- a/ReinforcementLearning/TrainingProcess.js +++ b/ReinforcementLearning/LearningProcess.js @@ -1,10 +1,10 @@ -exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) { +exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) { /* This Module will load all the process data dependencies from files and send them downstream. After execution, will save the time range and status report of the process. */ const FULL_LOG = true; - const MODULE_NAME = "Training Process"; + const MODULE_NAME = "Learning Process"; const GMT_SECONDS = ':00.000 GMT+0000'; thisObject = { @@ -115,7 +115,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) the simulation loop, once we discover that all candles from a certain date have benn processed. Here is the point where we sync one and the other. */ - let TrainingProcessDate = global.REMOVE_TIME(bot.simulationState.tradingEngine.current.episode.processDate.value) + let LearningProcessDate = global.REMOVE_TIME(bot.simulationState.tradingEngine.current.episode.processDate.value) await processSingleFiles() if (await processMarketFiles() === false) { @@ -166,7 +166,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) use it to save Output Files and later the Data Ranges. This is the point where the date calculated by the Simulation is applied at the Trading Process Level. */ - TrainingProcessDate = global.REMOVE_TIME(bot.simulationState.tradingEngine.current.episode.processDate.value) + LearningProcessDate = global.REMOVE_TIME(bot.simulationState.tradingEngine.current.episode.processDate.value) if (checkStopTaskGracefully() === false) { break } if (checkStopProcessing() === false) { break } @@ -447,7 +447,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) async function processDailyFiles() { /* Telling the world we are alive and doing well and which date we are processing right now. */ - let processingDateString = TrainingProcessDate.getUTCFullYear() + '-' + utilities.pad(TrainingProcessDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(TrainingProcessDate.getUTCDate(), 2); + let processingDateString = LearningProcessDate.getUTCFullYear() + '-' + utilities.pad(LearningProcessDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(LearningProcessDate.getUTCDate(), 2); bot.processHeartBeat(processingDateString, undefined, "Running...") /* @@ -493,7 +493,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) } } - let dateForPath = TrainingProcessDate.getUTCFullYear() + '/' + utilities.pad(TrainingProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(TrainingProcessDate.getUTCDate(), 2); + let dateForPath = LearningProcessDate.getUTCFullYear() + '/' + utilities.pad(LearningProcessDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(LearningProcessDate.getUTCDate(), 2); let filePath if (dependency.referenceParent.config.codeName === "Multi-Period-Daily") { filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + timeFrameLabel + "/" + dateForPath; @@ -612,7 +612,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) chart, currentTimeFrame, currentTimeFrameLabel, - TrainingProcessDate + LearningProcessDate ) /* @@ -648,7 +648,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) async function writeDataRange(productCodeName) { let dataRange = { begin: contextVariables.dateBeginOfMarket.valueOf(), - end: TrainingProcessDate.valueOf() + global.ONE_DAY_IN_MILISECONDS + end: LearningProcessDate.valueOf() + global.ONE_DAY_IN_MILISECONDS } let fileContent = JSON.stringify(dataRange); @@ -707,7 +707,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) let thisReport = statusDependencies.statusReports.get(reportKey) thisReport.file.lastExecution = bot.currentDaytime - thisReport.file.lastFile = TrainingProcessDate + thisReport.file.lastFile = LearningProcessDate thisReport.file.simulationState = bot.simulationState thisReport.file.timeFrames = currentTimeFrameLabel await thisReport.asyncSave() @@ -721,7 +721,7 @@ exports.newTrainingProcess = function newTrainingProcess(bot, logger, UTILITIES) thisReport.file.simulationState = bot.simulationState thisReport.file.timeFrames = currentTimeFrameLabel - logger.newInternalLoop(bot.codeName, bot.process, TrainingProcessDate) + logger.newInternalLoop(bot.codeName, bot.process, LearningProcessDate) await thisReport.asyncSave() } } diff --git a/ReinforcementLearning/MarkovDecisionProcess.js b/ReinforcementLearning/MarkovDecisionProcess.js index 6b4e1cd1f..072611a7b 100644 --- a/ReinforcementLearning/MarkovDecisionProcess.js +++ b/ReinforcementLearning/MarkovDecisionProcess.js @@ -25,7 +25,7 @@ exports.newMarkovDecisionProcess = function newMarkovDecisionProcess(bot, logger function initialize() { tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.AI_SESSION.parameters + sessionParameters = bot.LEARNING_SESSION.parameters initializeNodeMap(tradingEngine) diff --git a/Root.js b/Root.js index 0511dccad..198f15944 100644 --- a/Root.js +++ b/Root.js @@ -266,8 +266,8 @@ runTradingBot(botConfig, processConfig); break; } - case 'AI Bot': { - runAIBot(botConfig, processConfig); + case 'Learning Bot': { + runLearningBot(botConfig, processConfig); break; } default: { @@ -300,8 +300,8 @@ runTradingBot(botConfig, processConfig); break; } - case 'AI Bot': { - runAIBot(botConfig, processConfig); + case 'Learning Bot': { + runLearningBot(botConfig, processConfig); break; } default: { @@ -347,8 +347,8 @@ runTradingBot(botConfig, processConfig); break; } - case 'AI Bot': { - runAIBot(botConfig, processConfig); + case 'Learning Bot': { + runLearningBot(botConfig, processConfig); break; } default: { @@ -553,7 +553,7 @@ } } - function runAIBot(pBotConfig, pProcessConfig) { + function runLearningBot(pBotConfig, pProcessConfig) { global.TOTAL_PROCESS_INSTANCES_CREATED++ @@ -562,19 +562,19 @@ let logger; logger = DEBUG_MODULE.newDebugLog(); - global.LOGGER_MAP.set('runAIBot', logger) + global.LOGGER_MAP.set('runLearningBot', logger) logger.bot = pBotConfig; - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runAIBot -> Entering function."); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runLearningBot -> Entering function."); } - let aiBot = TRADING_BOT_MODULE.newAIBot(pBotConfig, logger); - aiBot.initialize(pProcessConfig, onInitializeReady); + let learningBot = TRADING_BOT_MODULE.newLearningBot(pBotConfig, logger); + learningBot.initialize(pProcessConfig, onInitializeReady); function onInitializeReady(err) { if (err.result === global.DEFAULT_OK_RESPONSE.result) { - aiBot.run(whenRunFinishes); + learningBot.run(whenRunFinishes); function whenRunFinishes(err) { @@ -586,34 +586,34 @@ if (err.result === global.DEFAULT_OK_RESPONSE.result) { - logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); - logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runLearningBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished sucessfully."); + logger.write(MODULE_NAME, "[INFO] start -> bootingBot -> runLearningBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); logger.persist(); } else { - logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); - logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bye."); - logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); - console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> whenStartFinishes -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> whenStartFinishes -> Execution will be stopped. "); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> whenStartFinishes -> Bye."); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> whenStartFinishes -> Bot Id = " + botId); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> whenStartFinishes -> Bot execution finished with errors. Please check the logs."); logger.persist(); } setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } else { - logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> err = " + err.message); - logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> Failed to initialize the bot. "); - console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runAIBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> err = " + err.message); + logger.write(MODULE_NAME, "[ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> Failed to initialize the bot. "); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runLearningBot -> onInitializeReady -> err = " + err.message); logger.persist(); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } } catch (err) { - console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runAIBot -> err = " + err.stack); + console.log(logDisplace + "Root : [ERROR] start -> bootingBot -> runLearningBot -> err = " + err.stack); setTimeout(exitProcessInstance, WAIT_TIME_FOR_ALL_PROCESS_INSTANCES_TO_START) } } diff --git a/StatusReport.js b/StatusReport.js index db34accaa..2a7699e55 100644 --- a/StatusReport.js +++ b/StatusReport.js @@ -104,8 +104,8 @@ sessionPath = bot.TRADING_SESSION.folderName + "/" } - if (bot.AI_SESSION !== undefined && statusDependencyNode.bottype === "AI Bot") { - sessionPath = bot.AI_SESSION.folderName + "/" + if (bot.LEARNING_SESSION !== undefined && statusDependencyNode.bottype === "Learning Bot") { + sessionPath = bot.LEARNING_SESSION.folderName + "/" } /* Now we will see where do we need to fetch this status report from. */ @@ -268,7 +268,7 @@ On the contraty, if we are running a Sensor bot or an Indicator bot, we might, if necesary, use a previous version of a Status Report since there will be no big impact, just some reprocessing. */ - if (bot.TRADING_SESSION !== undefined || bot.AI_SESSION !== undefined) { + if (bot.TRADING_SESSION !== undefined || bot.LEARNING_SESSION !== undefined) { canUserPrevious = false } else { canUserPrevious = true From 27503eed3f7865bd9cd4f4624e1d6126c79f6a9b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 4 Nov 2020 13:37:02 +0100 Subject: [PATCH 37/41] Bug Fixes --- LearningSession.js | 12 ++++++------ TradingSession.js | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/LearningSession.js b/LearningSession.js index 6b6a5f438..78256597a 100644 --- a/LearningSession.js +++ b/LearningSession.js @@ -38,10 +38,10 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { bot.sessionKey = bot.processNode.session.name + '-' + bot.processNode.session.type + '-' + bot.processNode.session.id global.SESSION_MAP.set(bot.sessionKey, bot.sessionKey) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Session Status', undefined, bot.sessionKey, undefined, onSessionStatus) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Run Session', undefined, bot.sessionKey, undefined, onSessionRun) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Stop Session', undefined, bot.sessionKey, undefined, onSessionStop) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Resume Session', undefined, bot.sessionKey, undefined, onSessionResume) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Learning Session Status', undefined, bot.sessionKey, undefined, onSessionStatus) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Run Learning Session', undefined, bot.sessionKey, undefined, onSessionRun) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Stop Learning Session', undefined, bot.sessionKey, undefined, onSessionStop) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.sessionKey, 'Resume Learning Session', undefined, bot.sessionKey, undefined, onSessionResume) /* Connect this here so that it is accesible from other places */ bot.sessionError = sessionError @@ -57,12 +57,12 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { function onSessionStatus() { if (bot.SESSION_STATUS === 'Running') { let event = { - status: 'Session Runnning' + status: 'Learning Session Runnning' } global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Status Response', event) } else { let event = { - status: 'Not Session Runnning' + status: 'Learning Session Not Runnning' } global.EVENT_SERVER_CLIENT.raiseEvent(bot.sessionKey, 'Status Response', event) } diff --git a/TradingSession.js b/TradingSession.js index 25f2907b4..fd8f6c290 100644 --- a/TradingSession.js +++ b/TradingSession.js @@ -38,10 +38,10 @@ bot.TRADING_SESSIONKey = bot.processNode.session.name + '-' + bot.processNode.session.type + '-' + bot.processNode.session.id global.SESSION_MAP.set(bot.TRADING_SESSIONKey, bot.TRADING_SESSIONKey) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Session Status', undefined, bot.TRADING_SESSIONKey, undefined, onSessionStatus) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Run Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionRun) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Stop Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionStop) - global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Resume Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionResume) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Trading Session Status', undefined, bot.TRADING_SESSIONKey, undefined, onSessionStatus) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Run Trading Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionRun) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Stop Trading Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionStop) + global.EVENT_SERVER_CLIENT.listenToEvent(bot.TRADING_SESSIONKey, 'Resume Trading Session', undefined, bot.TRADING_SESSIONKey, undefined, onSessionResume) /* Connect this here so that it is accesible from other places */ bot.TRADING_SESSIONError = sessionError @@ -57,12 +57,12 @@ function onSessionStatus() { if (bot.TRADING_SESSION_STATUS === 'Running') { let event = { - status: 'Session Runnning' + status: 'Trading Session Runnning' } global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Status Response', event) } else { let event = { - status: 'Not Session Runnning' + status: 'Trading Session Not Runnning' } global.EVENT_SERVER_CLIENT.raiseEvent(bot.TRADING_SESSIONKey, 'Status Response', event) } From 7b12240a7bd811ad441a7042c467b5f847363c7c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 5 Nov 2020 10:03:15 +0100 Subject: [PATCH 38/41] Bug Fixed --- LowFrequencyTrading/TradingEngine.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index 966c4213d..7c868dafb 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -91,6 +91,17 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { break } } + /* + We can not have the last begin or last end to be zero, because that would prevent + objects starting with lastBegin in zero to being saved on file. For that reason + we will do this: + */ + if (tradingEngine.current.episode.cycle.lastBegin.value === 0) { + tradingEngine.current.episode.cycle.lastBegin.value = tradingEngine.current.episode.cycle.begin.value + } + if (tradingEngine.current.episode.cycle.lastEnd.value === 0) { + tradingEngine.current.episode.cycle.lastEnd.value = tradingEngine.current.episode.cycle.end.value + } } function cloneValues(originNode, destinationNode) { From e7757a0f20144a0ba83bbded56003b7ba0783ba3 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 5 Nov 2020 15:40:18 +0100 Subject: [PATCH 39/41] Parameters Implementation --- LearningSession.js | 104 +++++++++--------- LowFrequencyTrading/OrdersCalculations.js | 2 +- LowFrequencyTrading/OrdersSimulations.js | 2 +- LowFrequencyTrading/Snapshots.js | 2 +- LowFrequencyTrading/TradingEngine.js | 2 +- LowFrequencyTrading/TradingEpisode.js | 2 +- LowFrequencyTrading/TradingExecution.js | 2 +- LowFrequencyTrading/TradingOrders.js | 2 +- LowFrequencyTrading/TradingPosition.js | 2 +- LowFrequencyTrading/TradingProcess.js | 18 +-- LowFrequencyTrading/TradingRecords.js | 2 +- LowFrequencyTrading/TradingSimulation.js | 2 +- LowFrequencyTrading/TradingStages.js | 2 +- LowFrequencyTrading/TradingStrategy.js | 2 +- LowFrequencyTrading/TradingSystem.js | 2 +- ReinforcementLearning/LearningBot.js | 6 +- ReinforcementLearning/LearningProcess.js | 18 +-- .../MarkovDecisionProcess.js | 2 +- TradingBot.js | 6 +- TradingSession.js | 104 +++++++++--------- 20 files changed, 142 insertions(+), 142 deletions(-) diff --git a/LearningSession.js b/LearningSession.js index 78256597a..6a7953b65 100644 --- a/LearningSession.js +++ b/LearningSession.js @@ -183,7 +183,7 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { non critical is missing, we complete it with a default value. */ - if (bot.LEARNING_SESSION.parameters === undefined) { + if (bot.LEARNING_SESSION.learningParameters === undefined) { let errorMessage = "Session Node with no Parameters." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) bot.sessionError(bot.LEARNING_SESSION, errorMessage) @@ -191,8 +191,8 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { } /* Time Range */ - if (bot.LEARNING_SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. - bot.LEARNING_SESSION.parameters.timeRange = { + if (bot.LEARNING_SESSION.learningParameters.timeRange === undefined) { // if the Time Range is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.timeRange = { name: 'Missing Time Range', type: 'Time Range', config: { @@ -203,17 +203,17 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { } else { /* Check that we received valid dates */ if (bot.LEARNING_SESSION.type === 'Backtesting Session') { - if (isNaN(new Date(bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { + if (isNaN(new Date(bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters, errorMessage) return false } } - if (isNaN(new Date(bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { + if (isNaN(new Date(bot.LEARNING_SESSION.learningParameters.timeRange.config.finalDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters, errorMessage) return false } } @@ -249,80 +249,80 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { /* Initial Datetime */ if (bot.LEARNING_SESSION.type === 'Backtesting Session') { - if (bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime === undefined) { - bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime === undefined) { + bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime = initialDefault } else { - bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime = (new Date(bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime)).valueOf() } } else { /* Non backtest session can start from the past only if explicitly configured that way */ - if (bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.LEARNING_SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { - bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime === undefined || bot.LEARNING_SESSION.learningParameters.timeRange.config.allowStartingFromThePast !== true) { + bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime = initialDefault } else { - bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.LEARNING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime = (new Date(bot.LEARNING_SESSION.learningParameters.timeRange.config.initialDatetime)).valueOf() } } /* Final Datetime */ - if (bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime === undefined) { - bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime = finalDefault + if (bot.LEARNING_SESSION.learningParameters.timeRange.config.finalDatetime === undefined) { + bot.LEARNING_SESSION.learningParameters.timeRange.config.finalDatetime = finalDefault } else { - bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.LEARNING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf() + bot.LEARNING_SESSION.learningParameters.timeRange.config.finalDatetime = (new Date(bot.LEARNING_SESSION.learningParameters.timeRange.config.finalDatetime)).valueOf() } } /* Time Frame */ - if (bot.LEARNING_SESSION.parameters.timeFrame === undefined) { + if (bot.LEARNING_SESSION.learningParameters.timeFrame === undefined) { let errorMessage = "Session Parameters Node with no Time Frame." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters, errorMessage) return false } - if (bot.LEARNING_SESSION.parameters.timeFrame.config.label === undefined) { + if (bot.LEARNING_SESSION.learningParameters.timeFrame.config.label === undefined) { let errorMessage = "Session Parameters Node with no Time Frame Label configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters.timeFrame, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters.timeFrame, errorMessage) return false } - bot.LEARNING_SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.LEARNING_SESSION.parameters.timeFrame.config.label) - if (bot.LEARNING_SESSION.parameters.timeFrame.config.value === undefined) { + bot.LEARNING_SESSION.learningParameters.timeFrame.config.value = getTimeFrameFromLabel(bot.LEARNING_SESSION.learningParameters.timeFrame.config.label) + if (bot.LEARNING_SESSION.learningParameters.timeFrame.config.value === undefined) { let errorMessage = "Config error: label value not recognized. Try 01-min or 01-hs for example." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters.timeFrame, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters.timeFrame, errorMessage) return false } /* Session Base Asset */ - if (bot.LEARNING_SESSION.parameters.sessionBaseAsset === undefined) { + if (bot.LEARNING_SESSION.learningParameters.sessionBaseAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Base Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters, errorMessage) return false } - if (bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { + if (bot.LEARNING_SESSION.learningParameters.sessionBaseAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Base Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters.sessionBaseAsset, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters.sessionBaseAsset, errorMessage) return false } /* Session Quoted Asset */ - if (bot.LEARNING_SESSION.parameters.sessionQuotedAsset === undefined) { + if (bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Quoted Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters, errorMessage) return false } - if (bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { + if (bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Quoted Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.sessionError(bot.LEARNING_SESSION.parameters.sessionQuotedAsset, errorMessage) + bot.sessionError(bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset, errorMessage) return false } /* Slippage */ - if (bot.LEARNING_SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. - bot.LEARNING_SESSION.parameters.slippage = { + if (bot.LEARNING_SESSION.learningParameters.slippage === undefined) { // if the Slippage is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.slippage = { name: 'Missing Slippage', type: 'Slippage', config: { @@ -332,19 +332,19 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { } } } - if (bot.LEARNING_SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. - bot.LEARNING_SESSION.parameters.slippage.config.marketOrderRate = 0 + if (bot.LEARNING_SESSION.learningParameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.slippage.config.marketOrderRate = 0 } - if (bot.LEARNING_SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. - bot.LEARNING_SESSION.parameters.slippage.config.stopLoss = 0 + if (bot.LEARNING_SESSION.learningParameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.slippage.config.stopLoss = 0 } - if (bot.LEARNING_SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. - bot.LEARNING_SESSION.parameters.slippage.config.takeProfit = 0 + if (bot.LEARNING_SESSION.learningParameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.slippage.config.takeProfit = 0 } /* Fee Structure */ - if (bot.LEARNING_SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. - bot.LEARNING_SESSION.parameters.feeStructure = { + if (bot.LEARNING_SESSION.learningParameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.feeStructure = { name: 'Missing Fee Structure', type: 'Fee Structure', config: { @@ -353,11 +353,11 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { } } } - if (bot.LEARNING_SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. - bot.LEARNING_SESSION.parameters.feeStructure.config.maker = 0 + if (bot.LEARNING_SESSION.learningParameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.feeStructure.config.maker = 0 } - if (bot.LEARNING_SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. - bot.LEARNING_SESSION.parameters.feeStructure.config.taker = 0 + if (bot.LEARNING_SESSION.learningParameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. + bot.LEARNING_SESSION.learningParameters.feeStructure.config.taker = 0 } return true @@ -391,16 +391,16 @@ exports.newLearningSession = function newLearningSession(bot, parentLogger) { balancePercentage = bot.LEARNING_SESSION.config.balancePercentage } - bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 - bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 + bot.LEARNING_SESSION.learningParameters.sessionBaseAsset.config.initialBalance = bot.LEARNING_SESSION.learningParameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 + bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset.config.initialBalance = bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 - bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 - bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.learningParameters.sessionBaseAsset.config.minimumBalance = bot.LEARNING_SESSION.learningParameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset.config.minimumBalance = bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 - bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.LEARNING_SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 - bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.LEARNING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.learningParameters.sessionBaseAsset.config.maximumBalance = bot.LEARNING_SESSION.learningParameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 + bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset.config.maximumBalance = bot.LEARNING_SESSION.learningParameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 - pProcessConfig.normalWaitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value + pProcessConfig.normalWaitTime = bot.LEARNING_SESSION.learningParameters.timeFrame.config.value return true } diff --git a/LowFrequencyTrading/OrdersCalculations.js b/LowFrequencyTrading/OrdersCalculations.js index 4d24e4887..94ffe42d3 100644 --- a/LowFrequencyTrading/OrdersCalculations.js +++ b/LowFrequencyTrading/OrdersCalculations.js @@ -23,7 +23,7 @@ exports.newOrdersCalculations = function newOrdersCalculations(bot, logger) { return thisObject function initialize() { - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem } diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index 1432a822f..c50977c1a 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -23,7 +23,7 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { return thisObject function initialize() { - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem } diff --git a/LowFrequencyTrading/Snapshots.js b/LowFrequencyTrading/Snapshots.js index afa2c83e7..933d0f37c 100644 --- a/LowFrequencyTrading/Snapshots.js +++ b/LowFrequencyTrading/Snapshots.js @@ -45,7 +45,7 @@ exports.newSnapshots = function newSnapshots(bot, logger) { function initialize() { tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters } function finalize() { diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index 7c868dafb..b91ed2247 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -25,7 +25,7 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { function initialize() { tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters initializeNodeMap(tradingEngine) diff --git a/LowFrequencyTrading/TradingEpisode.js b/LowFrequencyTrading/TradingEpisode.js index 56d6d289c..05ea301c0 100644 --- a/LowFrequencyTrading/TradingEpisode.js +++ b/LowFrequencyTrading/TradingEpisode.js @@ -23,7 +23,7 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters } function finalize() { diff --git a/LowFrequencyTrading/TradingExecution.js b/LowFrequencyTrading/TradingExecution.js index ecc3d6f35..b1e5e3e25 100644 --- a/LowFrequencyTrading/TradingExecution.js +++ b/LowFrequencyTrading/TradingExecution.js @@ -24,7 +24,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters tradingOrdersModule.initialize() } diff --git a/LowFrequencyTrading/TradingOrders.js b/LowFrequencyTrading/TradingOrders.js index 651905e9c..2c432eb57 100644 --- a/LowFrequencyTrading/TradingOrders.js +++ b/LowFrequencyTrading/TradingOrders.js @@ -27,7 +27,7 @@ exports.newTradingOrders = function newTradingOrders(bot, logger, tradingEngineM function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters exchangeAPIModule.initialize() announcementsModule.initialize() diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index a0448f1b6..8c606d203 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -31,7 +31,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters } function finalize() { diff --git a/LowFrequencyTrading/TradingProcess.js b/LowFrequencyTrading/TradingProcess.js index 95c076e1e..1032c04ea 100644 --- a/LowFrequencyTrading/TradingProcess.js +++ b/LowFrequencyTrading/TradingProcess.js @@ -103,7 +103,7 @@ This variable tell us which day we are standing at, specially while working with Daily Files. From this Date is that we are going to load the Daily Files. */ - bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime).valueOf() + bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime).valueOf() } /* @@ -219,7 +219,7 @@ callBackFunction(global.DEFAULT_OK_RESPONSE) function checkThereAreCandles(chart) { - let sessionParameters = bot.TRADING_SESSION.parameters + let sessionParameters = bot.TRADING_SESSION.tradingParameters let propertyName = 'at' + sessionParameters.timeFrame.config.label.replace('-', '') let candles = chart[propertyName].candles @@ -236,7 +236,7 @@ let statusReport; /* We are going to use the start date as beging of market date. */ - contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime) + contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime) /* Here we get the status report from the bot who knows which is the end of the market. */ @@ -371,7 +371,7 @@ dataFiles = new Map(); - if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.marketFilesPeriods[n][0]; currentTimeFrameLabel = global.marketFilesPeriods[n][1]; } @@ -384,7 +384,7 @@ } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } @@ -431,8 +431,8 @@ let dataRecord = dataFile[i] let begin = dataRecord[beginIndex] let end = dataRecord[endIndex] - if (end + timeFrame < bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element - if (begin > bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime) { continue } + if (end + timeFrame < bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element + if (begin > bot.TRADING_SESSION.tradingParameters.timeRange.config.finalDatetime) { continue } result.push(dataRecord) } return result @@ -468,7 +468,7 @@ } } - if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.dailyFilePeriods[n][0]; currentTimeFrameLabel = global.dailyFilePeriods[n][1]; } @@ -488,7 +488,7 @@ } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index 3ae05a2bf..5540158dd 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -20,7 +20,7 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { function initialize(pOutputDatasetsMap) { tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters outputDatasetsMap = pOutputDatasetsMap // These are the files turned into arrays, stored in a Map by Product codeName. } diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index 25f9aa694..3c262e76a 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -28,7 +28,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, tradin let tradingSystem = bot.simulationState.tradingSystem let tradingEngine = bot.simulationState.tradingEngine - let sessionParameters = bot.TRADING_SESSION.parameters + let sessionParameters = bot.TRADING_SESSION.tradingParameters if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime) } if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime) } diff --git a/LowFrequencyTrading/TradingStages.js b/LowFrequencyTrading/TradingStages.js index 9c25e8992..c62fd4b4c 100644 --- a/LowFrequencyTrading/TradingStages.js +++ b/LowFrequencyTrading/TradingStages.js @@ -47,7 +47,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters tradingStrategyModule.initialize() tradingPositionModule.initialize() diff --git a/LowFrequencyTrading/TradingStrategy.js b/LowFrequencyTrading/TradingStrategy.js index 3c11a59c0..fbd170845 100644 --- a/LowFrequencyTrading/TradingStrategy.js +++ b/LowFrequencyTrading/TradingStrategy.js @@ -19,7 +19,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng function initialize() { tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters } function finalize() { diff --git a/LowFrequencyTrading/TradingSystem.js b/LowFrequencyTrading/TradingSystem.js index 58c3135c5..ddafc6f33 100644 --- a/LowFrequencyTrading/TradingSystem.js +++ b/LowFrequencyTrading/TradingSystem.js @@ -28,7 +28,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM function initialize() { tradingSystem = bot.simulationState.tradingSystem tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.TRADING_SESSION.parameters + sessionParameters = bot.TRADING_SESSION.tradingParameters tradingSystem.conditions = new Map() tradingSystem.formulas = new Map() diff --git a/ReinforcementLearning/LearningBot.js b/ReinforcementLearning/LearningBot.js index f12046828..ad887979f 100644 --- a/ReinforcementLearning/LearningBot.js +++ b/ReinforcementLearning/LearningBot.js @@ -562,15 +562,15 @@ exports.newLearningBot = function newLearningBot(bot, parentLogger) { } else { switch (bot.LEARNING_SESSION.type) { case 'Live Trading Session': { - waitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value + waitTime = bot.LEARNING_SESSION.learningParameters.timeFrame.config.value break } case 'Fordward Tessting Session': { - waitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value + waitTime = bot.LEARNING_SESSION.learningParameters.timeFrame.config.value break } case 'Paper Trading Session': { - waitTime = bot.LEARNING_SESSION.parameters.timeFrame.config.value + waitTime = bot.LEARNING_SESSION.learningParameters.timeFrame.config.value break } case 'Backtesting Session': { diff --git a/ReinforcementLearning/LearningProcess.js b/ReinforcementLearning/LearningProcess.js index 0ebbd1bb3..9cb44f672 100644 --- a/ReinforcementLearning/LearningProcess.js +++ b/ReinforcementLearning/LearningProcess.js @@ -103,7 +103,7 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) This variable tell us which day we are standing at, specially while working with Daily Files. From this Date is that we are going to load the Daily Files. */ - bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime).valueOf() + bot.simulationState.tradingEngine.current.episode.processDate.value = global.REMOVE_TIME(bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime).valueOf() } /* @@ -219,7 +219,7 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) callBackFunction(global.DEFAULT_OK_RESPONSE) function checkThereAreCandles(chart) { - let sessionParameters = bot.TRADING_SESSION.parameters + let sessionParameters = bot.TRADING_SESSION.tradingParameters let propertyName = 'at' + sessionParameters.timeFrame.config.label.replace('-', '') let candles = chart[propertyName].candles @@ -236,7 +236,7 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) let statusReport; /* We are going to use the start date as beging of market date. */ - contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime) + contextVariables.dateBeginOfMarket = global.REMOVE_TIME(bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime) /* Here we get the status report from the bot who knows which is the end of the market. */ @@ -371,7 +371,7 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) dataFiles = new Map(); - if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.marketFilesPeriods[n][0]; currentTimeFrameLabel = global.marketFilesPeriods[n][1]; } @@ -384,7 +384,7 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } @@ -431,8 +431,8 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) let dataRecord = dataFile[i] let begin = dataRecord[beginIndex] let end = dataRecord[endIndex] - if (end + timeFrame < bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element - if (begin > bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime) { continue } + if (end + timeFrame < bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime - 1) { continue } // /1 because we need the previous closed element + if (begin > bot.TRADING_SESSION.tradingParameters.timeRange.config.finalDatetime) { continue } result.push(dataRecord) } return result @@ -468,7 +468,7 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) } } - if (bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel) { + if (bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel) { currentTimeFrame = global.dailyFilePeriods[n][0]; currentTimeFrameLabel = global.dailyFilePeriods[n][1]; } @@ -488,7 +488,7 @@ exports.newLearningProcess = function newLearningProcess(bot, logger, UTILITIES) } if (dataDependenciesModule.isItADepenency(timeFrameLabel, datasetModule.node.parentNode.config.singularVariableName) !== true) { - if (!(bot.TRADING_SESSION.parameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { + if (!(bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === timeFrameLabel && datasetModule.node.parentNode.config.pluralVariableName === 'candles')) { continue } } diff --git a/ReinforcementLearning/MarkovDecisionProcess.js b/ReinforcementLearning/MarkovDecisionProcess.js index 072611a7b..ca46398dc 100644 --- a/ReinforcementLearning/MarkovDecisionProcess.js +++ b/ReinforcementLearning/MarkovDecisionProcess.js @@ -25,7 +25,7 @@ exports.newMarkovDecisionProcess = function newMarkovDecisionProcess(bot, logger function initialize() { tradingEngine = bot.simulationState.tradingEngine - sessionParameters = bot.LEARNING_SESSION.parameters + sessionParameters = bot.LEARNING_SESSION.learningParameters initializeNodeMap(tradingEngine) diff --git a/TradingBot.js b/TradingBot.js index deccc008b..064a3a3c2 100644 --- a/TradingBot.js +++ b/TradingBot.js @@ -562,15 +562,15 @@ } else { switch (bot.TRADING_SESSION.type) { case 'Live Trading Session': { - waitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value + waitTime = bot.TRADING_SESSION.tradingParameters.timeFrame.config.value break } case 'Fordward Tessting Session': { - waitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value + waitTime = bot.TRADING_SESSION.tradingParameters.timeFrame.config.value break } case 'Paper Trading Session': { - waitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value + waitTime = bot.TRADING_SESSION.tradingParameters.timeFrame.config.value break } case 'Backtesting Session': { diff --git a/TradingSession.js b/TradingSession.js index fd8f6c290..5999d48dd 100644 --- a/TradingSession.js +++ b/TradingSession.js @@ -183,7 +183,7 @@ non critical is missing, we complete it with a default value. */ - if (bot.TRADING_SESSION.parameters === undefined) { + if (bot.TRADING_SESSION.tradingParameters === undefined) { let errorMessage = "Session Node with no Parameters." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) bot.TRADING_SESSIONError(bot.TRADING_SESSION, errorMessage) @@ -191,8 +191,8 @@ } /* Time Range */ - if (bot.TRADING_SESSION.parameters.timeRange === undefined) { // if the Time Range is missing we create a default one. - bot.TRADING_SESSION.parameters.timeRange = { + if (bot.TRADING_SESSION.tradingParameters.timeRange === undefined) { // if the Time Range is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.timeRange = { name: 'Missing Time Range', type: 'Time Range', config: { @@ -203,17 +203,17 @@ } else { /* Check that we received valid dates */ if (bot.TRADING_SESSION.type === 'Backtesting Session') { - if (isNaN(new Date(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf()) { + if (isNaN(new Date(bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters, errorMessage) return false } } - if (isNaN(new Date(bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf()) { + if (isNaN(new Date(bot.TRADING_SESSION.tradingParameters.timeRange.config.finalDatetime)).valueOf()) { let errorMessage = "sessionParameters.timeRange.config.initialDatetime is not a valid date." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters, errorMessage) return false } } @@ -249,80 +249,80 @@ /* Initial Datetime */ if (bot.TRADING_SESSION.type === 'Backtesting Session') { - if (bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime === undefined) { - bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime === undefined) { + bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime = initialDefault } else { - bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime = (new Date(bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime)).valueOf() } } else { /* Non backtest session can start from the past only if explicitly configured that way */ - if (bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime === undefined || bot.TRADING_SESSION.parameters.timeRange.config.allowStartingFromThePast !== true) { - bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = initialDefault + if (bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime === undefined || bot.TRADING_SESSION.tradingParameters.timeRange.config.allowStartingFromThePast !== true) { + bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime = initialDefault } else { - bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.TRADING_SESSION.parameters.timeRange.config.initialDatetime)).valueOf() + bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime = (new Date(bot.TRADING_SESSION.tradingParameters.timeRange.config.initialDatetime)).valueOf() } } /* Final Datetime */ - if (bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime === undefined) { - bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime = finalDefault + if (bot.TRADING_SESSION.tradingParameters.timeRange.config.finalDatetime === undefined) { + bot.TRADING_SESSION.tradingParameters.timeRange.config.finalDatetime = finalDefault } else { - bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.TRADING_SESSION.parameters.timeRange.config.finalDatetime)).valueOf() + bot.TRADING_SESSION.tradingParameters.timeRange.config.finalDatetime = (new Date(bot.TRADING_SESSION.tradingParameters.timeRange.config.finalDatetime)).valueOf() } } /* Time Frame */ - if (bot.TRADING_SESSION.parameters.timeFrame === undefined) { + if (bot.TRADING_SESSION.tradingParameters.timeFrame === undefined) { let errorMessage = "Session Parameters Node with no Time Frame." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters, errorMessage) return false } - if (bot.TRADING_SESSION.parameters.timeFrame.config.label === undefined) { + if (bot.TRADING_SESSION.tradingParameters.timeFrame.config.label === undefined) { let errorMessage = "Session Parameters Node with no Time Frame Label configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.timeFrame, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters.timeFrame, errorMessage) return false } - bot.TRADING_SESSION.parameters.timeFrame.config.value = getTimeFrameFromLabel(bot.TRADING_SESSION.parameters.timeFrame.config.label) - if (bot.TRADING_SESSION.parameters.timeFrame.config.value === undefined) { + bot.TRADING_SESSION.tradingParameters.timeFrame.config.value = getTimeFrameFromLabel(bot.TRADING_SESSION.tradingParameters.timeFrame.config.label) + if (bot.TRADING_SESSION.tradingParameters.timeFrame.config.value === undefined) { let errorMessage = "Config error: label value not recognized. Try 01-min or 01-hs for example." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.timeFrame, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters.timeFrame, errorMessage) return false } /* Session Base Asset */ - if (bot.TRADING_SESSION.parameters.sessionBaseAsset === undefined) { + if (bot.TRADING_SESSION.tradingParameters.sessionBaseAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Base Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters, errorMessage) return false } - if (bot.TRADING_SESSION.parameters.sessionBaseAsset.config.initialBalance === undefined) { + if (bot.TRADING_SESSION.tradingParameters.sessionBaseAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Base Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.sessionBaseAsset, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters.sessionBaseAsset, errorMessage) return false } /* Session Quoted Asset */ - if (bot.TRADING_SESSION.parameters.sessionQuotedAsset === undefined) { + if (bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset === undefined) { let errorMessage = "Session Parameters Node with no Session Quoted Asset." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters, errorMessage) return false } - if (bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.initialBalance === undefined) { + if (bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset.config.initialBalance === undefined) { let errorMessage = "Session Parameters Session Quoted Asset with no initialBalance configuration." parentLogger.write(MODULE_NAME, "[ERROR] initialize -> checkParemeters -> " + errorMessage) - bot.TRADING_SESSIONError(bot.TRADING_SESSION.parameters.sessionQuotedAsset, errorMessage) + bot.TRADING_SESSIONError(bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset, errorMessage) return false } /* Slippage */ - if (bot.TRADING_SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one. - bot.TRADING_SESSION.parameters.slippage = { + if (bot.TRADING_SESSION.tradingParameters.slippage === undefined) { // if the Slippage is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.slippage = { name: 'Missing Slippage', type: 'Slippage', config: { @@ -332,19 +332,19 @@ } } } - if (bot.TRADING_SESSION.parameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. - bot.TRADING_SESSION.parameters.slippage.config.marketOrderRate = 0 + if (bot.TRADING_SESSION.tradingParameters.slippage.config.marketOrderRate === undefined) { // if the marketOrderRate is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.slippage.config.marketOrderRate = 0 } - if (bot.TRADING_SESSION.parameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. - bot.TRADING_SESSION.parameters.slippage.config.stopLoss = 0 + if (bot.TRADING_SESSION.tradingParameters.slippage.config.stopLoss === undefined) { // if the stopLoss is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.slippage.config.stopLoss = 0 } - if (bot.TRADING_SESSION.parameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. - bot.TRADING_SESSION.parameters.slippage.config.takeProfit = 0 + if (bot.TRADING_SESSION.tradingParameters.slippage.config.takeProfit === undefined) { // if the takeProfit is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.slippage.config.takeProfit = 0 } /* Fee Structure */ - if (bot.TRADING_SESSION.parameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. - bot.TRADING_SESSION.parameters.feeStructure = { + if (bot.TRADING_SESSION.tradingParameters.feeStructure === undefined) { // if the Fee Structure is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.feeStructure = { name: 'Missing Fee Structure', type: 'Fee Structure', config: { @@ -353,11 +353,11 @@ } } } - if (bot.TRADING_SESSION.parameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. - bot.TRADING_SESSION.parameters.feeStructure.config.maker = 0 + if (bot.TRADING_SESSION.tradingParameters.feeStructure.config.maker === undefined) { // if the maker is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.feeStructure.config.maker = 0 } - if (bot.TRADING_SESSION.parameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. - bot.TRADING_SESSION.parameters.feeStructure.config.taker = 0 + if (bot.TRADING_SESSION.tradingParameters.feeStructure.config.taker === undefined) { // if the taker is missing we create a default one. + bot.TRADING_SESSION.tradingParameters.feeStructure.config.taker = 0 } return true @@ -391,16 +391,16 @@ balancePercentage = bot.TRADING_SESSION.config.balancePercentage } - bot.TRADING_SESSION.parameters.sessionBaseAsset.config.initialBalance = bot.TRADING_SESSION.parameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 - bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.initialBalance = bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 + bot.TRADING_SESSION.tradingParameters.sessionBaseAsset.config.initialBalance = bot.TRADING_SESSION.tradingParameters.sessionBaseAsset.config.initialBalance * balancePercentage / 100 + bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset.config.initialBalance = bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset.config.initialBalance * balancePercentage / 100 - bot.TRADING_SESSION.parameters.sessionBaseAsset.config.minimumBalance = bot.TRADING_SESSION.parameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 - bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance = bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 + bot.TRADING_SESSION.tradingParameters.sessionBaseAsset.config.minimumBalance = bot.TRADING_SESSION.tradingParameters.sessionBaseAsset.config.minimumBalance * balancePercentage / 100 + bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset.config.minimumBalance = bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset.config.minimumBalance * balancePercentage / 100 - bot.TRADING_SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.TRADING_SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 - bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.TRADING_SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 + bot.TRADING_SESSION.tradingParameters.sessionBaseAsset.config.maximumBalance = bot.TRADING_SESSION.tradingParameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100 + bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset.config.maximumBalance = bot.TRADING_SESSION.tradingParameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100 - pProcessConfig.normalWaitTime = bot.TRADING_SESSION.parameters.timeFrame.config.value + pProcessConfig.normalWaitTime = bot.TRADING_SESSION.tradingParameters.timeFrame.config.value return true } From eb267550085f2469c938ff21715b50f391455e85 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 7 Nov 2020 11:24:57 +0100 Subject: [PATCH 40/41] Update OrdersSimulations.js --- LowFrequencyTrading/OrdersSimulations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LowFrequencyTrading/OrdersSimulations.js b/LowFrequencyTrading/OrdersSimulations.js index c50977c1a..cf4fa5a20 100644 --- a/LowFrequencyTrading/OrdersSimulations.js +++ b/LowFrequencyTrading/OrdersSimulations.js @@ -284,13 +284,13 @@ exports.newOrdersSimulations = function newOrdersSimulations(bot, logger) { let orderWasHit switch (tradingSystemOrder.type) { case 'Limit Buy Order': { - if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.actualRate.value) { + if (tradingEngine.current.episode.candle.min.value <= tradingEngineOrder.orderStatistics.actualRate.value) { orderWasHit = true } break } case 'Limit Sell Order': { - if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.actualRate.value) { + if (tradingEngine.current.episode.candle.max.value >= tradingEngineOrder.orderStatistics.actualRate.value) { orderWasHit = true } break From ff7768c1f0e984d0384ccbbddbe9fc9883c17658 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 7 Nov 2020 13:25:13 +0100 Subject: [PATCH 41/41] Perf Improvement --- LowFrequencyTrading/TradingRecords.js | 29 ++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index 5540158dd..8777fc2b6 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -143,7 +143,7 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { the Root Node specifically for this property only. */ if (recordProperty.config.nodePath !== undefined) { - if ( recordProperty.config.nodePath === "tradingEngine.exchangeOrders.marketBuyOrders.marketOrders[index].orderBaseAsset") { + if (recordProperty.config.nodePath === "tradingEngine.exchangeOrders.marketBuyOrders.marketOrders[index].orderBaseAsset") { let a = tradingEngine } try { @@ -287,8 +287,31 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { } } } else { - /* When we are not dealing with objects, we add every record to the existing file. */ - outputDatasetArray.push(record) + /* + When we are not dealing with objects, we add every record to the existing file except + for the ones that are filtered out at the Product Definition. + */ + if (product.config.codeName === "Trading-System-Errors") { + let a = 1 + } + if (product.config.propertyNameThatDefinesStatus !== undefined && product.config.propertyValueThatPreventsSavingObject !== undefined) { + for (let j = 0; j < product.record.properties.length; j++) { + let recordProperty = product.record.properties[j] + if (recordProperty.config.codeName === product.config.propertyNameThatDefinesStatus) { + let propertyValue = record[j] + + if (product.config.propertyValueThatPreventsSavingObject === "->Empty Array->" && propertyValue.length === 0) { + break + } + if (propertyValue !== product.config.propertyValueThatPreventsSavingObject) { + outputDatasetArray.push(record) + } + break + } + } + } else { + outputDatasetArray.push(record) + } } }