Async Await implementation at Trading Process

This commit is contained in:
Luis Fernando Molina committed 2020-08-18 14:31:52 +02:00
1 parent ea047363af
commit fdbffef52e
17 files changed
+902 -905

No files matched your search

+42 -5
View File
@@ -4,7 +4,7 @@ const path = require('path')
exports.newFileStorage = function newFileStorage(logger, host, port) {
const MODULE_NAME = 'FileStorage'
const MAX_RETRY = 30
const MAX_RETRY = 10
const FAST_RETRY_TIME_IN_MILISECONDS = 500
const SLOW_RETRY_TIME_IN_MILISECONDS = 2000
@@ -17,6 +17,8 @@ exports.newFileStorage = function newFileStorage(logger, host, port) {
}
let thisObject = {
asyncGetTextFile: asyncGetTextFile,
asyncCreateTextFile: asyncCreateTextFile,
getTextFile: getTextFile,
createTextFile: createTextFile,
deleteTextFile: deleteTextFile
@@ -24,6 +26,41 @@ exports.newFileStorage = function newFileStorage(logger, host, port) {
return thisObject
async function asyncGetTextFile(filePath, noRetry, canUsePrevious) {
/* This function allows its caller to work with async / await instead of callbacks */
let promise = new Promise((resolve, reject) => {
getTextFile(filePath, onFileRead, noRetry, canUsePrevious)
function onFileRead(err, text) {
let response = {
err: err,
text: text
}
resolve(response)
}
})
return promise
}
async function asyncCreateTextFile(filePath, fileContent, keepPrevious, noTemp) {
/* This function allows its caller to work with async / await instead of callbacks */
let promise = new Promise((resolve, reject) => {
createTextFile(filePath, fileContent, onFileWriten, keepPrevious, noTemp)
function onFileWriten(err) {
let response = {
err: err
}
resolve(response)
}
})
return promise
}
function getTextFile(filePath, callBackFunction, noRetry, canUsePrevious) {
let currentRetryGetTextFile = 0
@@ -160,7 +197,7 @@ exports.newFileStorage = function newFileStorage(logger, host, port) {
}
function retry() {
console.log(new Date(), ' Retry #: ' + currentRetryGetTextFile, fileLocation)
console.log(new Date(), 'Read File Retry #: ' + currentRetryGetTextFile, fileLocation)
if (currentRetryGetTextFile < MAX_RETRY) {
currentRetryGetTextFile++
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Will try to read the file again -> Retry #: ' + currentRetryGetTextFile)
@@ -216,12 +253,12 @@ exports.newFileStorage = function newFileStorage(logger, host, port) {
*/
const fs = require('fs')
if (noTemp === true) {
fs.writeFile(fileLocation, fileContent, onFileWritenn)
fs.writeFile(fileLocation, fileContent, onFileWriten)
} else {
fs.writeFile(fileLocation + '.tmp', fileContent, onFileWritenn)
fs.writeFile(fileLocation + '.tmp', fileContent, onFileWriten)
}
function onFileWritenn(err) {
function onFileWriten(err) {
let retryTimeToUse = FAST_RETRY_TIME_IN_MILISECONDS
if (currentRetryWriteTextFile > MAX_RETRY - 2) {
retryTimeToUse = SLOW_RETRY_TIME_IN_MILISECONDS
+149 -106
View File
@@ -9,7 +9,6 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
openEpisode: openEpisode,
updateExitType: updateExitType,
closeEpisode: closeEpisode,
calculateResults: calculateResults,
calculateStatistics: calculateStatistics,
initialize: initialize,
finalize: finalize
@@ -116,116 +115,160 @@ exports.newTradingEpisode = function newTradingEpisode(bot, logger, tradingEngin
function calculateStatistics() {
/* Updating Profit Loss */
tradingEngine.current.episode.episodeStatistics.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.episode.candle.close.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
tradingEngine.current.episode.episodeStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.profitLoss.value, 10)
/* Updating ROI */
tradingEngine.current.episode.episodeStatistics.ROI.value =
(
sessionParameters.sessionBaseAsset.config.initialBalance * tradingEngine.current.episode.candle.close.value +
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.episode.candle.close.value +
sessionParameters.sessionQuotedAsset.config.initialBalance +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) / (
sessionParameters.sessionBaseAsset.config.initialBalance * tradingEngine.current.episode.candle.close.value +
calculateAssetsStatistics()
calculateEpisodeStatistics()
function calculateAssetsStatistics() {
/* Updating Hits & Fails */
if (tradingEngine.current.position.positionBaseAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeBaseAsset.hits.value++
} else {
tradingEngine.current.episode.episodeBaseAsset.fails.value++
}
if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeQuotedAsset.hits.value++
} else {
tradingEngine.current.episode.episodeQuotedAsset.fails.value++
}
/* Updating Profit Loss */
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
sessionParameters.sessionBaseAsset.config.initialBalance
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
sessionParameters.sessionQuotedAsset.config.initialBalance
) - 1
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.profitLoss.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value, 10)
/*
Updating ROI
https://www.investopedia.com/articles/basics/10/guide-to-calculating-roi.asp
*/
tradingEngine.current.episode.episodeBaseAsset.ROI.value =
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value /
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * 100
tradingEngine.current.episode.episodeQuotedAsset.ROI.value =
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value /
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value * 100
tradingEngine.current.episode.episodeBaseAsset.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.ROI.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.ROI.value, 10)
/* Updating Hit Ratio */
tradingEngine.current.episode.episodeBaseAsset.hitRatio.value =
tradingEngine.current.episode.episodeBaseAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value =
tradingEngine.current.episode.episodeQuotedAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
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
https://www.investopedia.com/terms/a/annualized-rate.asp
*/
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value =
Math.pow(
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value +
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value
) / tradingEngine.current.episode.episodeBaseAsset.beginBalance.value
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
) - 1
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value =
Math.pow(
(
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) / tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
) - 1
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value, 10)
/* Updating Hit or Fail */
if (tradingEngine.current.episode.episodeBaseAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeBaseAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.episode.episodeBaseAsset.hitFail.value = 'Fail'
}
if (tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeQuotedAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.episode.episodeQuotedAsset.hitFail.value = 'Fail'
}
}
tradingEngine.current.episode.episodeStatistics.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.ROI.value, 10)
/* Updating Annualized Rate Of Return */
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value =
tradingEngine.current.episode.episodeStatistics.ROI.value /
tradingEngine.current.episode.episodeStatistics.days.value * 365
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value, 10)
/* Updating Hit or Fail */
if (tradingEngine.current.episode.episodeStatistics.profitLoss.value > 0) {
tradingEngine.current.episode.episodeStatistics.hitFail.value = 'Hit'
} else {
tradingEngine.current.episode.episodeStatistics.hitFail.value = 'Fail'
function calculateEpisodeStatistics() {
/* Updating Profit Loss */
tradingEngine.current.episode.episodeStatistics.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.episode.candle.close.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
tradingEngine.current.episode.episodeStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.profitLoss.value, 10)
/*
Updating ROI
https://www.investopedia.com/articles/basics/10/guide-to-calculating-roi.asp
*/
tradingEngine.current.episode.episodeStatistics.ROI.value =
(
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.episode.endRate.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) / (
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
) * 100
tradingEngine.current.episode.episodeStatistics.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.ROI.value, 10)
/*
Updating Annualized Rate Of Return
https://www.investopedia.com/terms/a/annualized-rate.asp
*/
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value =
Math.pow(
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value +
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
) /
(
tradingEngine.current.episode.episodeBaseAsset.beginBalance.value * tradingEngine.current.episode.beginRate.value +
tradingEngine.current.episode.episodeQuotedAsset.beginBalance.value
)
,
(365 / tradingEngine.current.episode.episodeStatistics.days.value)
) - 1
tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeStatistics.annualizedRateOfReturn.value, 10)
/* Updating Hit or Fail */
if (tradingEngine.current.episode.episodeStatistics.profitLoss.value > 0) {
tradingEngine.current.episode.episodeStatistics.hitFail.value = 'Hit'
} else {
tradingEngine.current.episode.episodeStatistics.hitFail.value = 'Fail'
}
}
}
function calculateResults() {
/* Updating Hits & Fails */
if (tradingEngine.current.position.positionBaseAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeBaseAsset.hits.value++
} else {
tradingEngine.current.episode.episodeBaseAsset.fails.value++
}
if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeQuotedAsset.hits.value++
} else {
tradingEngine.current.episode.episodeQuotedAsset.fails.value++
}
/* Updating Profit Loss */
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
sessionParameters.sessionBaseAsset.config.initialBalance
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
sessionParameters.sessionQuotedAsset.config.initialBalance
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.profitLoss.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value, 10)
/* Updating ROI */
tradingEngine.current.episode.episodeBaseAsset.ROI.value =
(sessionParameters.sessionBaseAsset.config.initialBalance + tradingEngine.current.episode.episodeBaseAsset.profitLoss.value) /
sessionParameters.sessionBaseAsset.config.initialBalance - 1
tradingEngine.current.episode.episodeQuotedAsset.ROI.value =
(sessionParameters.sessionQuotedAsset.config.initialBalance + tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value) /
sessionParameters.sessionQuotedAsset.config.initialBalance - 1
tradingEngine.current.episode.episodeBaseAsset.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.ROI.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.ROI.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.ROI.value, 10)
/* Updating Hit Ratio */
tradingEngine.current.episode.episodeBaseAsset.hitRatio.value =
tradingEngine.current.episode.episodeBaseAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
tradingEngine.current.episode.episodeQuotedAsset.hitRatio.value =
tradingEngine.current.episode.episodeQuotedAsset.hits.value /
tradingEngine.current.episode.episodeCounters.positions.value
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 */
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value =
tradingEngine.current.episode.episodeBaseAsset.ROI.value /
tradingEngine.current.episode.episodeStatistics.days.value * 365
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value =
tradingEngine.current.episode.episodeQuotedAsset.ROI.value /
tradingEngine.current.episode.episodeStatistics.days.value * 365
tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeBaseAsset.annualizedRateOfReturn.value, 10)
tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value = global.PRECISE(tradingEngine.current.episode.episodeQuotedAsset.annualizedRateOfReturn.value, 10)
/* Updating Hit or Fail */
if (tradingEngine.current.episode.episodeBaseAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeBaseAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.episode.episodeBaseAsset.hitFail.value = 'Fail'
}
if (tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value > 0) {
tradingEngine.current.episode.episodeQuotedAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.episode.episodeQuotedAsset.hitFail.value = 'Fail'
}
}
function updateDistanceToEventsCounters() {
/* Keeping Distance Counters Up-to-date while avoinding counting before the first event happens. */
+43 -106
View File
@@ -3,7 +3,6 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL
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 FULL_LOG = true
const MODULE_NAME = 'Trading Bot'
let thisObject = {
@@ -24,21 +23,13 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL
commons = undefined
}
function initialize(callBackFunction) {
try {
logger.fileName = MODULE_NAME
logger.initialize()
function initialize() {
callBackFunction(global.DEFAULT_OK_RESPONSE)
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] initialize -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function start(chart, timeFrame, timeFrameLabel, currentDay, callBackFunction) {
async function start(chart, timeFrame, timeFrameLabel, currentDay) {
try {
bot.processingDailyFiles
if (timeFrame > global.dailyFilePeriods[0][0]) {
bot.processingDailyFiles = false
} else {
@@ -51,10 +42,6 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL
let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets
let market = bot.market
let totalFilesToBeRead = 0
let totalFilesRead = 0
let totalFilesToBeWritten = 0
let totalFilesWritten = 0
let outputDatasetsMap = new Map()
if (bot.FIRST_EXECUTION === true && bot.RESUME === false) {
@@ -63,21 +50,27 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL
*/
bot.simulationState.tradingEngine = bot.TRADING_ENGINE
bot.simulationState.tradingSystem = bot.TRADING_SYSTEM
initializeOutputs()
await initializeOutputs()
} else {
readFiles()
await readFiles()
}
await tradingSimulation.runSimulation(
chart,
outputDatasetsMap,
writeFiles
)
return
function initializeOutputs() {
async function initializeOutputs() {
if (bot.processingDailyFiles) {
initializeDailyFiles()
await initializeDailyFiles()
} else {
initializeMarketFiles()
await initializeMarketFiles()
}
}
function initializeDailyFiles() {
async function initializeDailyFiles() {
for (let i = 0; i < outputDatasets.length; i++) {
let outputDatasetNode = outputDatasets[i]
let dataset = outputDatasetNode.referenceParent
@@ -86,10 +79,9 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL
outputDatasetsMap.set(dataset.parentNode.config.codeName, [])
}
}
runSimulation()
}
function initializeMarketFiles() {
async function initializeMarketFiles() {
for (let i = 0; i < outputDatasets.length; i++) {
let outputDatasetNode = outputDatasets[i]
let dataset = outputDatasetNode.referenceParent
@@ -98,23 +90,22 @@ exports.newTradingOutput = function newTradingOutput(bot, logger, UTILITIES, FIL
outputDatasetsMap.set(dataset.parentNode.config.codeName, [])
}
}
runSimulation()
}
function readFiles() {
async function readFiles() {
/*
This bot have an output of files that it generates. At every call to the bot, it needs to read the previously generated
files in order to later append more information after the execution is over. Here in this function we are going to
read those output files and get them ready for appending content during the simulation.
*/
if (bot.processingDailyFiles) {
readDailyFiles()
await readDailyFiles()
} else {
readMarketFiles()
await readMarketFiles()
}
}
function readMarketFiles() {
async function readMarketFiles() {
for (let i = 0; i < outputDatasets.length; i++) {
let outputDatasetNode = outputDatasets[i]
let dataset = outputDatasetNode.referenceParent
@@ -124,12 +115,12 @@ 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.codeName)
await readOutputFile(fileName, filePath, dataset.parentNode.config.codeName)
}
}
}
function readDailyFiles() {
async function readDailyFiles() {
for (let i = 0; i < outputDatasets.length; i++) {
let outputDatasetNode = outputDatasets[i]
let dataset = outputDatasetNode.referenceParent
@@ -140,70 +131,38 @@ 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.codeName)
await readOutputFile(fileName, filePath, dataset.parentNode.config.codeName)
}
}
}
function readOutputFile(fileName, filePath, productName) {
totalFilesToBeRead++
async function readOutputFile(fileName, filePath, productName) {
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileRead, true)
function onFileRead(err, text) {
try {
let response = await fileStorage.asyncGetTextFile(filePath, true)
if (err.message === 'File does not exist.') {
outputDatasetsMap.set(productName, [])
anotherFileRead()
return
}
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> err = ' + err.stack)
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> filePath = ' + filePath)
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> market = ' + market.baseAsset + '_' + market.quotedAsset)
callBackFunction(err)
return
}
outputDatasetsMap.set(productName, JSON.parse(text))
anotherFileRead()
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
if (response.err.message === 'File does not exist.') {
outputDatasetsMap.set(productName, [])
return
}
}
function anotherFileRead() {
totalFilesRead++
if (totalFilesRead === totalFilesToBeRead) {
runSimulation()
if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) {
throw(response.err)
}
outputDatasetsMap.set(productName, JSON.parse(text))
}
function runSimulation() {
tradingSimulation.runSimulation(
chart,
outputDatasetsMap,
writeFiles,
callBackFunction)
}
function writeFiles() {
async function writeFiles() {
/*
The output of files which were appended with information during the simulation execution, now needs to be saved.
*/
if (bot.processingDailyFiles) {
writeDailyFiles()
await writeDailyFiles()
} else {
writeMarketFiles()
await writeMarketFiles()
}
}
function writeMarketFiles() {
async function writeMarketFiles() {
for (let i = 0; i < outputDatasets.length; i++) {
let outputDatasetNode = outputDatasets[i]
let dataset = outputDatasetNode.referenceParent
@@ -213,12 +172,12 @@ 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.codeName)
await writeOutputFile(fileName, filePath, dataset.parentNode.config.codeName)
}
}
}
function writeDailyFiles() {
async function writeDailyFiles() {
for (let i = 0; i < outputDatasets.length; i++) {
let outputDatasetNode = outputDatasets[i]
let dataset = outputDatasetNode.referenceParent
@@ -229,47 +188,25 @@ 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.codeName)
await writeOutputFile(fileName, filePath, dataset.parentNode.config.codeName)
}
}
}
function writeOutputFile(fileName, filePath, productName) {
totalFilesToBeWritten++
async function writeOutputFile(fileName, filePath, productName) {
filePath += '/' + fileName
let fileContent = JSON.stringify(outputDatasetsMap.get(productName))
fileStorage.createTextFile(filePath, fileContent, onFileCreated)
let response = await fileStorage.asyncCreateTextFile(filePath, fileContent)
function onFileCreated(err) {
try {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> err = ' + err.stack)
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> filePath = ' + filePath)
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> market = ' + market.baseAsset + '_' + market.quotedAsset)
callBackFunction(err)
return
}
anotherFileWritten()
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> readMarketFiles -> readOutputFile -> onFileRead -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
}
function anotherFileWritten() {
totalFilesWritten++
if (totalFilesWritten === totalFilesToBeWritten) {
callBackFunction(global.DEFAULT_OK_RESPONSE)
if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) {
throw(response.err)
}
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] start -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
throw(global.DEFAULT_FAIL_RESPONSE)
}
}
}
@@ -6,6 +6,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
let thisObject = {
mantain: mantain,
reset: reset,
calculateStatistics: calculateStatistics,
openPosition: openPosition,
closingPosition: closingPosition,
closePosition: closePosition,
@@ -87,9 +88,6 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
tradingEngine.current.position.positionBaseAsset.endBalance.value = tradingEngine.current.episode.episodeBaseAsset.balance.value
tradingEngine.current.position.positionQuotedAsset.endBalance.value = tradingEngine.current.episode.episodeQuotedAsset.balance.value
/* Position Statistics & Results */
updateStatistics()
updateResults()
/*
Now that the position is closed, it is the right time to move this position from current to last at the Trading Engine data structure.
*/
@@ -258,82 +256,84 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
}
}
function updateStatistics() {
function calculateStatistics(tradingEngineStageNode) {
/* Profit Loss Calculation */
tradingEngine.current.position.positionStatistics.profitLoss.value =
(
tradingEngine.current.episode.episodeBaseAsset.balance.value * tradingEngine.current.position.endRate.value +
tradingEngine.current.episode.episodeQuotedAsset.balance.value
) -
(
tradingEngine.current.position.positionBaseAsset.beginBalance * tradingEngine.current.position.beginRate.value +
calculateAssetsStatistics()
calculatePositionStatistics()
function calculateAssetsStatistics() {
/* Profit Loss Calculation */
tradingEngine.current.position.positionBaseAsset.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
tradingEngine.current.position.positionBaseAsset.beginBalance
tradingEngine.current.position.positionQuotedAsset.profitLoss.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
tradingEngine.current.position.positionQuotedAsset.beginBalance
)
tradingEngine.current.position.positionStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionStatistics.profitLoss.value, 10)
/* ROI Calculation */
tradingEngine.current.position.positionStatistics.ROI.value =
(
tradingEngine.current.position.positionStatistics.profitLoss.value
)
* 100 /
(
tradingEngine.current.position.positionBaseAsset.beginBalance * tradingEngine.current.position.beginRate.value +
tradingEngine.current.position.positionQuotedAsset.beginBalance
)
tradingEngine.current.position.positionStatistics.ROI.value = global.PRECISE(tradingEngine.current.position.positionStatistics.ROI.value, 10)
/* Hit Fail Calculation */
if (tradingEngine.current.position.positionStatistics.ROI.value > 0) {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Hit'
} else {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Fail'
tradingEngine.current.position.positionBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.profitLoss.value, 10)
tradingEngine.current.position.positionQuotedAsset.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionQuotedAsset.profitLoss.value, 10)
/* ROI Calculation */
tradingEngine.current.position.positionBaseAsset.ROI.value =
tradingEngine.current.position.positionBaseAsset.profitLoss.value * 100 /
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value
tradingEngine.current.position.positionQuotedAsset.ROI.value =
tradingEngine.current.position.positionQuotedAsset.profitLoss.value * 100 /
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled.value
tradingEngine.current.position.positionBaseAsset.ROI.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.ROI.value, 10)
tradingEngine.current.position.positionQuotedAsset.ROI.value = global.PRECISE(tradingEngine.current.position.positionQuotedAsset.ROI.value, 10)
/* Hit Fail Calculation */
if (tradingEngine.current.position.positionBaseAsset.ROI.value > 0) {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Fail'
}
if (tradingEngine.current.position.positionQuotedAsset.ROI.value > 0) {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Fail'
}
}
/* Days Calculation */
tradingEngine.current.position.positionStatistics.days.value =
tradingEngine.current.position.positionCounters.periods.value *
sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS
function calculatePositionStatistics() {
/* Profit Loss Calculation */
tradingEngine.current.position.positionStatistics.profitLoss.value =
(
tradingEngine.current.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.position.endRate.value +
tradingEngine.current.episode.episodeQuotedAsset.profitLoss.value
)
tradingEngine.current.position.positionStatistics.days.value = global.PRECISE(tradingEngine.current.position.positionStatistics.days.value, 10)
}
function updateResults() {
/* Profit Loss Calculation */
tradingEngine.current.position.positionBaseAsset.profitLoss.value =
tradingEngine.current.episode.episodeBaseAsset.balance.value -
tradingEngine.current.position.positionBaseAsset.beginBalance
tradingEngine.current.position.positionQuotedAsset.profitLoss.value =
tradingEngine.current.episode.episodeQuotedAsset.balance.value -
tradingEngine.current.position.positionQuotedAsset.beginBalance
tradingEngine.current.position.positionBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.profitLoss.value, 10)
tradingEngine.current.position.positionQuotedAsset.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionQuotedAsset.profitLoss.value, 10)
/* ROI Calculation */
tradingEngine.current.position.positionBaseAsset.ROI.value =
tradingEngine.current.position.positionBaseAsset.profitLoss.value * 100 /
tradingEngine.current.position.positionBaseAsset.entryTargetSize.value
tradingEngine.current.position.positionQuotedAsset.ROI.value =
tradingEngine.current.position.positionQuotedAsset.profitLoss.value * 100 /
tradingEngine.current.position.positionQuotedAsset.entryTargetSize.value
tradingEngine.current.position.positionBaseAsset.ROI.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.ROI.value, 10)
tradingEngine.current.position.positionQuotedAsset.ROI.value = global.PRECISE(tradingEngine.current.position.positionQuotedAsset.ROI.value, 10)
/* Hit Fail Calculation */
if (tradingEngine.current.position.positionBaseAsset.ROI.value > 0) {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.position.positionBaseAsset.hitFail.value = 'Fail'
}
if (tradingEngine.current.position.positionQuotedAsset.ROI.value > 0) {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Hit'
} else {
tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'Fail'
tradingEngine.current.position.positionStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionStatistics.profitLoss.value, 10)
/* ROI Calculation */
tradingEngine.current.position.positionStatistics.ROI.value =
(
tradingEngine.current.position.positionStatistics.profitLoss.value
)
* 100 /
(
tradingEngine.current.position.positionBaseAsset.beginBalance * tradingEngine.current.position.beginRate.value +
tradingEngine.current.position.positionQuotedAsset.beginBalance
)
tradingEngine.current.position.positionStatistics.ROI.value = global.PRECISE(tradingEngine.current.position.positionStatistics.ROI.value, 10)
/* Hit Fail Calculation */
if (tradingEngine.current.position.positionStatistics.ROI.value > 0) {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Hit'
} else {
tradingEngine.current.position.positionStatistics.hitFail.value = 'Fail'
}
/* Days Calculation */
tradingEngine.current.position.positionStatistics.days.value =
tradingEngine.current.position.positionCounters.periods.value *
sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS
tradingEngine.current.position.positionStatistics.days.value = global.PRECISE(tradingEngine.current.position.positionStatistics.days.value, 10)
}
}
File diff suppressed because it is too large. Load diff
@@ -19,11 +19,11 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
thisObject = undefined
}
function runSimulation(
async function runSimulation(
chart,
outputDatasetsMap,
callback,
callBackFunction) {
writeFiles,
) {
try {
let tradingSystem = bot.simulationState.tradingSystem
@@ -58,51 +58,54 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
let heartBeatDate
let previousHeartBeatDate
let firstLoopExecution = true
/*
Loop Initialization
*/
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
let diff = lastBegin - firstBegin
let amount = diff / sessionParameters.timeFrame.config.value
initializeLoop()
tradingEngine.current.episode.candle.index.value = Math.trunc(amount) + 1 // Because we need to start from the next candle
if (tradingEngine.current.episode.candle.index.value < 0 || tradingEngine.current.episode.candle.index.value > candles.length - 1) {
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> Cannot resume simulation.')
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> tradingEngine.current.episode.candle.index.value = ' + tradingEngine.current.episode.candle.index.value)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> firstBegin = ' + firstBegin)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> lastBegin = ' + lastBegin)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> diff = ' + diff)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> amount = ' + amount)
function initializeLoop() {
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
let diff = lastBegin - firstBegin
let amount = diff / sessionParameters.timeFrame.config.value
tradingEngine.current.episode.candle.index.value = Math.trunc(amount) + 1 // Because we need to start from the next candle
if (tradingEngine.current.episode.candle.index.value < 0 || tradingEngine.current.episode.candle.index.value > candles.length - 1) {
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> Cannot resume simulation.')
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> tradingEngine.current.episode.candle.index.value = ' + tradingEngine.current.episode.candle.index.value)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> firstBegin = ' + firstBegin)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> lastBegin = ' + lastBegin)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> diff = ' + diff)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> initializeLoop -> amount = ' + amount)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
} else {
/* Estimate Initial Candle based on the timeRage configured for the session. */
let firstEnd = candles[0].end
let targetEnd = sessionParameters.timeRange.config.initialDatetime
let diff = targetEnd - firstEnd
let amount = diff / sessionParameters.timeFrame.config.value
tradingEngine.current.episode.candle.index.value = Math.trunc(amount)
if (tradingEngine.current.episode.candle.index.value < 0) { tradingEngine.current.episode.candle.index.value = 0 }
if (tradingEngine.current.episode.candle.index.value > candles.length - 1) {
/*
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.
*/
tradingEngine.current.episode.candle.index.value = candles.length - 1
}
throw(global.DEFAULT_FAIL_RESPONSE)
return
}
} else {
/* Estimate Initial Candle based on the timeRage configured for the session. */
let firstEnd = candles[0].end
let targetEnd = sessionParameters.timeRange.config.initialDatetime
let diff = targetEnd - firstEnd
let amount = diff / sessionParameters.timeFrame.config.value
loop()
tradingEngine.current.episode.candle.index.value = Math.trunc(amount)
if (tradingEngine.current.episode.candle.index.value < 0) { tradingEngine.current.episode.candle.index.value = 0 }
if (tradingEngine.current.episode.candle.index.value > candles.length - 1) {
/*
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.
*/
tradingEngine.current.episode.candle.index.value = candles.length - 1
}
}
async function loop() {
/*
Main Simulation Loop
*/
let breakLoop = false
for( let i = tradingEngine.current.episode.candle.index.value; i < candles.length; i++) {
tradingEngine.current.episode.candle.index.value = i
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Processing candle # ' + tradingEngine.current.episode.candle.index.value) }
let candle = candles[tradingEngine.current.episode.candle.index.value] // This is the current candle the Simulation is working at.
@@ -119,19 +122,8 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
firstLoopExecution = false
}
if (checkLastCandle() === false) {
closeEpisode('Last Candle Reached')
return
}
if (checkMinimunAndMaximunBalance() === false) {
closeEpisode('Min or Max Balance Reached')
return
}
if (checkInitialAndFinalDatetime() === false) {
controlLoop()
return
if (checkInitialDatetime() === false) {
continue
}
positionChartAtCurrentCandle()
@@ -178,75 +170,61 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
tradingEngineModule.setCurrentCycle('Second')
await tradingSystemModule.run()
controlLoop()
}
function controlLoop() {
/* Checking if we should continue processing this loop or not. */
if (bot.STOP_SESSION === true) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing this session.') }
console.log('[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing this session.')
updateEpisode('Session Stopped')
return
breakLoop = true
}
if (global.STOP_TASK_GRACEFULLY === true) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing this task.') }
console.log('[INFO] runSimulation -> controlLoop -> We are going to stop here bacause we were requested to stop processing this task.')
updateEpisode('Task Stopped')
return
breakLoop = true
}
if (tradingEngine.current.episode.candle.index.value + 1 < candles.length) {
/* Add new records to the process output */
tradingRecordsModule.appendRecords('Second')
tradingEngine.current.episode.candle.index.value++
/*
This will execute the next loop in the next iteration of the NodeJs event loop
allowing for other callbacks to be executed. It also prevents the error
'Maximum call stack size exceeded', since the call is not placed at the call stack.
*/
setImmediate(loop)
} else {
updateEpisode('All Candles Processed')
if (checkFinalDatetime() === false) {
closeEpisode('Final Datetime Reached')
breakLoop = true
}
if (checkLastCandle() === false) {
updateEpisode('All Available Candles Processed')
breakLoop = true
}
if (checkMinimunAndMaximunBalance() === false) {
closeEpisode('Min or Max Balance Reached')
breakLoop = true
}
/* Add new records to the process output */
tradingRecordsModule.appendRecords('Second')
if (breakLoop === true) {break}
}
tradingEngineModule.finalize()
tradingSystemModule.finalize()
tradingRecordsModule.finalize()
tradingEpisodeModule.finalize()
tradingEngineModule = undefined
tradingSystemModule = undefined
tradingRecordsModule = undefined
tradingEpisodeModule = undefined
await writeFiles()
function closeEpisode(exitType) {
tradingEpisodeModule.updateExitType(exitType)
tradingEpisodeModule.closeEpisode()
/* Add new records to the process output */
tradingRecordsModule.appendRecords('Second')
afterLoop()
}
function updateEpisode(exitType) {
tradingEpisodeModule.updateExitType(exitType)
/* Add new records to the process output */
tradingRecordsModule.appendRecords('Second')
afterLoop()
}
function afterLoop() {
tradingEngineModule.finalize()
tradingSystemModule.finalize()
tradingRecordsModule.finalize()
tradingEpisodeModule.finalize()
tradingEngineModule = undefined
tradingSystemModule = undefined
tradingRecordsModule = undefined
tradingEpisodeModule = undefined
callback()
}
function heartBeat() {
@@ -368,7 +346,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] runSimulation -> getElement -> datasetName = ' + datasetName)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> getElement -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
throw(global.DEFAULT_FAIL_RESPONSE)
}
}
@@ -379,7 +357,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
*/
if (bot.processingDailyFiles) { // We are processing Daily Files
let candlesPerDay = global.ONE_DAY_IN_MILISECONDS / sessionParameters.timeFrame.config.value
if (tradingEngine.current.episode.candle.index.value === candles.length - 1) {
if (tradingEngine.current.episode.candle.index.value + 1 === candles.length - 1) {
if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkLastCandle -> Skipping Candle because it is the last one and has not been closed yet.') }
return false
@@ -390,7 +368,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
}
}
} else { // We are processing Market Files
if (tradingEngine.current.episode.candle.index.value === candles.length - 1) {
if (tradingEngine.current.episode.candle.index.value + 1 === candles.length - 1) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkLastCandle -> Skipping Candle because it is the last one and has not been closed yet.') }
return false
}
@@ -398,13 +376,18 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
return true
}
function checkInitialAndFinalDatetime() {
/* Here we check that the current candle is not outside of user-defined time range at the session parameters.*/
function checkInitialDatetime() {
/* Here we check that the current candle is not before the initial datetime defined at the session parameters.*/
if (tradingEngine.current.episode.candle.end.value < sessionParameters.timeRange.config.initialDatetime) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkInitialAndFinalDatetime -> Skipping Candle before the sessionParameters.timeRange.config.initialDatetime.') }
return false
}
if (tradingEngine.current.episode.candle.begin.value > sessionParameters.timeRange.config.finalDatetime) {
return true
}
function checkFinalDatetime() {
/* Here we check that the next candle is not after of the user-defined final datetime at the session parameters.*/
if (tradingEngine.current.episode.candle.begin.value + sessionParameters.timeFrame.config.value > sessionParameters.timeRange.config.finalDatetime) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkInitialAndFinalDatetime -> Skipping Candle after the sessionParameters.timeRange.config.finalDatetime.') }
return false
}
@@ -464,7 +447,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
}
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] runSimulation -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
throw(global.DEFAULT_FAIL_RESPONSE)
}
}
}
@@ -7,6 +7,7 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
updateChart: updateChart,
mantain: mantain,
reset: reset,
calculateStatistics: calculateStatistics,
runTriggerStage: runTriggerStage,
runOpenStage: runOpenStage,
runManageStage: runManageStage,
@@ -101,6 +102,11 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
resetTradingEngineDataStructure()
}
function calculateStatistics() {
tradingEpisodeModule.calculateStatistics()
tradingPositionModule.calculateStatistics()
}
function runTriggerStage() {
/*
We check if we will be triggering on, off or taking position.
@@ -870,9 +876,6 @@ exports.newTradingStages = function newTradingStages(bot, logger, tradingEngineM
/* Close the Position */
tradingPositionModule.closePosition()
tradingEpisodeModule.calculateResults()
tradingEpisodeModule.calculateStatistics()
/* Close the Strategy */
tradingStrategyModule.closeStrategy('Position Closed')
@@ -131,6 +131,9 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
/* Run the Close Stage */
await tradingStagesModule.runCloseStage()
tradingStagesModule.calculateStatistics()
} catch (err) {
/*
If an error ocurred during execution, it was alreeady logged and
+1 -1
View File
@@ -490,7 +490,7 @@
function writeDataRange(pBegin, pEnd, productCodeName, callBack) {
let dataRange = {
begin: pBegin.valueOf(),
end: pEnd.valueOf()
end: pEnd.valueOf() + ONE_DAY_IN_MILISECONDS
};
let fileContent = JSON.stringify(dataRange)
let fileName = '/Data.Range.json';
+68 -1
View File
@@ -9,7 +9,8 @@
let bot = BOT;
let thisObject = {
raiseEvents, raiseEvents
raiseEvents: raiseEvents,
asyncRaiseEvents: asyncRaiseEvents
};
return thisObject;
@@ -99,4 +100,70 @@
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function asyncRaiseEvents(lastFile, timeFrames) {
if (bot.processNode.referenceParent.processOutput !== undefined) {
if (bot.processNode.referenceParent.processOutput.outputDatasets !== undefined) {
let outputDatasets = bot.processNode.referenceParent.processOutput.outputDatasets
for (let j = 0; j < outputDatasets.length; j++) {
let outputDataset = outputDatasets[j]
/* Some validations to verify that everything is in place. */
if (outputDataset.referenceParent !== undefined) {
if (outputDataset.referenceParent.config.codeName === undefined) {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Dataset Definition not defined. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent));
throw(global.DEFAULT_FAIL_RESPONSE);
}
if (outputDataset.referenceParent.parentNode !== undefined) {
if (outputDataset.referenceParent.parentNode.config.codeName === undefined) {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Product not defined. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode));
throw(global.DEFAULT_FAIL_RESPONSE);
}
if (outputDataset.referenceParent.parentNode.parentNode !== undefined) {
if (outputDataset.referenceParent.parentNode.parentNode.config.codeName === undefined) {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Bot not defined. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode));
throw(global.DEFAULT_FAIL_RESPONSE);
}
if (outputDataset.referenceParent.parentNode.parentNode.parentNode !== undefined) {
if (outputDataset.referenceParent.parentNode.parentNode.parentNode.config.codeName === undefined) {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> codeName at Data Mine not defined. Data Mine = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode.parentNode));
throw(global.DEFAULT_FAIL_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Bot not attached to a Data Mine. Bot = " + JSON.stringify(outputDataset.referenceParent.parentNode.parentNode));
throw(global.DEFAULT_FAIL_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Product not attached to a Bot. Product = " + JSON.stringify(outputDataset.referenceParent.parentNode));
throw(global.DEFAULT_FAIL_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Dataset Definition not attached to a Product. Dataset Definition = " + JSON.stringify(outputDataset.referenceParent));
throw(global.DEFAULT_FAIL_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] raiseEvents -> Output Dataset with no reference to a Dataset Definition. Output Dataset = " + JSON.stringify(outputDataset));
throw(global.DEFAULT_FAIL_RESPONSE);
}
/* All good, lets emit the event. */
let dataMine = outputDataset.referenceParent.parentNode.parentNode.parentNode.config.codeName
let botName = outputDataset.referenceParent.parentNode.parentNode.config.codeName
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 event = {
lastFile: lastFile,
timeFrames: timeFrames
}
global.EVENT_SERVER_CLIENT.createEventHandler(key, 'Dataset Updated')
global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Dataset Updated', event)
}
}
}
}
};
+16 -10
View File
@@ -22,9 +22,9 @@
/* Set the folderName for early logging */
if (bot.processNode.session.config.folderName === undefined) {
bot.SESSION.folderName = bot.SESSION.id
bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id
} else {
bot.SESSION.folderName = bot.processNode.session.config.folderName + "-" + bot.SESSION.id
bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName
}
/* Check if there is a session */
@@ -125,14 +125,18 @@
}
function setUpSessionFolderName() {
/* Set the folderName for logging, reports, context and data output */
/*
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.SESSION.id
bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.SESSION.id
} else {
bot.SESSION.folderName = config.folderName + "-" + bot.SESSION.id
bot.SESSION.folderName = bot.processNode.session.type.replace(' ', '-').replace(' ', '-') + '-' + bot.processNode.session.config.folderName
}
}
}
@@ -162,11 +166,13 @@
}
} else {
/* Check that we received valid dates */
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 (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."
+20 -1
View File
@@ -20,6 +20,7 @@
finalize: finalize,
load: load,
save: save,
asyncSave: asyncSave,
status: undefined
};
@@ -343,4 +344,22 @@
}
}
};
async function asyncSave() {
let fileName = "Status.Report.json";
let filePath = bot.filePathRoot + "/Reports/" + sessionPath + statusDependencyNode.process;
filePath += '/' + fileName
let fileContent = JSON.stringify(thisObject.file);
let response = await fileStorage.asyncCreateTextFile(filePath, fileContent + '\n', true)
if (response.err.result !== global.DEFAULT_OK_RESPONSE.result) {
throw(response.err)
}
/* All good, lets emit the event that means data has been updated. */
let processOutput = PROCESS_OUTPUT.newProcessOutput(bot, logger)
processOutput.asyncRaiseEvents(thisObject.file.lastFile, thisObject.file.timeFrames)
}
}