diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index 3189af917..62f0889b5 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -23,7 +23,7 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { tradingEngine = bot.simulationState.tradingEngine sessionParameters = bot.SESSION.parameters - if (bot.RESUME === false) { + if (bot.FIRST_EXECUTION === true) { initializeNode(tradingEngine) tradingEngine.current.balance.baseAsset.value = sessionParameters.sessionBaseAsset.config.initialBalance tradingEngine.current.balance.quotedAsset.value = sessionParameters.sessionQuotedAsset.config.initialBalance diff --git a/LowFrequencyTrading/TradingOutput.js b/LowFrequencyTrading/TradingOutput.js index 5c99087ff..0abe8c40a 100644 --- a/LowFrequencyTrading/TradingOutput.js +++ b/LowFrequencyTrading/TradingOutput.js @@ -57,7 +57,10 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL let totalFilesWritten = 0 let outputDatasetsMap = new Map() - if (bot.RESUME !== true) { + 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 initializeOutputs() diff --git a/LowFrequencyTrading/TradingProcess.js b/LowFrequencyTrading/TradingProcess.js index 5e790d354..eb23e6ae6 100644 --- a/LowFrequencyTrading/TradingProcess.js +++ b/LowFrequencyTrading/TradingProcess.js @@ -66,7 +66,6 @@ let currentTimeFrame let currentTimeFrameLabel let market = bot.market; - let botNeverRan = true; /* Context Variables */ @@ -256,6 +255,10 @@ } } + /* + We do market files first since if the simulation is run on daily files, there will be a loop to getch each daus files and we do not need + that loop to reload market files. + */ function processMarketFiles() { let n; timeFramesLoop(); @@ -523,7 +526,7 @@ } else { n = 0; if (currentTimeFrame !== undefined) { - buildCharts(); + buildCharts(advanceTime); } else { logger.write(MODULE_NAME, "[ERROR] start -> processDailyFiles -> timeFramesControlLoop -> Time Frame not Recognized. Can not continue."); callBackFunction(global.DEFAULT_FAIL_RESPONSE); @@ -532,7 +535,7 @@ } } - function buildCharts() { + function buildCharts(advanceTime) { const COMMONS = require('../Commons.js') let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE) @@ -598,8 +601,7 @@ return; } - botNeverRan = false - bot.RESUME = true // From here on, all other loops executions must resume from where we left at this current run. + bot.FIRST_EXECUTION = false // From here on, all other loops executions wont be the first execution. if (currentTimeFrame > global.dailyFilePeriods[0][0]) { writeMarketStatusReport(onMarketStatusReport) diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index 993c75edd..f15526649 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -53,9 +53,11 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { } function persistRecord() { + pruneOutputFile(product, outputDatasetArray) let record = scanRecordDefinition(product) if (product.config.saveAsObjects === true) { + /* For saving objects we need to take care of a different set of rules. */ for (let j = 0; j < product.record.properties.length; j++) { let recordProperty = product.record.properties[j] if (recordProperty.config.codeName === product.config.propertyNameThatDefinesObject) { @@ -73,26 +75,50 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { outputDatasetArray.splice(outputDatasetArray.length - 1, 1) } } - /* - We will add a record everytime that proeprty value does not match this - */ - if (propertyValue !== product.config.propertyValueThatPreventsSavingObject) { - outputDatasetArray.push(record) + if (bot.processingDailyFiles) { + /* + When dealing with Daily Files, we need to avoid to write an open object at the last 'candle' of the day, + since the object will be duplicated on the next day. How do we know we are positioned at the last candle + of the day? Easy: the end of the candle must be 1 millisecod before the next day. That happens at any + time frame. + */ + let currentDay = new Date(tradingEngine.current.candle.end.value) + let nextDay = new Date(tradingEngine.current.candle.end.value + 1) + if (currentDay.getUTCDate() !== nextDay.getUTCDate()) { + /* + We will save the object only if it is closed, becasuse we are at the last candle of the day. + */ + if (propertyValue === product.config.propertyValueThatClosesObject) { + outputDatasetArray.push(record) + } + } else { + /* + When we are not at the end of the day, we will save the object normally, like in market files. + */ + if (propertyValue !== product.config.propertyValueThatPreventsSavingObject) { + outputDatasetArray.push(record) + } + } + } + else { + /* + For Market Files we will add a record everytime that proeprty value does not match this + */ + if (propertyValue !== product.config.propertyValueThatPreventsSavingObject) { + outputDatasetArray.push(record) + } } break } } } else { - /* We add every record to the existing file. */ + /* When we are not dealing with objects, we add every record to the existing file. */ outputDatasetArray.push(record) } } } function scanRecordDefinition(product) { - if (product.config.codeName === 'Position-Objects') { - let a = 1 - } /* 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. @@ -210,4 +236,29 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { } return value } + + function pruneOutputFile(product, outputFile) { + if (outputFile.isPrunned === true) { return } + /* + When a session is resumed, we will be potentially reading output files belonging to a previous session execution. + For that reason we need to prune all the records that are beyond the current candle. We do not delete everything + because we might be resuming a stopped session, which is fine. + */ + for (let i = 0; i < outputFile.length; i++) { + let record = outputFile[i] + + for (let j = 0; j < product.record.properties.length; j++) { + let recordProperty = product.record.properties[j] + if (recordProperty.config.codeName === 'end') { + let end = record[j] + if (end >= tradingEngine.current.candle.end.value) { + outputFile.splice(i, 1) + pruneOutputFile(product, outputFile) + return + } + } + } + } + outputFile.isPrunned = true + } } \ No newline at end of file diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index 5ddc2d633..208ff2c4c 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -82,7 +82,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT initializeLoop() function initializeLoop() { - if (bot.RESUME === true && tradingEngine.last.candle !== undefined) { + if (bot.FIRST_EXECUTION === false && tradingEngine.last.candle !== undefined) { /* Estimate the Initial Candle based on the last candle saved at tradingEngine */ let firstBegin = candles[0].begin let lastBegin = tradingEngine.last.candle.begin @@ -294,7 +294,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT if (dataDependencyNode.referenceParent.config.codeName !== 'Multi-Period-Daily') { continue } let singularVariableName = dataDependencyNode.referenceParent.parentNode.config.singularVariableName let pluralVariableName = dataDependencyNode.referenceParent.parentNode.config.pluralVariableName - let currentElement = getElement(thisChart[pluralVariableName], candle, 'Daily' + '-' + mapKey + '-' + pluralVariableName) + let currentElement = getElement(thisChart[pluralVariableName], 'Daily' + '-' + mapKey + '-' + pluralVariableName) thisChart[singularVariableName] = currentElement } } diff --git a/Session.js b/Session.js index 8b362bd64..beedc4463 100644 --- a/Session.js +++ b/Session.js @@ -61,6 +61,7 @@ bot.SESSION = JSON.parse(message.event.session) bot.DEPENDENCY_FILTER = JSON.parse(message.event.dependencyFilter) bot.RESUME = message.event.resume + bot.FIRST_EXECUTION = true bot.SESSION.stop = stopSession // stop function setUpAppSchema()