From 9ef05adf0c47d16119648592a3748fd3c6fcb086 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 23 Aug 2019 18:12:31 +0200 Subject: [PATCH] Trading Engine Changes --- Commons.js | 223 +++++++++++++++++++++----------- TradingEngineProcessMainLoop.js | 8 +- 2 files changed, 153 insertions(+), 78 deletions(-) diff --git a/Commons.js b/Commons.js index 2fad1c1cc..54bbff3a8 100644 --- a/Commons.js +++ b/Commons.js @@ -164,16 +164,61 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } /* Slippage */ - - let slippage = 0 + let slippage = { // Default Values + positionRate: 0, + stopLoss: 0, + takeProfit: 0 + } if (definition.tradingSystem !== undefined) { if (definition.tradingSystem.parameters !== undefined) { if (definition.tradingSystem.parameters.baseAsset !== undefined) { if (definition.tradingSystem.parameters.baseAsset.formula !== undefined) { if (definition.tradingSystem.parameters.baseAsset.formula.code !== undefined) { - let code = JSON.parse(definition.tradingSystem.parameters.baseAsset.formula.code) - if (code.initialDatetime !== undefined) { - slippage = code.slippage + try { + let code = JSON.parse(definition.tradingSystem.parameters.baseAsset.formula.code) + if (code.slippage !== undefined) { + if (code.slippage.positionRate !== undefined) { + slippage.positionRate = code.slippage.positionRate + } + if (code.slippage.stopLoss !== undefined) { + slippage.stopLoss = code.slippage.stopLoss + } + if (code.slippage.takeProfit !== undefined) { + slippage.takeProfit = code.slippage.takeProfit + } + } + } catch (err) { + tradingSystem.parameters.baseAsset.formula.error = "Slippage Error: " + err.message + } + } + } + } + } + } + + /* Fee Structure */ + let feeStructure = { // Default Values + maker: 0, + taker: 0 + } + + if (definition.tradingSystem !== undefined) { + if (definition.tradingSystem.parameters !== undefined) { + if (definition.tradingSystem.parameters.baseAsset !== undefined) { + if (definition.tradingSystem.parameters.baseAsset.formula !== undefined) { + if (definition.tradingSystem.parameters.baseAsset.formula.code !== undefined) { + try { + let code = JSON.parse(definition.tradingSystem.parameters.baseAsset.formula.code) + if (code.feeStructure !== undefined) { + if (code.feeStructure.maker !== undefined) { + feeStructure.maker = code.feeStructure.maker + } + if (code.feeStructure.taker !== undefined) { + feeStructure.taker = code.feeStructure.taker + } + } + } catch (err) { + tradingSystem.parameters.baseAsset.formula.error = "Fee Structure Error: " + err.message } } } @@ -550,16 +595,10 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { /* Assistant Info */ - let ticker - - if (process.env.START_MODE === "live" && i === candles.length - 2) { - ticker = assistant.getTicker() - } else { - ticker = { - bid: candle.close, - ask: candle.close, - last: candle.close - } + let ticker = { + bid: candle.close, + ask: candle.close, + last: candle.close } //let LRC = LRCMap.get(candle.begin); @@ -1280,8 +1319,6 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { takeProfitStage = 'Open Stage'; stopLossPhase = 1; takeProfitPhase = 1; - currentTrade.begin = candle.begin; - currentTrade.beginRate = strategy.positionRate; if (processingDailyFiles) { if (positionedAtYesterday) { @@ -1290,8 +1327,6 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { yesterday.takeProfitStage = takeProfitStage; yesterday.stopLossPhase = stopLossPhase; yesterday.takeProfitPhase = takeProfitPhase; - yesterday.currentTrade.begin = currentTrade.begin; - yesterday.currentTrade.beginRate = currentTrade.beginRate; } } @@ -1583,20 +1618,23 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if ((baseAsset === 'BTC' && candle.max >= stopLoss) || (baseAsset !== 'BTC' && candle.min <= stopLoss)) { - /* Check the Slipagge */ + /* + Hit Point Validation - let stopLossHitPoint = stopLoss + 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 (baseAsset === 'BTC') { - if (stopLossHitPoint < candle.open) { stopLossHitPoint = candle.open} - if (stopLossHitPoint < candle.close) { stopLossHitPoint = candle.close } + if (stopLoss < candle.min) { stopLoss = candle.min } } else { - if (stopLossHitPoint > candle.open) { stopLossHitPoint = candle.open } - if (stopLossHitPoint > candle.close) { stopLossHitPoint = candle.close } + if (stopLoss > candle.max) { stopLoss = candle.max } } - let slippedStopLoss = stopLossHitPoint - let slippageAmount = stopLossHitPoint * slippage / 100 + let slippedStopLoss = stopLoss + + /* Apply the Slippage */ + let slippageAmount = slippedStopLoss * slippage.stopLoss / 100 if (baseAsset === 'BTC') { slippedStopLoss = slippedStopLoss + slippageAmount @@ -1604,17 +1642,25 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { slippedStopLoss = slippedStopLoss - slippageAmount } - if (baseAsset === 'BTC') { - strategy.positionSize = balanceAssetB / slippedStopLoss; - strategy.positionRate = slippedStopLoss; + let finalStopLoss = slippedStopLoss; - balanceAssetA = balanceAssetA + balanceAssetB / slippedStopLoss; + let feePaid = 0 + + if (baseAsset === 'BTC') { + strategy.positionSize = balanceAssetB / finalStopLoss; + strategy.positionRate = finalStopLoss; + + feePaid = balanceAssetB / finalStopLoss * feeStructure.taker / 100 + + balanceAssetA = balanceAssetA + balanceAssetB / finalStopLoss - feePaid; balanceAssetB = 0; } else { - strategy.positionSize = balanceAssetA * slippedStopLoss; - strategy.positionRate = slippedStopLoss; + strategy.positionSize = balanceAssetA * finalStopLoss; + strategy.positionRate = finalStopLoss; - balanceAssetB = balanceAssetB + balanceAssetA * slippedStopLoss; + feePaid = balanceAssetA * finalStopLoss * feeStructure.taker / 100 + + balanceAssetB = balanceAssetB + balanceAssetA * finalStopLoss - feePaid; balanceAssetA = 0; } @@ -1625,7 +1671,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } - marketRate = slippedStopLoss; + marketRate = finalStopLoss; type = '"Close@StopLoss"'; strategyStage = 'Close Stage'; stopLossStage = 'No Stage'; @@ -1633,8 +1679,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { currentTrade.end = candle.end; currentTrade.status = 1; currentTrade.exitType = 1; - currentTrade.endRate = slippedStopLoss; - + currentTrade.endRate = finalStopLoss; + closePositionNow = true; if (processingDailyFiles) { @@ -1654,38 +1700,48 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if ((baseAsset === 'BTC' && candle.min <= takeProfit) || (baseAsset !== 'BTC' && candle.max >= takeProfit)) { - /* Check the Slipagge */ + /* + Hit Point Validation: - let takeProfitHitPoint = stopLoss + 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 (baseAsset === 'BTC') { - if (takeProfitHitPoint > candle.open) { takeProfitHitPoint = candle.open } - if (takeProfitHitPoint > candle.close) { takeProfitHitPoint = candle.close } + if (takeProfit > candle.max) { takeProfit = candle.max } } else { - if (takeProfitHitPoint < candle.open) { takeProfitHitPoint = candle.open } - if (takeProfitHitPoint < candle.close) { takeProfitHitPoint = candle.close } + if (takeProfit < candle.min) { takeProfit = candle.min } } let slippedTakeProfit = takeProfit - let slippageAmount = stopLoss * slippage / 100 + /* Apply the Slippage */ + let slippageAmount = slippedTakeProfit * slippage.takeProfit / 100 if (baseAsset === 'BTC') { - slippedTakeProfit = slippedTakeProfit - slippageAmount - } else { slippedTakeProfit = slippedTakeProfit + slippageAmount + } else { + slippedTakeProfit = slippedTakeProfit - slippageAmount } - if (baseAsset === 'BTC') { - strategy.positionSize = balanceAssetB / slippedTakeProfit; - strategy.positionRate = slippedTakeProfit; + let finalTakeProfit = slippedTakeProfit; - balanceAssetA = balanceAssetA + balanceAssetB / slippedTakeProfit; + let feePaid = 0 + + if (baseAsset === 'BTC') { + strategy.positionSize = balanceAssetB / finalTakeProfit; + strategy.positionRate = finalTakeProfit; + + feePaid = balanceAssetB / finalTakeProfit * feeStructure.taker / 100 + + balanceAssetA = balanceAssetA + balanceAssetB / finalTakeProfit - feePaid; balanceAssetB = 0; } else { - strategy.positionSize = balanceAssetA * slippedTakeProfit; - strategy.positionRate = slippedTakeProfit; + strategy.positionSize = balanceAssetA * finalTakeProfit; + strategy.positionRate = finalTakeProfit; - balanceAssetB = balanceAssetB + balanceAssetA * slippedTakeProfit; + feePaid = balanceAssetA * finalTakeProfit * feeStructure.taker / 100 + + balanceAssetB = balanceAssetB + balanceAssetA * finalTakeProfit - feePaid; balanceAssetA = 0; } @@ -1697,7 +1753,7 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } - marketRate = slippedTakeProfit; + marketRate = finalTakeProfit; type = '"Close@TakeProfit"'; strategyStage = 'Close Stage'; stopLossStage = 'No Stage'; @@ -1706,8 +1762,8 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { currentTrade.end = candle.end; currentTrade.status = 1; currentTrade.exitType = 2; - currentTrade.endRate = slippedTakeProfit; - + currentTrade.endRate = finalTakeProfit; + closePositionNow = true; if (processingDailyFiles) { @@ -1736,22 +1792,15 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { let strategy = tradingSystem.strategies[currentStrategyIndex]; tradePositionSize = strategy.positionSize; + tradePositionRate = strategy.positionRate; - if (i === candles.length - 2) { /* Only at the last closed candle I can take the value from the formula, since the use of ticker could alter the value at previous candles.*/ - tradePositionRate = strategy.positionRate; + /* We take what was calculated at the formula and apply the slippage. */ + let slippageAmount = tradePositionRate * slippage.positionRate / 100 + + if (baseAsset === 'BTC') { + tradePositionRate = tradePositionRate - slippageAmount } else { - - /* Check the Slipagge */ - - tradePositionRate = candle.close; - - let slippageAmount = tradePositionRate * slippage / 100 - - if (baseAsset === 'BTC') { - tradePositionRate = tradePositionRate - slippageAmount - } else { - tradePositionRate = tradePositionRate + slippageAmount - } + tradePositionRate = tradePositionRate + slippageAmount } if (processingDailyFiles) { @@ -1761,6 +1810,18 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { } } + /* Update the trade record information. */ + currentTrade.begin = candle.begin; + currentTrade.beginRate = tradePositionRate; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentTrade.begin = currentTrade.begin; + yesterday.currentTrade.beginRate = currentTrade.beginRate; + } + } + + /* Check if we need to execute. */ if (i > 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 (process.env.START_MODE === "live") { @@ -1972,11 +2033,19 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { lastTradeProfitLoss = 0; lastTradeROI = 0; + let feePaid = 0 + if (baseAsset === 'BTC') { - balanceAssetB = balanceAssetB + tradePositionSize * tradePositionRate; + + feePaid = tradePositionSize * tradePositionRate * feeStructure.taker / 100 + + balanceAssetB = balanceAssetB + tradePositionSize * tradePositionRate - feePaid; balanceAssetA = balanceAssetA - tradePositionSize; } else { - balanceAssetA = balanceAssetA + tradePositionSize / tradePositionRate; + + feePaid = tradePositionSize / tradePositionRate * feeStructure.taker / 100 + + balanceAssetA = balanceAssetA + tradePositionSize / tradePositionRate - feePaid; balanceAssetB = balanceAssetB - tradePositionSize; } @@ -2257,8 +2326,6 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { if (processingDailyFiles) { if (positionedAtYesterday) { - yesterday.currentStrategyIndex = currentStrategyIndex; - yesterday.strategyStage = strategyStage; yesterday.stopLossStage = stopLossStage; yesterday.takeProfitStage = takeProfitStage; yesterday.stopLossPhase = stopLossPhase; @@ -2293,6 +2360,14 @@ exports.newCommons = function newCommons(bot, logger, UTILITIES) { currentStrategyIndex = -1; strategyStage = 'No Stage'; + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.currentStrategyIndex = currentStrategyIndex; + yesterday.strategyStage = strategyStage; + } + } + timerToCloseStage = 0 if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Closing the Closing Stage -> Exiting Close Stage."); } diff --git a/TradingEngineProcessMainLoop.js b/TradingEngineProcessMainLoop.js index bcfdd1727..b6dbc7661 100644 --- a/TradingEngineProcessMainLoop.js +++ b/TradingEngineProcessMainLoop.js @@ -54,8 +54,8 @@ } USER_BOT_MODULE = {} - //USER_BOT_MODULE.newUserBot = require(ROOT_DIR + 'User.Bot').newUserBot // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. - USER_BOT_MODULE.newUserBot = eval(text); // Use this for production + USER_BOT_MODULE.newUserBot = require(ROOT_DIR + 'User.Bot').newUserBot // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. + //USER_BOT_MODULE.newUserBot = eval(text); // Use this for production filePath = global.DEV_TEAM + "/" + "bots" + "/" + bot.repo; filePath += "/Commons.js" @@ -70,8 +70,8 @@ return; } COMMONS_MODULE = {} - //COMMONS_MODULE.newCommons = require(ROOT_DIR + 'Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. - COMMONS_MODULE.newCommons = eval(text); // Use this for production + COMMONS_MODULE.newCommons = require(ROOT_DIR + 'Commons').newCommons // Use this for a better debugging experience. You need to bring this js module to this folder in order to work. + //COMMONS_MODULE.newCommons = eval(text); // Use this for production callBackFunction(global.DEFAULT_OK_RESPONSE); }