diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index c122735f5..3189af917 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -7,8 +7,6 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { let thisObject = { setCurrentCandle: setCurrentCandle, updateEpisodeCountersAndStatistics: updateEpisodeCountersAndStatistics, - resetPositionCountersAndStatistics: resetPositionCountersAndStatistics, - updatePositionCountersAndStatistics: updatePositionCountersAndStatistics, updateDistanceToEventsCounters: updateDistanceToEventsCounters, initialize: initialize, finalize: finalize @@ -98,6 +96,7 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { node.value = node.config.initialValue } } + node.initialize = initializeNode // This will allow anyone to initialize this node and its children. /* Now we go down through all this node children */ let nodeDefinition = bot.APP_SCHEMA_MAP.get(node.type) @@ -137,26 +136,6 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { tradingEngine.episode.episodeStatistics.days.value = tradingEngine.episode.episodeCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS } - function resetPositionCountersAndStatistics() { - /* Keeping Position Counters Up-to-date */ - if ( - (tradingEngine.current.strategy.stageType.value === 'Open Stage' || tradingEngine.current.strategy.stageType.value === 'Manage Stage') - ) { return } - - tradingEngine.current.position.positionCounters.periods.value = tradingEngine.current.position.positionCounters.periods.config.initialValue - tradingEngine.current.position.positionStatistics.days.value = tradingEngine.current.position.positionStatistics.days.config.initialValue - } - - function updatePositionCountersAndStatistics() { - /* Keeping Position Counters Up-to-date */ - if ( - (tradingEngine.current.strategy.stageType.value === 'Open Stage' || tradingEngine.current.strategy.stageType.value === 'Manage Stage') - ) { - tradingEngine.current.position.positionCounters.periods.value++ - tradingEngine.current.position.positionStatistics.days.value = tradingEngine.current.position.positionCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS - } - } - function updateDistanceToEventsCounters() { /* Keeping Distance Counters Up-to-date */ if ( diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js new file mode 100644 index 000000000..f613c9f1c --- /dev/null +++ b/LowFrequencyTrading/TradingPosition.js @@ -0,0 +1,192 @@ +exports.newTradingPosition = function newTradingPosition(bot, logger) { + /* + This module packages all functions related to Positions. + */ + const MODULE_NAME = 'Trading Position' + let thisObject = { + openPosition: openPosition, + closePosition: closePosition, + applyStopLossFormula: applyStopLossFormula, + applyTakeProfitFormula: applyTakeProfitFormula, + preventStopLossDistortion: preventStopLossDistortion, + applySlippageToStopLoss: applySlippageToStopLoss, + preventTakeProfitDistortion: preventTakeProfitDistortion, + applySlippageToTakeProfit: applySlippageToTakeProfit, + updateStopLoss: updateStopLoss, + updateTakeProfit: updateTakeProfit, + updateSizeAndRate: updateSizeAndRate, + applySlippageToRate: applySlippageToRate, + updateEnds: updateEnds, + updateStatus: updateStatus, + updateCounters: updateCounters, + updateStatistics: updateStatistics, + resetPosition: resetPosition, + initialize: initialize, + finalize: finalize + } + + let tradingEngine + let sessionParameters + + return thisObject + + function initialize() { + tradingEngine = bot.simulationState.tradingEngine + sessionParameters = bot.SESSION.parameters + } + + function finalize() { + tradingEngine = undefined + sessionParameters = undefined + } + + function openPosition(situationName) { + tradingEngine.current.position.status.value = 'Open' + tradingEngine.current.position.begin.value = tradingEngine.current.candle.begin.value + tradingEngine.current.position.end.value = tradingEngine.current.candle.end.value + + tradingEngine.current.position.situationName.value = situationName + + tradingEngine.current.position.stopLoss.stopLossStage.value = 'Open Stage' + tradingEngine.current.position.takeProfit.takeProfitStage.value = 'Open Stage' + tradingEngine.current.position.stopLoss.stopLossPhase.value = 0 + tradingEngine.current.position.takeProfit.takeProfitPhase.value = 0 + } + + function closePosition(exitType) { + tradingEngine.current.position.status.value = 'Closed' + tradingEngine.current.position.end.value = tradingEngine.current.candle.end.value + tradingEngine.current.position.exitType.value = exitType + } + + function resetPosition() { + tradingEngine.current.position.initialize(tradingEngine.current.position) + } + + function applyStopLossFormula(formulas, formulaId) { + tradingEngine.current.position.stopLoss.value = formulas.get(formulaId) + } + + function applyTakeProfitFormula(formulas, formulaId) { + tradingEngine.current.position.takeProfit.value = formulas.get(formulaId) + } + + function updateStopLoss(phase, stage) { + tradingEngine.current.position.stopLoss.stopLossPhase.value = phase + tradingEngine.current.position.stopLoss.stopLossStage.value = stage + } + + function updateTakeProfit(phase, stage) { + tradingEngine.current.position.takeProfit.takeProfitPhase.value = phase + tradingEngine.current.position.takeProfit.takeProfitStage.value = stage + } + + function updateSizeAndRate(size, rate) { + tradingEngine.current.position.size.value = size + tradingEngine.current.position.rate.value = rate + } + + function applySlippageToRate() { + let slippageAmount = tradingEngine.current.position.rate.value * bot.SESSION.parameters.slippage.config.positionRate / 100 + + if (bot.sessionAndMarketBaseAssetsAreEqual) { + tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value - slippageAmount + } else { + tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value + slippageAmount + } + } + + function preventStopLossDistortion() { + /* + Hit Point Validation + + This prevents misscalculations when a formula places the stop loss in this case way beyond the market price. + If we take the stop loss value at those situation would be a huge distortion of facts. + */ + if (bot.sessionAndMarketBaseAssetsAreEqual) { + if (tradingEngine.current.position.stopLoss.value < tradingEngine.current.candle.min.value) { + tradingEngine.current.position.stopLoss.value = tradingEngine.current.candle.min.value + } + } else { + if (tradingEngine.current.position.stopLoss.value > tradingEngine.current.candle.max.value) { + tradingEngine.current.position.stopLoss.value = tradingEngine.current.candle.max.value + } + } + } + + function applySlippageToStopLoss() { + let slippedStopLoss = tradingEngine.current.position.stopLoss.value + + /* Apply the Slippage */ + let slippageAmount = slippedStopLoss * bot.SESSION.parameters.slippage.config.stopLoss / 100 + + if (bot.sessionAndMarketBaseAssetsAreEqual) { + slippedStopLoss = slippedStopLoss + slippageAmount + } else { + slippedStopLoss = slippedStopLoss - slippageAmount + } + + tradingEngine.current.position.endRate.value = slippedStopLoss + } + + function preventTakeProfitDistortion() { + /* + Hit Point Validation: + + This prevents misscalculations when a formula places the take profit in this case way beyond the market price. + If we take the stop loss value at those situation would be a huge distortion of facts. + */ + if (bot.sessionAndMarketBaseAssetsAreEqual) { + if (tradingEngine.current.position.takeProfit.value > tradingEngine.current.candle.max.value) { + tradingEngine.current.position.takeProfit.value = tradingEngine.current.candle.max.value + } + } else { + if (tradingEngine.current.position.takeProfit.value < tradingEngine.current.candle.min.value) { + tradingEngine.current.position.takeProfit.value = tradingEngine.current.candle.min.value + } + } + } + + function applySlippageToTakeProfit() { + let slippedTakeProfit = tradingEngine.current.position.takeProfit.value + /* Apply the Slippage */ + let slippageAmount = slippedTakeProfit * bot.SESSION.parameters.slippage.config.takeProfit / 100 + + if (bot.sessionAndMarketBaseAssetsAreEqual) { + slippedTakeProfit = slippedTakeProfit + slippageAmount + } else { + slippedTakeProfit = slippedTakeProfit - slippageAmount + } + + tradingEngine.current.position.endRate.value = slippedTakeProfit + } + + function updateEnds() { + if (tradingEngine.current.position.status.value === 'Open') { + tradingEngine.current.position.end.value = tradingEngine.current.candle.end.value + } + } + + function updateStatus() { + if (tradingEngine.current.position.status.value === 'Closed') { + resetPosition() + } + } + + function updateCounters() { + if (tradingEngine.current.position.status.value === 'Open') { + tradingEngine.current.position.positionCounters.periods.value++ + } + } + + function updateStatistics() { + if (bot.sessionAndMarketBaseAssetsAreEqual) { + tradingEngine.current.position.positionStatistics.profitLoss.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.previous.balance.baseAsset.value + tradingEngine.current.position.positionStatistics.ROI.value = tradingEngine.current.position.positionStatistics.profitLoss.value * 100 / tradingEngine.current.position.size.value + } else { + tradingEngine.current.position.positionStatistics.profitLoss.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.previous.balance.quotedAsset.value + tradingEngine.current.position.positionStatistics.ROI.value = tradingEngine.current.position.positionStatistics.profitLoss.value * 100 / tradingEngine.current.position.size.value + } + tradingEngine.current.position.positionStatistics.days.value = tradingEngine.current.position.positionCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS + } +} \ No newline at end of file diff --git a/LowFrequencyTrading/TradingRecords.js b/LowFrequencyTrading/TradingRecords.js index 95af633d7..742272708 100644 --- a/LowFrequencyTrading/TradingRecords.js +++ b/LowFrequencyTrading/TradingRecords.js @@ -41,10 +41,6 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { let product = dataset.parentNode let outputDatasetArray = outputDatasetsMap.get(product.config.codeName) - if (product.config.codeName === 'Strategy-Objects') { - let a = 1 - } - if (bot.processingDailyFiles === true && dataset.config.type === 'Daily Files') { persistRecord() } @@ -91,6 +87,9 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { } 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. @@ -166,6 +165,9 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { if (recordProperty.config.isString !== true && Array.isArray(value) !== true) { value = safeNumericValue(value) } + if (recordProperty.config.isString === true && Array.isArray(value) !== true) { + value = safeStringValue(value) + } record.push(value) } return record @@ -191,4 +193,18 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) { } return value } + + function safeStringValue(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 === undefined) { + value = '' + } + if (value === null) { + value = '' + } + return value + } } \ No newline at end of file diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index b7516706a..a8a7cc891 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -186,8 +186,6 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT tradingSystemModule.calculateTakeProfit() } - tradingEngineModule.resetPositionCountersAndStatistics() - tradingEngineModule.updatePositionCountersAndStatistics() tradingEngineModule.updateDistanceToEventsCounters() /* Checking if Stop or Take Profit were hit */ @@ -198,6 +196,16 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT /* Taking a Position */ if (takingPosition === true) { tradingSystemModule.getReadyToTakePosition() + /* + Whem we calculated all the formulas at the begining of the loop, we didn't know + we were going to take position in this same loop cycle. Some might formulas depends on + calculations that are triggered only when we are taking position. For that reason + we will calculate all formulas again, and after that the Stop Loss and Take Profit. + */ + tradingSystemModule.evalFormulas() + tradingSystemModule.calculateTakeProfit() + tradingSystemModule.calculateStopLoss() + tradingExecutionModule.takePosition() tradingSystemModule.takePosition() takingPosition = false diff --git a/LowFrequencyTrading/TradingStrategy.js b/LowFrequencyTrading/TradingStrategy.js index e3e588b6f..0cd221649 100644 --- a/LowFrequencyTrading/TradingStrategy.js +++ b/LowFrequencyTrading/TradingStrategy.js @@ -40,17 +40,15 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger) { tradingEngine.current.strategy.strategyName.value = strategyName } - function closeStrategy() { + function closeStrategy(exitType) { tradingEngine.current.strategy.status.value = 'Closed' + tradingEngine.current.strategy.exitType.value = exitType tradingEngine.current.strategy.end.value = tradingEngine.current.candle.end.value tradingEngine.current.strategy.endRate.value = tradingEngine.current.candle.min.value } function resetStrategy() { - tradingEngine.current.strategy.status.value = tradingEngine.current.strategy.status.config.initialValue - tradingEngine.current.strategy.stageType.value = tradingEngine.current.strategy.stageType.config.initialValue - tradingEngine.current.strategy.index.value = tradingEngine.current.strategy.index.config.initialValue - tradingEngine.current.strategy.strategyCounters.periods.value = tradingEngine.current.strategy.strategyCounters.periods.config.initialValue + tradingEngine.current.strategy.initialize(tradingEngine.current.strategy) } function updateEnds() { diff --git a/LowFrequencyTrading/TradingSystem.js b/LowFrequencyTrading/TradingSystem.js index d1f90f394..aea6f75d6 100644 --- a/LowFrequencyTrading/TradingSystem.js +++ b/LowFrequencyTrading/TradingSystem.js @@ -41,6 +41,9 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { const TRADING_STRATEGY_MODULE = require('./TradingStrategy.js') let tradingStrategyModule = TRADING_STRATEGY_MODULE.newTradingStrategy(bot, logger) + const TRADING_POSITION_MODULE = require('./TradingPosition.js') + let tradingPositionModule = TRADING_POSITION_MODULE.newTradingPosition(bot, logger) + return thisObject function initialize() { @@ -49,12 +52,16 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { sessionParameters = bot.SESSION.parameters tradingStrategyModule.initialize() + tradingPositionModule.initialize() } function finalize() { tradingStrategyModule.finalize() tradingStrategyModule = undefined + tradingPositionModule.finalize() + tradingPositionModule = undefined + chart = undefined conditions = undefined @@ -89,7 +96,9 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { } function mantainPositions() { - + tradingPositionModule.updateStatus() + tradingPositionModule.updateCounters() + tradingPositionModule.updateEnds() } function evalConditions() { @@ -333,7 +342,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { tradingSystem.highlights.push(triggerStage.triggerOff.id) tradingSystem.highlights.push(triggerStage.id) - tradingStrategyModule.closeStrategy() + tradingStrategyModule.closeStrategy('Trigger Off') tradingEngine.current.distanceToEvent.triggerOff.value = 1 @@ -353,7 +362,10 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { function checkTakePosition() { /* Take Position Condition */ - if (tradingEngine.current.strategy.stageType.value === 'Trigger Stage') { + if ( + tradingEngine.current.strategy.stageType.value === 'Trigger Stage' && + tradingEngine.current.strategy.status.value === 'Open' + ) { let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] let triggerStage = strategy.triggerStage @@ -383,12 +395,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { tradingSystem.highlights.push(triggerStage.id) tradingStrategyModule.updateStageType('Open Stage') - - tradingEngine.current.position.stopLoss.stopLossStage.value = 'Open Stage' - tradingEngine.current.position.takeProfit.takeProfitStage.value = 'Open Stage' - tradingEngine.current.position.stopLoss.stopLossPhase.value = 0 - tradingEngine.current.position.takeProfit.takeProfitPhase.value = 0 - tradingEngine.current.position.situationName.value = situation.name + tradingPositionModule.openPosition(situation.name) /* TODO See what to do with this: checkAnnouncements(triggerStage.takePosition) @@ -470,8 +477,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(stage.id) - tradingEngine.current.position.stopLoss.stopLossPhase.value++ - tradingEngine.current.position.stopLoss.stopLossStage.value = 'Manage Stage' + tradingPositionModule.updateStopLoss(tradingEngine.current.position.stopLoss.stopLossPhase.value + 1, 'Manage Stage') + if (tradingEngine.current.position.takeProfit.takeProfitPhase.value > 0) { tradingStrategyModule.updateStageType('Manage Stage') /* TODO ANNOUNCEMENT @@ -521,7 +528,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { if (moveToPhase !== undefined) { for (let q = 0; q < stopLoss.phases.length; q++) { if (stopLoss.phases[q].id === moveToPhase.id) { - tradingEngine.current.position.stopLoss.stopLossPhase.value = q + 1 + tradingPositionModule.updateStopLoss(q + 1, 'Manage Stage') } } } else { @@ -529,7 +536,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { continue } - tradingEngine.current.position.stopLoss.stopLossStage.value = 'Manage Stage' if (tradingEngine.current.position.takeProfit.takeProfitPhase.value > 0) { tradingStrategyModule.updateStageType('Manage Stage') /* TODO ANNOUNCEMENT @@ -572,7 +578,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { if (phase !== undefined) { if (phase.formula !== undefined) { let previousValue = tradingEngine.current.position.stopLoss.value - tradingEngine.current.position.stopLoss.value = formulas.get(phase.formula.id) + tradingPositionModule.applyStopLossFormula(formulas, phase.formula.id) /* TODO ANNOUNCEMENTS if (tradingEngine.current.position.stopLoss.value !== previousValue) { @@ -648,8 +654,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { tradingSystem.highlights.push(parentNode.id) tradingSystem.highlights.push(stage.id) - tradingEngine.current.position.takeProfit.takeProfitPhase.value++ - tradingEngine.current.position.takeProfit.takeProfitStage.value = 'Manage Stage' + tradingPositionModule.updateTakeProfit(tradingEngine.current.position.takeProfit.takeProfitPhase.value + 1, 'Manage Stage') + if (tradingEngine.current.position.stopLoss.stopLossPhase.value > 0) { tradingStrategyModule.updateStageType('Manage Stage') /* TODO ANNOUNCEMENT @@ -698,7 +704,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { if (moveToPhase !== undefined) { for (let q = 0; q < takeProfit.phases.length; q++) { if (takeProfit.phases[q].id === moveToPhase.id) { - tradingEngine.current.position.takeProfit.takeProfitPhase.value = q + 1 + tradingPositionModule.updateTakeProfit(q + 1, 'Manage Stage') } } } else { @@ -706,7 +712,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { continue } - tradingEngine.current.position.takeProfit.takeProfitStage.value = 'Manage Stage' if (tradingEngine.current.position.stopLoss.stopLossPhase.value > 0) { tradingStrategyModule.updateStageType('Manage Stage') /* TODO ANNOUNCEMENT @@ -748,8 +753,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { if (phase !== undefined) { if (phase.formula !== undefined) { - let previousValue = tradingEngine.current.position.stopLoss.value - tradingEngine.current.position.takeProfit.value = formulas.get(phase.formula.id) + let previousValue = tradingEngine.current.position.takeProfit.value + tradingPositionModule.applyTakeProfitFormula(formulas, phase.formula.id) /* TODO ANNOUNCEMENTS if (tradingEngine.current.position.takeProfit.value !== previousValue) { @@ -776,45 +781,16 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { (!bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.min.value <= tradingEngine.current.position.stopLoss.value) ) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Stop Loss was hit.') - /* - Hit Point Validation - - This prevents misscalculations when a formula places the stop loss in this case way beyond the market price. - If we take the stop loss value at those situation would be a huge distortion of facts. - */ - if (bot.sessionAndMarketBaseAssetsAreEqual) { - if (tradingEngine.current.position.stopLoss.value < tradingEngine.current.candle.min.value) { - tradingEngine.current.position.stopLoss.value = tradingEngine.current.candle.min.value - } - } else { - if (tradingEngine.current.position.stopLoss.value > tradingEngine.current.candle.max.value) { - tradingEngine.current.position.stopLoss.value = tradingEngine.current.candle.max.value - } - } - let slippedStopLoss = tradingEngine.current.position.stopLoss.value - - /* Apply the Slippage */ - let slippageAmount = slippedStopLoss * bot.SESSION.parameters.slippage.config.stopLoss / 100 - - if (bot.sessionAndMarketBaseAssetsAreEqual) { - slippedStopLoss = slippedStopLoss + slippageAmount - } else { - slippedStopLoss = slippedStopLoss - slippageAmount - } - - tradingEngine.current.position.endRate.value = slippedStopLoss + tradingPositionModule.preventStopLossDistortion() + tradingPositionModule.applySlippageToStopLoss() + tradingPositionModule.closePosition('Stop Loss') tradingStrategyModule.updateStageType('Close Stage') + /* TODO ANNOUNCEMENT checkAnnouncements(strategy.closeStage, 'Stop') */ - tradingEngine.current.position.stopLoss.stopLossStage.value = 'No Stage' - tradingEngine.current.position.takeProfit.takeProfitStage.value = 'No Stage' - tradingEngine.current.position.end.value = tradingEngine.current.candle.end.value - tradingEngine.current.position.status.value = 1 - tradingEngine.current.position.exitType.value = 1 - return true // This means that the STOP was hit. } @@ -824,46 +800,16 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { (!bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.max.value >= tradingEngine.current.position.takeProfit.value) ) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Take Profit was hit.') - /* - Hit Point Validation: - - This prevents misscalculations when a formula places the take profit in this case way beyond the market price. - If we take the stop loss value at those situation would be a huge distortion of facts. - */ - if (bot.sessionAndMarketBaseAssetsAreEqual) { - if (tradingEngine.current.position.takeProfit.value > tradingEngine.current.candle.max.value) { - tradingEngine.current.position.takeProfit.value = tradingEngine.current.candle.max.value - } - } else { - if (tradingEngine.current.position.takeProfit.value < tradingEngine.current.candle.min.value) { - tradingEngine.current.position.takeProfit.value = tradingEngine.current.candle.min.value - } - } - let slippedTakeProfit = tradingEngine.current.position.takeProfit.value - /* Apply the Slippage */ - let slippageAmount = slippedTakeProfit * bot.SESSION.parameters.slippage.config.takeProfit / 100 - - if (bot.sessionAndMarketBaseAssetsAreEqual) { - slippedTakeProfit = slippedTakeProfit + slippageAmount - } else { - slippedTakeProfit = slippedTakeProfit - slippageAmount - } - - tradingEngine.current.position.endRate.value = slippedTakeProfit + tradingPositionModule.preventTakeProfitDistortion() + tradingPositionModule.applySlippageToTakeProfit() + tradingPositionModule.closePosition('Take Profit') tradingStrategyModule.updateStageType('Close Stage') + /* TODO ANNOUNCEMENT checkAnnouncements(strategy.closeStage, 'Take Profit') */ - tradingEngine.current.position.stopLoss.stopLossStage.value = 'No Stage' - tradingEngine.current.position.takeProfit.takeProfitStage.value = 'No Stage' - - tradingEngine.current.position.end.value = tradingEngine.current.candle.end.value - tradingEngine.current.position.status.value = 1 - tradingEngine.current.position.exitType.value = 2 - - addToSnapshots = true return true // This means that the Take Profit was hit. } } @@ -872,31 +818,20 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { } function getReadyToTakePosition() { + /* Updating Episode Counters */ + tradingEngine.episode.episodeCounters.positions.value++ + /* Inicializing this counter */ tradingEngine.current.distanceToEvent.takePosition.value = 1 /* Position size and rate */ - tradingEngine.current.position.size.value = getPositionSize() - tradingEngine.current.position.rate.value = getPositionRate() + tradingPositionModule.updateSizeAndRate(getPositionSize(), getPositionRate()) - /* We take what was calculated at the formula and apply the slippage. */ - let slippageAmount = tradingEngine.current.position.rate.value * bot.SESSION.parameters.slippage.config.positionRate / 100 - - if (bot.sessionAndMarketBaseAssetsAreEqual) { - tradingEngine.current.position.rate.value = tradingEngine.current.position.rate.value - slippageAmount - } else { - tradingEngine.current.position.rate.value = tradingEngine.current.position.rate.value + slippageAmount - } - - /* Update the position information. */ - tradingEngine.current.position.begin.value = tradingEngine.current.candle.begin.value - tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value + /* We take what was calculated at the formula of position rate and apply the slippage. */ + tradingPositionModule.applySlippageToRate() } function takePosition() { - calculateTakeProfit() // TODO: Check if this is really necesary - calculateStopLoss() // TODO: Check if this is really necesary - tradingEngine.previous.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value tradingEngine.previous.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value @@ -927,70 +862,44 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { } function closePosition() { - tradingEngine.episode.episodeCounters.positions.value++ - + /* We calculate the new balances after the position is closed. */ let feePaid = 0 - if (bot.sessionAndMarketBaseAssetsAreEqual) { - feePaid = tradingEngine.current.balance.quotedAsset.value / tradingEngine.current.position.endRate.value * bot.SESSION.parameters.feeStructure.config.taker / 100 - tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngine.current.balance.quotedAsset.value / tradingEngine.current.position.endRate.value - feePaid tradingEngine.current.balance.quotedAsset.value = 0 } else { - feePaid = tradingEngine.current.balance.baseAsset.value * tradingEngine.current.position.endRate.value * bot.SESSION.parameters.feeStructure.config.taker / 100 - tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngine.current.balance.baseAsset.value * tradingEngine.current.position.endRate.value - feePaid tradingEngine.current.balance.baseAsset.value = 0 } - if (bot.sessionAndMarketBaseAssetsAreEqual) { - tradingEngine.last.position.positionStatistics.profitLoss.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.previous.balance.baseAsset.value - tradingEngine.last.position.positionStatistics.ROI.value = tradingEngine.last.position.positionStatistics.profitLoss.value * 100 / tradingEngine.current.position.size.value - if (isNaN(tradingEngine.last.position.positionStatistics.ROI.value)) { tradingEngine.last.position.positionStatistics.ROI.value = 0 } - tradingEngine.episode.episodeStatistics.profitLoss.value = tradingEngine.current.balance.baseAsset.value - sessionParameters.sessionBaseAsset.config.initialBalance - } else { - tradingEngine.last.position.positionStatistics.profitLoss.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.previous.balance.quotedAsset.value - tradingEngine.last.position.positionStatistics.ROI.value = tradingEngine.last.position.positionStatistics.profitLoss.value * 100 / tradingEngine.current.position.size.value - if (isNaN(tradingEngine.last.position.positionStatistics.ROI.value)) { tradingEngine.last.position.positionStatistics.ROI.value = 0 } - tradingEngine.episode.episodeStatistics.profitLoss.value = tradingEngine.current.balance.quotedAsset.value - sessionParameters.sessionQuotedAsset.config.initialBalance - } - - tradingEngine.current.position.positionStatistics.ROI.value = tradingEngine.last.position.positionStatistics.ROI.value + tradingPositionModule.updateStatistics() + /* Recalculating Episode Counters */ if (tradingEngine.last.position.positionStatistics.profitLoss.value > 0) { tradingEngine.episode.episodeCounters.hits.value++ } else { tradingEngine.episode.episodeCounters.fails.value++ } + /* Recalculating Episode Statistics */ if (bot.sessionAndMarketBaseAssetsAreEqual) { + tradingEngine.episode.episodeStatistics.profitLoss.value = tradingEngine.current.balance.baseAsset.value - sessionParameters.sessionBaseAsset.config.initialBalance 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.episodeCounters.positions.value tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 } else { + tradingEngine.episode.episodeStatistics.profitLoss.value = tradingEngine.current.balance.quotedAsset.value - sessionParameters.sessionQuotedAsset.config.initialBalance 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.episodeCounters.positions.value tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 } - - tradingEngine.current.position.stopLoss.value = 0 - tradingEngine.current.position.takeProfit.value = 0 - - tradingEngine.current.position.rate.value = 0 - tradingEngine.current.position.size.value = 0 - - tradingEngine.current.position.stopLoss.stopLossStage.value = 'No Stage' - tradingEngine.current.position.takeProfit.takeProfitStage.value = 'No Stage' - tradingEngine.current.position.stopLoss.stopLossPhase.value = -1 - tradingEngine.current.position.takeProfit.takeProfitPhase.value = -1 - } function exitStrategyAfterPosition() { if (tradingEngine.current.strategy.stageType.value === 'Close Stage') { - tradingStrategyModule.closeStrategy() + tradingStrategyModule.closeStrategy('Position Closed') tradingEngine.current.distanceToEvent.triggerOff.value = 1 } }