diff --git a/LowFrequencyTrading/Announcements.js b/LowFrequencyTrading/Announcements.js index 99fdb57b3..cb52abb0a 100644 --- a/LowFrequencyTrading/Announcements.js +++ b/LowFrequencyTrading/Announcements.js @@ -45,7 +45,7 @@ exports.newAnnouncements = function newAnnouncements(bot, logger) { } } - if (tradingEngine.episode.episodeCounters.periods > lastPeriodAnnounced) { + if (tradingEngine.episode.episodeCounters.periods.value > lastPeriodAnnounced) { if (isNaN(value) === false) { /* The Value Variation is what tells us how much the value already announced must change in order to annouce it again. */ let valueVariation @@ -72,12 +72,12 @@ exports.newAnnouncements = function newAnnouncements(bot, logger) { /* Next, we will remmeber this announcement was already done, so that it is not announced again in further processing of the same day. */ if (newAnnouncementRecord.periods !== undefined) { - newAnnouncementRecord.periods = tradingEngine.episode.episodeCounters.periods + newAnnouncementRecord.periods = tradingEngine.episode.episodeCounters.periods.value newAnnouncementRecord.value = value } else { newAnnouncementRecord = { key: key, - periods: tradingEngine.episode.episodeCounters.periods, + periods: tradingEngine.episode.episodeCounters.periods.value, value: value } variable.announcements.push(newAnnouncementRecord) diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index 1c35bd47c..12765c8a0 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -133,8 +133,8 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { } function updateEpisodeCountersAndStatistics() { - tradingEngine.episode.episodeCounters.periods++ - tradingEngine.episode.episodeStatistics.days = tradingEngine.episode.episodeCounters.periods * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS + tradingEngine.episode.episodeCounters.periods.value++ + tradingEngine.episode.episodeStatistics.days.value = tradingEngine.episode.episodeCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS } function updatePositionCountersAndStatistics() { @@ -278,11 +278,11 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { if (sessionParameters.sessionBaseAsset.name === bot.market.marketBaseAsset) { tradingEngine.episode.episodeStatistics.ROI.value = (sessionParameters.sessionBaseAsset.config.initialBalance + tradingEngine.episode.episodeStatistics.profitLoss.value) / sessionParameters.sessionBaseAsset.config.initialBalance - 1 tradingEngine.episode.episodeStatistics.hitRatio.value = tradingEngine.episode.episodeCounters.hits.value / tradingEngine.episode.positionCounters.positions.value - tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days * 365 + tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 } else { tradingEngine.episode.episodeStatistics.ROI.value = (sessionParameters.sessionQuotedAsset.config.initialBalance + tradingEngine.episode.episodeStatistics.profitLoss.value) / sessionParameters.sessionQuotedAsset.config.initialBalance - 1 tradingEngine.episode.episodeStatistics.hitRatio.value = tradingEngine.episode.episodeCounters.hits.value / tradingEngine.episode.positionCounters.positions.value - tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days * 365 + tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 } addRecords() diff --git a/LowFrequencyTrading/TradingExecution.js b/LowFrequencyTrading/TradingExecution.js index 169b1f3b6..385792450 100644 --- a/LowFrequencyTrading/TradingExecution.js +++ b/LowFrequencyTrading/TradingExecution.js @@ -105,7 +105,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ if (variable.executionContext !== undefined) { if (variable.executionContext.periods !== undefined) { - if (tradingEngine.episode.episodeCounters.periods <= variable.executionContext.periods) { + if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) { if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution.') } takePositionAtSimulation() return @@ -149,7 +149,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { variable.executionContext = { status: 'Taking Position', - periods: tradingEngine.episode.episodeCounters.periods + periods: tradingEngine.episode.episodeCounters.periods.value } if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to create order.') } @@ -164,7 +164,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ') } variable.executionContext = { status: 'In a Position', - periods: tradingEngine.episode.episodeCounters.periods, + periods: tradingEngine.episode.episodeCounters.periods.value, amountA: amountA, amountB: amountB, orderId: order.id @@ -244,7 +244,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ if (variable.executionContext !== undefined) { if (variable.executionContext.periods !== undefined) { - if (tradingEngine.episode.episodeCounters.periods <= variable.executionContext.periods) { + if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) { if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution.') } closePositionAtSimulation() return @@ -275,7 +275,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { variable.executionContext = { status: 'Closing Position', - periods: tradingEngine.episode.episodeCounters.periods + periods: tradingEngine.episode.episodeCounters.periods.value } if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange.') } @@ -290,7 +290,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ') } variable.executionContext = { status: 'Position Closed', - periods: tradingEngine.episode.episodeCounters.periods, + periods: tradingEngine.episode.episodeCounters.periods.value, amountA: amountA, amountB: amountB, orderId: order.id diff --git a/LowFrequencyTrading/TradingOutput.js b/LowFrequencyTrading/TradingOutput.js index bf2ba4679..bc43286f7 100644 --- a/LowFrequencyTrading/TradingOutput.js +++ b/LowFrequencyTrading/TradingOutput.js @@ -83,7 +83,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL let fileName = 'Data.json' let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel - readOutputFile(fileName, filePath, dataset.parentNode.config.pluralVariableName) + readOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } } } @@ -99,7 +99,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL let fileName = 'Data.json' let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath - readOutputFile(fileName, filePath, dataset.parentNode.config.pluralVariableName) + readOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } } } @@ -172,7 +172,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL let fileName = 'Data.json' let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel - writeOutputFile(fileName, filePath, dataset.parentNode.config.pluralVariableName) + writeOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } } } @@ -188,7 +188,7 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL let fileName = 'Data.json' let filePath = bot.filePathRoot + '/Output/' + bot.SESSION.folderName + '/' + dataset.parentNode.config.codeName + '/' + dataset.config.codeName + '/' + timeFrameLabel + "/" + dateForPath - writeOutputFile(fileName, filePath, dataset.parentNode.config.pluralVariableName) + writeOutputFile(fileName, filePath, dataset.parentNode.config.codeName) } } } diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index 92d5c70c7..c26cc1623 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -14,7 +14,7 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { return thisObject - function initialize(pOutputDatasets, pOutputDatasetsMap) { + function initialize(pOutputDatasetsMap) { tradingEngine = bot.simulationState.tradingEngine tradingSystem = bot.simulationState.tradingSystem outputDatasetsMap = pOutputDatasetsMap // These are the files turned into arrays, stored in a Map by Product codeName. @@ -52,32 +52,84 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { } function scanRecordDefinition(product) { - let rootNode = eval(product.config.nodePath) + /* + The Product Root Node is the root of the node hiriarchy branch that we need to look at + in order to fnd the node where we are going to extract the value. + */ + let productRootNode = eval(product.config.nodePath) let record = [] - for (let j = 0; j < product.recordDefinition.recordProperties.length; j++) { - let recordProperty = product.recordDefinition.recordProperties[j] - let value + for (let j = 0; j < product.record.properties.length; j++) { + let recordProperty = product.record.properties[j] + /* + The Property Root Node is the Root of the Hiriarchy branch we must find in order + to get the node where we are going to extract the value. Initially + we point it to the Product Root Node, because that is the default in case + a property does not have at its configuration a different nodePath configured + pointing to an specific Root for the property. + */ + let propertyRootNode = productRootNode + /* + If we find at the configuration a nodePath, then we take this path to find + the Root Node specifically for this property only. + */ if (recordProperty.config.nodePath !== undefined) { - let childNode = rootNode[recordProperty.config.childNode] - if (recordProperty.config.index !== undefined) { - value = childNode[recordProperty.config.codeName] // this returns an array - value = value[recordProperty.config.index].value // this return a node item of the array and its value - } else { - value = childNode[recordProperty.config.codeName].value // here we get the value of the childNode - } - } else { - value = rootNode[recordProperty.config.codeName].value // this is the standard way + propertyRootNode = eval(recordProperty.config.nodePath) } - if (value === Infinity) { - value = Number.MAX_SAFE_INTEGER + /* + 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. + */ + let targetNode = propertyRootNode[recordProperty.config.codeName] + /* + If the codeName of the Record Property can not match the name of the property at + the target node, the user can explicitly specify the property name at the configuration, + and in those cases we need to use that. This happens when there are many Record Properties + pointing to the same property at the Target Node. + */ + if (recordProperty.config.childProperty !== undefined) { + targetNode = propertyRootNode[recordProperty.config.childProperty] } + /* + It can happen that intead of having a Node in targetNode what we have is an + array of nodes. We need to pick one of the elements of the array and for that + we use the Index value we find at the configuration of the Record Property. + */ + if (recordProperty.config.index !== undefined) { + targetNode = targetNode[recordProperty.config.index] + } + /* + By Default the value is extracted from the value property of the Target Node. + But it might happen the the Target Node does not exist for example when there is an Array + of Nodes defined in the Record Properties but not all of them exist at the Root Node. + We filter out those cases by not extracting the value from the value property. + */ + let value + if (targetNode !== undefined) { + value = targetNode.value + } + + value = safeValue(value) if (recordProperty.config.isString === true) { value = '"' + value + '"' } record.push(value) } - return record } } + + function safeValue(value) { + /* + The purpose of this function is to check that value variable does not have a value that + will later break the JSON format of files where this is going to be stored at. + */ + if (value === Infinity) { + value = Number.MAX_SAFE_INTEGER + } + if (value === undefined) { + value = 0 + } + return value + } } \ No newline at end of file diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index 59192b43d..d1eede93d 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -233,7 +233,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT snapshotsModule.manageSnapshots() tradingRecordsModule.appendRecords() - annoucementsModule.makeAnnoucements() // After everything at the simulation level was done, we will do the annoucements that are pending. + announcementsModule.makeAnnoucements() // After everything at the simulation level was done, we will do the annoucements that are pending. controlLoop() }