From 7e142a01dc51e5a6af11b03e6cb163cfbd46eb8c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 18 Jun 2020 16:02:50 +0200 Subject: [PATCH] cleaning --- LowFrequencyTrading/TradingEngine.js | 143 ++---------------- LowFrequencyTrading/TradingExecution.js | 183 +++++++++++------------ LowFrequencyTrading/TradingSimulation.js | 9 +- LowFrequencyTrading/TradingSystem.js | 153 +++++++++++++++++-- 4 files changed, 240 insertions(+), 248 deletions(-) diff --git a/LowFrequencyTrading/TradingEngine.js b/LowFrequencyTrading/TradingEngine.js index a755b5cd8..3e231918a 100644 --- a/LowFrequencyTrading/TradingEngine.js +++ b/LowFrequencyTrading/TradingEngine.js @@ -1,16 +1,11 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { - const MODULE_NAME = 'Trading Engine' - let thisObject = { setCurrentCandle: setCurrentCandle, updateEpisodeCountersAndStatistics: updateEpisodeCountersAndStatistics, + resetPositionCountersAndStatistics: resetPositionCountersAndStatistics, updatePositionCountersAndStatistics: updatePositionCountersAndStatistics, updateDistanceToEventsCounters: updateDistanceToEventsCounters, - getReadyToTakePosition: getReadyToTakePosition, - takePosition: takePosition, - getReadyToClosePosition: getReadyToClosePosition, - closePosition: closePosition, initialize: initialize, finalize: finalize } @@ -138,20 +133,23 @@ 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') ) { - if (takePositionNow === true) { - tradingEngine.current.position.positionCounters.periods.value = 0 - } - 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 - } else { - tradingEngine.current.position.positionCounters.periods.value = 0 - tradingEngine.current.position.positionStatistics.days.value = 0 } } @@ -182,124 +180,5 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) { } } - function getReadyToTakePosition(candle) { - /* 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() - - /* 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 trade record information. */ - tradingEngine.current.position.begin.value = candle.begin - tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value - } - - 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 - - tradingEngine.last.position.profitLoss.value = 0 - tradingEngine.last.position.ROI.value = 0 - - let feePaid = 0 - - if (bot.sessionAndMarketBaseAssetsAreEqual) { - feePaid = tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100 - - tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value - feePaid - tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.current.position.size.value - } else { - feePaid = tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100 - - tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value - feePaid - tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.current.position.size.value - } - } - - function getReadyToClosePosition() { - /* Inicializing this counter */ - tradingEngine.current.distanceToEvent.closePosition.value = 1 - - /* Position size and rate */ - let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] - } - - function closePosition() { - tradingEngine.episode.positionCounters.positions.value++ - - 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.profitLoss.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.previous.balance.baseAsset.value - tradingEngine.last.position.ROI.value = tradingEngine.last.position.profitLoss.value * 100 / tradingEngine.current.position.size.value - if (isNaN(tradingEngine.last.position.ROI.value)) { tradingEngine.last.position.ROI.value = 0 } - tradingEngine.episode.episodeStatistics.profitLoss.value = tradingEngine.current.balance.baseAsset.value - sessionParameters.sessionBaseAsset.config.initialBalance - } else { - tradingEngine.last.position.profitLoss.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.previous.balance.quotedAsset.value - tradingEngine.last.position.ROI.value = tradingEngine.last.position.profitLoss.value * 100 / tradingEngine.current.position.size.value - if (isNaN(tradingEngine.last.position.ROI.value)) { tradingEngine.last.position.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.ROI.value - - if (tradingEngine.last.position.profitLoss.value > 0) { - tradingEngine.episode.episodeCounters.hits.value++ - } else { - tradingEngine.episode.episodeCounters.fails.value++ - } - - if (bot.sessionAndMarketBaseAssetsAreEqual) { - tradingEngine.episode.episodeStatistics.ROI.value = (sessionParameters.sessionBaseAsset.config.initialBalance + tradingEngine.episode.episodeStatistics.profitLoss.value) / sessionParameters.sessionBaseAsset.config.initialBalance - 1 - tradingEngine.episode.episodeStatistics.hitRatio.value = tradingEngine.episode.episodeCounters.hits.value / tradingEngine.episode.positionCounters.positions.value - tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 - } else { - tradingEngine.episode.episodeStatistics.ROI.value = (sessionParameters.sessionQuotedAsset.config.initialBalance + tradingEngine.episode.episodeStatistics.profitLoss.value) / sessionParameters.sessionQuotedAsset.config.initialBalance - 1 - tradingEngine.episode.episodeStatistics.hitRatio.value = tradingEngine.episode.episodeCounters.hits.value / tradingEngine.episode.positionCounters.positions.value - tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 - } - - addRecords() - - tradingEngine.current.position.stopLoss.value = 0 - tradingEngine.current.position.takeProfit.value = 0 - - tradingEngine.current.position.rate.value = 0 - tradingEngine.current.position.size.value = 0 - - timerToCloseStage = candle.begin - 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 - - } } diff --git a/LowFrequencyTrading/TradingExecution.js b/LowFrequencyTrading/TradingExecution.js index b2cfdf148..1016cb64b 100644 --- a/LowFrequencyTrading/TradingExecution.js +++ b/LowFrequencyTrading/TradingExecution.js @@ -24,70 +24,65 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { } function takePosition() { - /* Check if we need to execute. */ - if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not. */ - /* Check that we are in LIVE MODE */ - if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') { - logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> Taking a Position in ' + bot.SESSION.type) - logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.size.value = ' + tradingEngine.current.position.size.value) - logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value) - logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> slippageAmount = ' + slippageAmount) - logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value) + /* Check that we are in LIVE MODE */ + if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') { + logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> Taking a Position in ' + bot.SESSION.type) + logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.size.value = ' + tradingEngine.current.position.size.value) + logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value) + logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> slippageAmount = ' + slippageAmount) + logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value) - /* We see if we need to put the actual order at the exchange. */ - if (variable.executionContext !== undefined) { - switch (variable.executionContext.status) { - case 'Without a Position': { // We need to put the order because It was not put yet. - if (strategy.openStage !== undefined) { - if (strategy.openStage.openExecution !== undefined) { - putOpeningOrder() - return - } + /* We see if we need to put the actual order at the exchange. */ + if (variable.executionContext !== undefined) { + switch (variable.executionContext.status) { + case 'Without a Position': { // We need to put the order because It was not put yet. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return } - break - } - case 'Position Closed': { // Waiting for a confirmation that the position was closed. - if (strategy.openStage !== undefined) { - if (strategy.openStage.openExecution !== undefined) { - putOpeningOrder() - return - } - } - break - } - case 'Taking Position': { // Waiting for a confirmation that the position was taken. - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is Taking Position.') } - break - } - case 'In a Position': { // This should mean that we already put the order at the exchange. - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is In a Position.') } - break } + break } - } else { // The context does not exist so it means we are not in a position. - if (strategy.openStage !== undefined) { - if (strategy.openStage.openExecution !== undefined) { - putOpeningOrder() - return + case 'Position Closed': { // Waiting for a confirmation that the position was closed. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return + } } + break + } + case 'Taking Position': { // Waiting for a confirmation that the position was taken. + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is Taking Position.') + break + } + case 'In a Position': { // This should mean that we already put the order at the exchange. + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is In a Position.') + break + } + } + } else { // The context does not exist so it means we are not in a position. + if (strategy.openStage !== undefined) { + if (strategy.openStage.openExecution !== undefined) { + putOpeningOrder() + return } } - } else { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Not trading live.') } } } else { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Not the last closed candle.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Not trading live.') } } function putOpeningOrder() { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function.') /* We wont take a position unless we are withing the sessionParameters.timeRange.config.initialDatetime and the sessionParameters.timeRange.config.finalDatetime range */ if (sessionParameters.timeRange.config.initialDatetime !== undefined) { if (candle.end < sessionParameters.timeRange.config.initialDatetime) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> sessionParameters.timeRange.config.initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime) } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> sessionParameters.timeRange.config.initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime) takePositionAtSimulation() return } @@ -96,7 +91,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { /* We wont take a position if we are past the final datetime */ if (sessionParameters.timeRange.config.finalDatetime !== undefined) { if (candle.begin > sessionParameters.timeRange.config.finalDatetime) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> sessionParameters.timeRange.config.finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime) } + logger.write(MODULE_NAME, '[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> sessionParameters.timeRange.config.finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime) takePositionAtSimulation() return } @@ -106,7 +101,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { if (variable.executionContext !== undefined) { if (variable.executionContext.periods !== undefined) { if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution.') takePositionAtSimulation() return } @@ -118,9 +113,9 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { let today = new Date(Math.trunc((new Date().valueOf()) / global.ONE_DAY_IN_MILISECONDS) * global.ONE_DAY_IN_MILISECONDS) let processDay = new Date(Math.trunc(currentDay.valueOf() / global.ONE_DAY_IN_MILISECONDS) * global.ONE_DAY_IN_MILISECONDS) if (today.valueOf() !== processDay.valueOf()) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the current candle belongs to the previous day and that is considered simulation and not live trading.') } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> today = ' + today) } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> processDay = ' + processDay) } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the current candle belongs to the previous day and that is considered simulation and not live trading.') + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> today = ' + today) + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> processDay = ' + processDay) takePositionAtSimulation() return } @@ -152,16 +147,16 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { periods: tradingEngine.episode.episodeCounters.periods.value } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to create order.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to create order.') exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated) function onOrderCreated(err, order) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Entering function.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Entering function.') try { switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ') variable.executionContext = { status: 'In a Position', periods: tradingEngine.episode.episodeCounters.periods.value, @@ -173,21 +168,21 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { return } case global.DEFAULT_FAIL_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ') } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message) } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ') + logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message) strategy.openStage.openExecution.error = err.message afterLoop() return } case global.DEFAULT_RETRY_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ') } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message) } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ') + logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message) strategy.openStage.openExecution.error = err.message afterLoop() return } } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message) } + logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message) callBackFunction(global.DEFAULT_FAIL_RESPONSE) return } catch (err) { @@ -199,39 +194,37 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { } function closePosition() { - if (currentCandleIndex > candles.length - 10) { /* Only at the last candles makes sense to check if we are in live mode or not. */ - /* Check that we are in LIVE MODE */ - if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') { - /* We see if we need to put the actual order at the exchange. */ - if (variable.executionContext !== undefined) { - switch (variable.executionContext.status) { - case 'Without a Position': { // No way to close anything at the exchange. - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Without a Position.') } - break - } - case 'In a Position': { // This should mean that we already put the order at the exchange. - if (strategy.closeStage !== undefined) { - if (strategy.closeStage.closeExecution !== undefined) { - putClosingOrder() - return - } + /* Check that we are in LIVE MODE */ + if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') { + /* We see if we need to put the actual order at the exchange. */ + if (variable.executionContext !== undefined) { + switch (variable.executionContext.status) { + case 'Without a Position': { // No way to close anything at the exchange. + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Without a Position.') + break + } + case 'In a Position': { // This should mean that we already put the order at the exchange. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return } - break } - case 'Closing Position': { // Waiting for a confirmation that the position was taken. - if (strategy.closeStage !== undefined) { - if (strategy.closeStage.closeExecution !== undefined) { - putClosingOrder() - return - } + break + } + case 'Closing Position': { // Waiting for a confirmation that the position was taken. + if (strategy.closeStage !== undefined) { + if (strategy.closeStage.closeExecution !== undefined) { + putClosingOrder() + return } - break } + break + } - case 'Position Closed': { // - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Position Closed.') } - break - } + case 'Position Closed': { // + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Position Closed.') + break } } } @@ -239,13 +232,11 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { } function putClosingOrder() { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> putClosingOrder -> Entering function.') } - /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ if (variable.executionContext !== undefined) { if (variable.executionContext.periods !== undefined) { if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution.') closePositionAtSimulation() return } @@ -278,16 +269,16 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { periods: tradingEngine.episode.episodeCounters.periods.value } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange.') exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated) function onOrderCreated(err, order) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Entering function.') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Entering function.') try { switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ') variable.executionContext = { status: 'Position Closed', periods: tradingEngine.episode.episodeCounters.periods.value, @@ -299,20 +290,20 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) { return } case global.DEFAULT_FAIL_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ') /* We will assume that the problem is temporary, and expect that it will work at the next execution. */ strategy.closeStage.closeExecution.error = err.message afterLoop() return } case global.DEFAULT_RETRY_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ') } + logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ') strategy.closeStage.closeExecution.error = err.message afterLoop() return } } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message) } + logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message) callBackFunction(global.DEFAULT_FAIL_RESPONSE) return } catch (err) { diff --git a/LowFrequencyTrading/TradingSimulation.js b/LowFrequencyTrading/TradingSimulation.js index 3ea233ffd..7060f0a9f 100644 --- a/LowFrequencyTrading/TradingSimulation.js +++ b/LowFrequencyTrading/TradingSimulation.js @@ -182,6 +182,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT tradingSystemModule.calculateTakeProfit() } + tradingEngineModule.resetPositionCountersAndStatistics() tradingEngineModule.updatePositionCountersAndStatistics() tradingEngineModule.updateDistanceToEventsCounters() @@ -192,17 +193,17 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT /* Taking a Position */ if (takingPosition === true) { - tradingEngineModule.getReadyToTakePosition(candle) + tradingSystemModule.getReadyToTakePosition() tradingExecutionModule.takePosition() - tradingEngineModule.takePosition() + tradingSystemModule.takePosition() takingPosition = false } /* Closing a Position */ if (closingPosition === true) { - tradingEngineModule.getReadyToClosePosition(candle) + tradingSystemModule.getReadyToClosePosition() tradingExecutionModule.closePosition() - tradingEngineModule.closePosition() + tradingSystemModule.closePosition() closingPosition = false } diff --git a/LowFrequencyTrading/TradingSystem.js b/LowFrequencyTrading/TradingSystem.js index bef5179ae..0d2f92d4f 100644 --- a/LowFrequencyTrading/TradingSystem.js +++ b/LowFrequencyTrading/TradingSystem.js @@ -1,8 +1,5 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { - const MODULE_NAME = 'Trading System' - const FULL_LOG = true - let thisObject = { reset: reset, evalConditions: evalConditions, @@ -15,6 +12,10 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { checkTakeProfitPhaseEvents: checkTakeProfitPhaseEvents, calculateTakeProfit: calculateTakeProfit, checkStopLossOrTakeProfitWasHit: checkStopLossOrTakeProfitWasHit, + getReadyToTakePosition: getReadyToTakePosition, + takePosition: takePosition, + getReadyToClosePosition: getReadyToClosePosition, + closePosition: closePosition, exitStrategyAfterPosition: exitStrategyAfterPosition, getPositionSize: getPositionSize, getPositionRate: getPositionRate, @@ -138,7 +139,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { } try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] evalCondition -> code = ' + code) } + logger.write(MODULE_NAME, '[INFO] evalCondition -> code = ' + code) value = eval(code) } catch (err) { /* @@ -170,8 +171,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { tradingSystem.values.push([node.id, value]) } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] evalCondition -> value = ' + value) } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] evalCondition -> error = ' + error) } + logger.write(MODULE_NAME, '[INFO] evalCondition -> value = ' + value) + logger.write(MODULE_NAME, '[INFO] evalCondition -> error = ' + error) } function evalFormula(node) { @@ -179,7 +180,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { let error try { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] evalFormula -> code = ' + node.code) } + logger.write(MODULE_NAME, '[INFO] evalFormula -> code = ' + node.code) value = eval(node.code) } catch (err) { /* @@ -208,8 +209,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { tradingSystem.values.push([node.id, value]) } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] evalFormula -> value = ' + value) } - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] evalFormula -> error = ' + error) } + logger.write(MODULE_NAME, '[INFO] evalFormula -> value = ' + value) + logger.write(MODULE_NAME, '[INFO] evalFormula -> error = ' + error) } function checkTriggerOn() { @@ -273,7 +274,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { saveAsLastTriggerOnSnapshot = true */ - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] checkTriggerOn -> Entering into Strategy: ' + strategy.name) } + logger.write(MODULE_NAME, '[INFO] checkTriggerOn -> Entering into Strategy: ' + strategy.name) return true // This Means that we have just met the conditions to trigger on. } } @@ -325,7 +326,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { checkAnnouncements(triggerStage.triggerOff) */ - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] checkTriggerOff -> Closing Strategy: ' + strategy.name) } + logger.write(MODULE_NAME, '[INFO] checkTriggerOff -> Closing Strategy: ' + strategy.name) return true // This Means that we have just met the conditions to trigger off. } } @@ -379,7 +380,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { saveAsLastTakePositionSnapshot = true */ - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] checkTakePosition -> Conditions at the Take Position Event were met.') } + logger.write(MODULE_NAME, '[INFO] checkTakePosition -> Conditions at the Take Position Event were met.') return true // This Means that we have just met the conditions to take position. } } @@ -725,7 +726,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { (bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.max.value >= tradingEngine.current.position.stopLoss.value) || (sessionParameters.sessionBaseAsset.name !== bot.market.baseAsset && tradingEngine.current.candle.min.value <= tradingEngine.current.position.stopLoss.value) ) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Stop Loss was hit.') } + logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Stop Loss was hit.') /* Hit Point Validation @@ -772,7 +773,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { (bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.min.value <= tradingEngine.current.position.takeProfit.value) || (sessionParameters.sessionBaseAsset.name !== bot.market.baseAsset && tradingEngine.current.candle.max.value >= tradingEngine.current.position.takeProfit.value) ) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Take Profit was hit.') } + logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Take Profit was hit.') /* Hit Point Validation: @@ -819,6 +820,126 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { } } + function getReadyToTakePosition() { + /* 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() + + /* 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 + } + + 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 + + tradingEngine.last.position.profitLoss.value = 0 + tradingEngine.last.position.ROI.value = 0 + + let feePaid = 0 + + if (bot.sessionAndMarketBaseAssetsAreEqual) { + feePaid = tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100 + + tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value - feePaid + tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.current.position.size.value + } else { + feePaid = tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100 + + tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value - feePaid + tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.current.position.size.value + } + } + + function getReadyToClosePosition() { + /* Inicializing this counter */ + tradingEngine.current.distanceToEvent.closePosition.value = 1 + + /* Position size and rate */ + let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value] + } + + function closePosition() { + tradingEngine.episode.positionCounters.positions.value++ + + 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.profitLoss.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.previous.balance.baseAsset.value + tradingEngine.last.position.ROI.value = tradingEngine.last.position.profitLoss.value * 100 / tradingEngine.current.position.size.value + if (isNaN(tradingEngine.last.position.ROI.value)) { tradingEngine.last.position.ROI.value = 0 } + tradingEngine.episode.episodeStatistics.profitLoss.value = tradingEngine.current.balance.baseAsset.value - sessionParameters.sessionBaseAsset.config.initialBalance + } else { + tradingEngine.last.position.profitLoss.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.previous.balance.quotedAsset.value + tradingEngine.last.position.ROI.value = tradingEngine.last.position.profitLoss.value * 100 / tradingEngine.current.position.size.value + if (isNaN(tradingEngine.last.position.ROI.value)) { tradingEngine.last.position.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.ROI.value + + if (tradingEngine.last.position.profitLoss.value > 0) { + tradingEngine.episode.episodeCounters.hits.value++ + } else { + tradingEngine.episode.episodeCounters.fails.value++ + } + + if (bot.sessionAndMarketBaseAssetsAreEqual) { + tradingEngine.episode.episodeStatistics.ROI.value = (sessionParameters.sessionBaseAsset.config.initialBalance + tradingEngine.episode.episodeStatistics.profitLoss.value) / sessionParameters.sessionBaseAsset.config.initialBalance - 1 + tradingEngine.episode.episodeStatistics.hitRatio.value = tradingEngine.episode.episodeCounters.hits.value / tradingEngine.episode.positionCounters.positions.value + tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 + } else { + tradingEngine.episode.episodeStatistics.ROI.value = (sessionParameters.sessionQuotedAsset.config.initialBalance + tradingEngine.episode.episodeStatistics.profitLoss.value) / sessionParameters.sessionQuotedAsset.config.initialBalance - 1 + tradingEngine.episode.episodeStatistics.hitRatio.value = tradingEngine.episode.episodeCounters.hits.value / tradingEngine.episode.positionCounters.positions.value + tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = tradingEngine.episode.episodeStatistics.ROI.value / tradingEngine.episode.episodeStatistics.days.value * 365 + } + + addRecords() + + tradingEngine.current.position.stopLoss.value = 0 + tradingEngine.current.position.takeProfit.value = 0 + + tradingEngine.current.position.rate.value = 0 + tradingEngine.current.position.size.value = 0 + + timerToCloseStage = candle.begin + 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') { if (candle.begin - 5 * 60 * 1000 > timerToCloseStage) { @@ -832,9 +953,9 @@ exports.newTradingSystem = function newTradingSystem(bot, logger) { timerToCloseStage = 0 tradingEngine.current.distanceToEvent.triggerOff.value = 1 - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] exitStrategyAfterPosition -> Exiting the Strategy.') } + logger.write(MODULE_NAME, '[INFO] exitStrategyAfterPosition -> Exiting the Strategy.') } else { - if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] exitStrategyAfterPosition -> Waiting for timer.') } + logger.write(MODULE_NAME, '[INFO] exitStrategyAfterPosition -> Waiting for timer.') } } }