Overall Simulation Structure done
This commit is contained in:
1 parent
f0b58645ff
commit
f8eb06b486
2 files changed
+203
-206
No files matched your search
+13
-5
@@ -11,8 +11,17 @@ exports.newSnapshots = function newSnapshots(bot, logger) {
|
||||
|
||||
let tradingEngine
|
||||
|
||||
/* We are going to take snapshots of the values of indicators at key moments of the sumulation. */
|
||||
|
||||
/* Snapshots of Trigger On and Take Positions */
|
||||
let snapshots = {
|
||||
headers: undefined,
|
||||
triggerOn: [],
|
||||
takePosition: [],
|
||||
lastTriggerOn: undefined,
|
||||
lastTakePosition: undefined
|
||||
}
|
||||
|
||||
/* We are going to take snapshots of the values of indicators at key moments of the sumulation. */
|
||||
let snapshotKeys = new Map()
|
||||
snapshotLoopHeaders = []
|
||||
snapshotDataRecord = []
|
||||
@@ -68,10 +77,9 @@ exports.newSnapshots = function newSnapshots(bot, logger) {
|
||||
}
|
||||
|
||||
|
||||
if (positionedAtYesterday === false) {
|
||||
snapshots.triggerOn.push(closeValues.concat(snapshots.lastTriggerOn))
|
||||
snapshots.takePosition.push(closeValues.concat(snapshots.lastTakePosition))
|
||||
}
|
||||
snapshots.triggerOn.push(closeValues.concat(snapshots.lastTriggerOn))
|
||||
snapshots.takePosition.push(closeValues.concat(snapshots.lastTakePosition))
|
||||
|
||||
snapshots.lastTriggerOn = undefined
|
||||
snapshots.lastTakePosition = undefined
|
||||
addToSnapshots = false
|
||||
|
||||
+190
-201
@@ -42,15 +42,6 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
processingDailyFiles = true
|
||||
}
|
||||
|
||||
/* Snapshots of Trigger On and Take Positions */
|
||||
let snapshots = {
|
||||
headers: undefined,
|
||||
triggerOn: [],
|
||||
takePosition: [],
|
||||
lastTriggerOn: undefined,
|
||||
lastTakePosition: undefined
|
||||
}
|
||||
|
||||
let tradingSystem = bot.TRADING_SYSTEM
|
||||
let tradingEngine = bot.TRADING_ENGINE
|
||||
let sessionParameters = bot.SESSION.parameters
|
||||
@@ -61,12 +52,10 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
let timerToCloseStage = 0
|
||||
|
||||
/* Stop Loss Management */
|
||||
|
||||
const MIN_STOP_LOSS_VALUE = 0.0000000001 // We can not let the stop be zero to avoid division by 0 error or infinity numbers as a result.
|
||||
const MAX_STOP_LOSS_VALUE = Number.MAX_SAFE_INTEGER
|
||||
|
||||
/* Take Profit Management */
|
||||
|
||||
const MIN_TAKE_PROFIT_VALUE = 0.0000000001 // We can not let the buy order be zero to avoid division by 0 error or infinity numbers as a result.
|
||||
const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER
|
||||
|
||||
@@ -80,6 +69,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
lastInstantOfTheDay = currentDay.valueOf() + ONE_DAY_IN_MILISECONDS - 1
|
||||
}
|
||||
|
||||
/* These are the Modules we will need to run the Simulation */
|
||||
const TRADING_ENGINE_MODULE = require('./TradingEngine.js')
|
||||
let tradingEngineModule = TRADING_ENGINE_MODULE.newTradingEngine(bot, logger)
|
||||
tradingEngineModule.initialize()
|
||||
@@ -104,17 +94,16 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
let tradingSystemModule = TRADING_SYSTEM_MODULE.newTradingSystem(bot, logger)
|
||||
tradingSystemModule.initialize()
|
||||
|
||||
/* Main Array and Maps */
|
||||
/* Setting up the candles array: The whole simulation is based on the array of candles at the time-frame defined at the session parameters. */
|
||||
let propertyName = 'at' + sessionParameters.timeFrame.config.label.replace('-', '')
|
||||
let candles = chart[propertyName].candles
|
||||
let currentChart = chart[propertyName]
|
||||
|
||||
/* Last Candle */
|
||||
let lastCandle = candles[candles.length - 1]
|
||||
let lastCandle = candles[candles.length - 1] // TODO Maybe remove this
|
||||
|
||||
/* For Loop Level heartbeat */
|
||||
let loopingDay
|
||||
let previousLoopingDay
|
||||
/* Variable needed for heartbeat functionality */
|
||||
let heartBeatDate
|
||||
let previousheartBeatDate
|
||||
|
||||
initializeLoop()
|
||||
|
||||
@@ -162,191 +151,38 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
}
|
||||
|
||||
function loop() {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Entering function.') }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Processing candle # ' + tradingEngine.current.candle.index.value) }
|
||||
|
||||
|
||||
let candle = candles[tradingEngine.current.candle.index.value]
|
||||
|
||||
tradingEngineModule.setCurrentCandle(candle)
|
||||
|
||||
/* Not processing while out of user-defined time range */
|
||||
if (candle.end < sessionParameters.timeRange.config.initialDatetime.valueOf()) {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle before the sessionParameters.timeRange.config.initialDatetime.') }
|
||||
controlLoop()
|
||||
return
|
||||
}
|
||||
if (candle.begin > sessionParameters.timeRange.config.finalDatetime.valueOf()) {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle after the sessionParameters.timeRange.config.finalDatetime.') }
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
|
||||
/* Here we update at the chart data structure the objects for each product representing where we are currently standing at the simulation loop */
|
||||
if (processingDailyFiles) {
|
||||
for (let j = 0; j < global.dailyFilePeriods.length; j++) {
|
||||
let mapKey = dailyFilePeriods[j][1]
|
||||
let propertyName = 'at' + mapKey.replace('-', '')
|
||||
let thisChart = chart[propertyName]
|
||||
|
||||
for (let k = 0; k < dataDependencies.length; k++) {
|
||||
let dataDependencyNode = dataDependencies[k]
|
||||
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)
|
||||
thisChart[singularVariableName] = currentElement
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finding the Current Element on Market Files */
|
||||
for (let j = 0; j < global.marketFilesPeriods.length; j++) {
|
||||
let mapKey = marketFilesPeriods[j][1]
|
||||
let propertyName = 'at' + mapKey.replace('-', '')
|
||||
let thisChart = chart[propertyName]
|
||||
|
||||
for (let k = 0; k < dataDependencies.length; k++) {
|
||||
let dataDependencyNode = dataDependencies[k]
|
||||
if (dataDependencyNode.referenceParent.config.codeName !== 'Multi-Period-Market') { continue }
|
||||
let singularVariableName = dataDependencyNode.referenceParent.parentNode.config.singularVariableName
|
||||
let pluralVariableName = dataDependencyNode.referenceParent.parentNode.config.pluralVariableName
|
||||
let currentElement = getElement(thisChart[pluralVariableName], candle, 'Market' + '-' + mapKey + '-' + pluralVariableName)
|
||||
thisChart[singularVariableName] = currentElement
|
||||
}
|
||||
}
|
||||
|
||||
/* Finding the Current Element on Single Files */
|
||||
function isItInside(elementWithTimestamp, elementWithBeginEnd) {
|
||||
if (elementWithTimestamp.timestamp >= elementWithBeginEnd.begin && elementWithTimestamp.timestamp <= elementWithBeginEnd.end) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let propertyName = 'atAnyTimeFrame'
|
||||
let thisChart = chart[propertyName]
|
||||
|
||||
for (let k = 0; k < dataDependencies.length; k++) {
|
||||
let dataDependencyNode = dataDependencies[k]
|
||||
if (dataDependencyNode.referenceParent.config.codeName !== 'Single-File') { continue }
|
||||
let singularVariableName = dataDependencyNode.referenceParent.parentNode.config.singularVariableName
|
||||
let pluralVariableName = dataDependencyNode.referenceParent.parentNode.config.pluralVariableName
|
||||
let elementArray = thisChart[pluralVariableName]
|
||||
let currentElement
|
||||
if (elementArray !== undefined) {
|
||||
currentElement = elementArray[elementArray.length - 1]
|
||||
}
|
||||
thisChart[singularVariableName] = currentElement
|
||||
}
|
||||
|
||||
/* While we are processing the previous day. */
|
||||
let positionedAtYesterday = false
|
||||
if (currentDay) {
|
||||
positionedAtYesterday = (candle.end < currentDay.valueOf())
|
||||
}
|
||||
let candle = candles[tradingEngine.current.candle.index.value] // This is the current candle the Simulation is working at.
|
||||
|
||||
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()) }
|
||||
|
||||
let ticker = {
|
||||
bid: candle.close,
|
||||
ask: candle.close,
|
||||
last: candle.close
|
||||
if (checkIfWeAreAtTheHeadOfTheMarket() === false) {
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
|
||||
/* We will produce a simulation level heartbeat in order to inform the user this is running. */
|
||||
loopingDay = new Date(Math.trunc(candle.begin / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS)
|
||||
if (loopingDay.valueOf() !== previousLoopingDay) {
|
||||
let processingDate = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2)
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Simulation ' + bot.sessionKey + ' Loop # ' + tradingEngine.current.candle.index.value + ' @ ' + processingDate) }
|
||||
|
||||
/* Telling the world we are alive and doing well */
|
||||
let fromDate = new Date(sessionParameters.timeRange.config.initialDatetime.valueOf())
|
||||
let lastDate = new Date(sessionParameters.timeRange.config.finalDatetime.valueOf())
|
||||
|
||||
let currentDateString = loopingDay.getUTCFullYear() + '-' + utilities.pad(loopingDay.getUTCMonth() + 1, 2) + '-' + utilities.pad(loopingDay.getUTCDate(), 2)
|
||||
let currentDate = new Date(loopingDay)
|
||||
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
|
||||
bot.processHeartBeat(currentDateString, percentage)
|
||||
|
||||
if (global.areEqualDates(currentDate, new Date()) === false) {
|
||||
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage)
|
||||
}
|
||||
}
|
||||
previousLoopingDay = loopingDay.valueOf()
|
||||
|
||||
tradingEngine.episode.episodeCounters.periods++
|
||||
tradingEngine.episode.episodeStatistics.days = tradingEngine.episode.episodeCounters.periods * sessionParameters.timeFrame.config.value / ONE_DAY_IN_MILISECONDS
|
||||
|
||||
if (processingDailyFiles) {
|
||||
/* We skip the candle at the head of the market because tradingEngine.current.candle.index.value has not closed yet. */
|
||||
let candlesPerDay = ONE_DAY_IN_MILISECONDS / sessionParameters.timeFrame.config.value
|
||||
if (tradingEngine.current.candle.index.value === candles.length - 1) {
|
||||
if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) {
|
||||
/* We are at the head of the market, thus we skip the last candle because it has not close yet. */
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet.') }
|
||||
controlLoop()
|
||||
return
|
||||
/* Note here that in the last candle of the first day or the second day it will use an incomplete candle and partially calculated indicators.
|
||||
if we skip these two tradingEngine.episode.episodeCounters.periods, then there will be a hole in the file since the last period will be missing. */
|
||||
}
|
||||
}
|
||||
} else { // We are processing Market Files
|
||||
if (tradingEngine.current.candle.index.value === candles.length - 1) {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Skipping Candle because it is the last one and has not been closed yet.') }
|
||||
controlLoop()
|
||||
return
|
||||
}
|
||||
if (checkInitialAndFinalDatetime() === false) {
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
|
||||
if (checkMinimunAndMaximunBalance() === false) {
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
heartBeat()
|
||||
|
||||
tradingEngineModule.setCurrentCandle(candle) // We move the current candle we are standing at, to the trading engine data structure to make it available to anyone, including conditions and formulas.
|
||||
positionChartAtCurrentCandle()
|
||||
|
||||
tradingEngineModule.updateEpisodeCountersAndStatistics()
|
||||
tradingSystemModule.setChart(chart)
|
||||
tradingSystemModule.evalConditions()
|
||||
tradingSystemModule.evalFormulas()
|
||||
|
||||
|
||||
|
||||
/* Checks for Minimun and Maximun Balance. We do the check while not inside any strategy only. */
|
||||
if (
|
||||
tradingEngine.current.strategy.stageType.value === 'No Stage' &&
|
||||
tradingEngine.current.strategy.index.value === -1
|
||||
) {
|
||||
/*
|
||||
First thing to do is to check if we are below the minimum balance, or above the maximun balance.
|
||||
If we are, we are not gooing to trigger on again.
|
||||
*/
|
||||
let minimumBalance
|
||||
let maximumBalance
|
||||
let balance
|
||||
|
||||
if (sessionParameters.sessionBaseAsset.name === bot.market.marketBaseAsset) {
|
||||
balance = tradingEngine.current.balance.baseAsset.value
|
||||
minimumBalance = sessionParameters.sessionBaseAsset.config.minimumBalance
|
||||
maximumBalance = sessionParameters.sessionBaseAsset.config.maximumBalance
|
||||
} else {
|
||||
balance = tradingEngine.current.balance.quotedAsset.value
|
||||
minimumBalance = sessionParameters.sessionQuotedAsset.config.minimumBalance
|
||||
maximumBalance = sessionParameters.sessionQuotedAsset.config.maximumBalance
|
||||
}
|
||||
|
||||
let stopRunningDate = new Date(candle.begin)
|
||||
if (balance <= minimumBalance) {
|
||||
tradingSystem.error = 'Min Balance @ ' + stopRunningDate.toLocaleString()
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> ' + tradingSystem.error) }
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
|
||||
if (balance >= maximumBalance) {
|
||||
tradingSystem.error = 'Max Balance @ ' + stopRunningDate.toLocaleString()
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> ' + tradingSystem.error) }
|
||||
afterLoop()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tradingSystemModule.checkTriggerOn()
|
||||
tradingSystemModule.checkTriggerOff()
|
||||
takePositionNow = tradingSystemModule.checkTakePosition()
|
||||
@@ -369,8 +205,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
tradingSystemModule.calculateTakeProfit()
|
||||
}
|
||||
|
||||
|
||||
tradingEngineModule.updatePositionCounters()
|
||||
tradingEngineModule.updatePositionCountersAndStatistics()
|
||||
tradingEngineModule.updateDistanceToEventsCounters()
|
||||
|
||||
/* Checking if Stop or Take Profit were hit */
|
||||
@@ -381,8 +216,6 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
closePositionNow = tradingSystemModule.checkStopLossOrTakeProfitWasHit()
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Taking a Position */
|
||||
if (
|
||||
takePositionNow === true
|
||||
@@ -403,7 +236,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
tradingEngineModule.closePosition()
|
||||
}
|
||||
|
||||
/* Closing the Closing Stage */
|
||||
/* Closing the Closing Stage TODO This should die once we have execution working. */
|
||||
if (tradingEngine.current.strategy.stageType.value === 'Close Stage') {
|
||||
if (candle.begin - 5 * 60 * 1000 > timerToCloseStage) {
|
||||
tradingEngine.current.strategy.end.value = candle.end
|
||||
@@ -427,13 +260,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
tradingRecordsModule.addConditionsRecord()
|
||||
tradingRecordsModule.addStrategyRecord()
|
||||
tradingRecordsModule.addPositionRecord()
|
||||
|
||||
/* We will remmember the last candle processed, so that if it is the last one of this run we can use it to continue from there next time. */
|
||||
tradingEngine.last.candle.begin = candle.begin
|
||||
tradingEngine.last.candle.end = candle.end
|
||||
|
||||
/* After everything at the simulation level was done, we will do the annoucements that are pending. */
|
||||
annoucementsModule.makeAnnoucements()
|
||||
annoucementsModule.makeAnnoucements() // After everything at the simulation level was done, we will do the annoucements that are pending.
|
||||
|
||||
controlLoop()
|
||||
}
|
||||
@@ -515,6 +342,168 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
function heartBeat() {
|
||||
/* We will produce a simulation level heartbeat in order to inform the user this is running. */
|
||||
heartBeatDate = new Date(Math.trunc(candle.begin / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS)
|
||||
if (heartBeatDate.valueOf() !== previousheartBeatDate) {
|
||||
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.candle.index.value + ' @ ' + processingDate) }
|
||||
|
||||
/* Telling the world we are alive and doing well */
|
||||
let fromDate = new Date(sessionParameters.timeRange.config.initialDatetime.valueOf())
|
||||
let lastDate = new Date(sessionParameters.timeRange.config.finalDatetime.valueOf())
|
||||
|
||||
let currentDateString = heartBeatDate.getUTCFullYear() + '-' + utilities.pad(heartBeatDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(heartBeatDate.getUTCDate(), 2)
|
||||
let currentDate = new Date(heartBeatDate)
|
||||
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
|
||||
bot.processHeartBeat(currentDateString, percentage)
|
||||
|
||||
if (global.areEqualDates(currentDate, new Date()) === false) {
|
||||
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage)
|
||||
}
|
||||
}
|
||||
previousheartBeatDate = heartBeatDate.valueOf()
|
||||
}
|
||||
|
||||
function positionChartAtCurrentCandle() {
|
||||
/*
|
||||
In conditions and Formulas, we want users to have an easy sintax to refer to indicators. In order to achieve that, we need the user to have
|
||||
easy access to the current candle for instance, or the current bollinger band, meaning the one the Simulation is currently standing at.
|
||||
For that reason we do the following processing, to have at the chart data structure the current objects of each indicator / time frame.
|
||||
*/
|
||||
|
||||
/* Finding the Current Element on Market Files */
|
||||
if (processingDailyFiles) {
|
||||
for (let j = 0; j < global.dailyFilePeriods.length; j++) {
|
||||
let mapKey = dailyFilePeriods[j][1]
|
||||
let propertyName = 'at' + mapKey.replace('-', '')
|
||||
let thisChart = chart[propertyName]
|
||||
|
||||
for (let k = 0; k < dataDependencies.length; k++) {
|
||||
let dataDependencyNode = dataDependencies[k]
|
||||
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)
|
||||
thisChart[singularVariableName] = currentElement
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finding the Current Element on Market Files */
|
||||
for (let j = 0; j < global.marketFilesPeriods.length; j++) {
|
||||
let mapKey = marketFilesPeriods[j][1]
|
||||
let propertyName = 'at' + mapKey.replace('-', '')
|
||||
let thisChart = chart[propertyName]
|
||||
|
||||
for (let k = 0; k < dataDependencies.length; k++) {
|
||||
let dataDependencyNode = dataDependencies[k]
|
||||
if (dataDependencyNode.referenceParent.config.codeName !== 'Multi-Period-Market') { continue }
|
||||
let singularVariableName = dataDependencyNode.referenceParent.parentNode.config.singularVariableName
|
||||
let pluralVariableName = dataDependencyNode.referenceParent.parentNode.config.pluralVariableName
|
||||
let currentElement = getElement(thisChart[pluralVariableName], candle, 'Market' + '-' + mapKey + '-' + pluralVariableName)
|
||||
thisChart[singularVariableName] = currentElement
|
||||
}
|
||||
}
|
||||
|
||||
/* Finding the Current Element At Single Files*/
|
||||
let propertyName = 'atAnyTimeFrame'
|
||||
let thisChart = chart[propertyName]
|
||||
|
||||
for (let k = 0; k < dataDependencies.length; k++) {
|
||||
let dataDependencyNode = dataDependencies[k]
|
||||
if (dataDependencyNode.referenceParent.config.codeName !== 'Single-File') { continue }
|
||||
let singularVariableName = dataDependencyNode.referenceParent.parentNode.config.singularVariableName
|
||||
let pluralVariableName = dataDependencyNode.referenceParent.parentNode.config.pluralVariableName
|
||||
let elementArray = thisChart[pluralVariableName]
|
||||
let currentElement
|
||||
if (elementArray !== undefined) {
|
||||
currentElement = elementArray[elementArray.length - 1]
|
||||
}
|
||||
thisChart[singularVariableName] = currentElement
|
||||
}
|
||||
}
|
||||
|
||||
function checkIfWeAreAtTheHeadOfTheMarket() {
|
||||
/*
|
||||
We skip the candle at the head of the market because it has not closed yet. The procedure to determine if we are at the head of the market is different
|
||||
when we are processing Market Files than when we are processing Daily Files. TODO Check that the procedure is good for Beta 6
|
||||
*/
|
||||
if (processingDailyFiles) { // We are processing Daily Files
|
||||
let candlesPerDay = ONE_DAY_IN_MILISECONDS / sessionParameters.timeFrame.config.value
|
||||
if (tradingEngine.current.candle.index.value === candles.length - 1) {
|
||||
if ((candles.length < candlesPerDay) || (candles.length > candlesPerDay && candles.length < candlesPerDay * 2)) {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkIfWeAreAtTheHeadOfTheMarket -> Skipping Candle because it is the last one and has not been closed yet.') }
|
||||
return false
|
||||
/*
|
||||
Note here that in the last candle of the first day or the second day it will use an incomplete candle and partially calculated indicators.
|
||||
if we skip these two periods, then there will be a hole in the file since the last period will be missing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
} else { // We are processing Market Files
|
||||
if (tradingEngine.current.candle.index.value === candles.length - 1) {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkIfWeAreAtTheHeadOfTheMarket -> Skipping Candle because it is the last one and has not been closed yet.') }
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function checkInitialAndFinalDatetime() {
|
||||
/* Here we check that the current candle is not outside of user-defined time range at the session parameters.*/
|
||||
if (candle.end < sessionParameters.timeRange.config.initialDatetime.valueOf()) {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkInitialAndFinalDatetime -> Skipping Candle before the sessionParameters.timeRange.config.initialDatetime.') }
|
||||
return false
|
||||
}
|
||||
if (candle.begin > sessionParameters.timeRange.config.finalDatetime.valueOf()) {
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkInitialAndFinalDatetime -> Skipping Candle after the sessionParameters.timeRange.config.finalDatetime.') }
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function checkMinimunAndMaximunBalance() {
|
||||
/* Checks for Minimun and Maximun Balance. We do the check while not inside any strategy only. */
|
||||
if (
|
||||
tradingEngine.current.strategy.stageType.value === 'No Stage' &&
|
||||
tradingEngine.current.strategy.index.value === -1
|
||||
) {
|
||||
/*
|
||||
First thing to do is to check if we are below the minimum balance, or above the maximun balance.
|
||||
If we are, we are not gooing to trigger on again.
|
||||
*/
|
||||
let minimumBalance
|
||||
let maximumBalance
|
||||
let balance
|
||||
|
||||
if (sessionParameters.sessionBaseAsset.name === bot.market.marketBaseAsset) {
|
||||
balance = tradingEngine.current.balance.baseAsset.value
|
||||
minimumBalance = sessionParameters.sessionBaseAsset.config.minimumBalance
|
||||
maximumBalance = sessionParameters.sessionBaseAsset.config.maximumBalance
|
||||
} else {
|
||||
balance = tradingEngine.current.balance.quotedAsset.value
|
||||
minimumBalance = sessionParameters.sessionQuotedAsset.config.minimumBalance
|
||||
maximumBalance = sessionParameters.sessionQuotedAsset.config.maximumBalance
|
||||
}
|
||||
|
||||
let stopRunningDate = new Date(candle.begin)
|
||||
if (balance <= minimumBalance) {
|
||||
tradingSystem.error = 'Min Balance @ ' + stopRunningDate.toLocaleString()
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkMinimunAndMaximunBalance -> ' + tradingSystem.error) }
|
||||
return false
|
||||
}
|
||||
|
||||
if (balance >= maximumBalance) {
|
||||
tradingSystem.error = 'Max Balance @ ' + stopRunningDate.toLocaleString()
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkMinimunAndMaximunBalance -> ' + tradingSystem.error) }
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
} catch (err) {
|
||||
logger.write(MODULE_NAME, '[ERROR] runSimulation -> err = ' + err.stack)
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
|
||||
Reference in new issue
Block a user