From 0fc5cb29292a579837088620945025d208467ba2 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sun, 2 Aug 2020 13:04:00 +0200 Subject: [PATCH] All calculated --- LowFrequencyTrading/ExchangeAPI.js | 12 +- LowFrequencyTrading/TradingExecution.js | 290 ++++++++++++++---------- LowFrequencyTrading/TradingPosition.js | 136 +++-------- LowFrequencyTrading/TradingSystem.js | 141 ++++++++++-- 4 files changed, 318 insertions(+), 261 deletions(-) diff --git a/LowFrequencyTrading/ExchangeAPI.js b/LowFrequencyTrading/ExchangeAPI.js index 3c4861120..b94740fe3 100644 --- a/LowFrequencyTrading/ExchangeAPI.js +++ b/LowFrequencyTrading/ExchangeAPI.js @@ -77,17 +77,7 @@ let type // CCXT: a string literal type of order, ccxt currently unifies market and limit orders only let side // CCXT: a string literal for the direction of your order, buy or sell let symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset // CCXT: a string literal symbol of the market you wish to trade on, like BTC/USD, ZEC/ETH, DOGE/DASH, etc - let amount // CCXT: how much of currency you want to trade - - /* - We need at the amount a value denominated in Base Asset, if we do not - have it, then we convert the value we have in Quoteed Asset. - */ - if (tradingEngineOrder.orderBaseAsset.size.value > 0) { - amount = tradingEngineOrder.orderBaseAsset.size.value - } else { - amount = tradingEngineOrder.orderQuotedAsset.size.value / tradingEngineOrder.rate.value - } + let amount = tradingEngineOrder.orderBaseAsset.size.value // CCXT: how much of currency you want to trade, in Base Asset. switch (tradingSystemOrder.type) { case 'Market Buy Order': { diff --git a/LowFrequencyTrading/TradingExecution.js b/LowFrequencyTrading/TradingExecution.js index 2eedca830..154a52db3 100644 --- a/LowFrequencyTrading/TradingExecution.js +++ b/LowFrequencyTrading/TradingExecution.js @@ -217,11 +217,11 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE /* Check Size: We are not going to create Orders which size is equal or less to zero. */ if (traingEngineOrderAsset.size.value <= 0) { return } - /* Place Order at the Exchange */ + /* Place Order at the Exchange, if needed. */ let result = await createOrderAtExchange(tradingSystemOrder, tradingEngineOrder) if (result !== true) { return } - /* Update Stage Counters */ + /* Update Stage Size */ tradingEngineStage.stageBaseAsset.size.value = tradingEngineStage.stageBaseAsset.size.value + tradingEngineOrder.orderBaseAsset.size.value tradingEngineStage.stageBaseAsset.size.value = global.PRECISE(tradingEngineStage.stageBaseAsset.size.value, 10) tradingEngineStage.stageQuotedAsset.size.value = tradingEngineStage.stageQuotedAsset.size.value + tradingEngineOrder.orderQuotedAsset.size.value @@ -392,18 +392,24 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE /* Filter by what is defined at the Strategy */ if (tradingSystemOrder.simulatedExchangeEvents === undefined) { return } - let previousSizeFilled = tradingEngineOrderAsset.sizeFilled.value - let previousPercentageFilled = tradingEngineOrder.orderStatistics.percentageFilled.value - let previousAmountReceived = tradingEngineOrder.orderStatistics.amountReceived.value - let previousFeesPaid = tradingEngineOrder.orderStatistics.feesPaid.value + let previousBaseAssetSizeFilled = tradingEngineOrder.orderBaseAsset.sizeFilled.value + let previousQuotedAssetSizeFilled = tradingEngineOrder.orderQuotedAsset.sizeFilled.value + let previousBaseAssetFeesPaid = tradingEngineOrder.orderBaseAsset.feesPaid.value + let previousQuotedAssetFeesPaid = tradingEngineOrder.orderQuotedAsset.feesPaid.value actualRateSimulation() feesPaidSimulation() - orderFillingSimulation() + percentageFilledSimulation() sizeFilledSimulation() - amountReceivedSimulation() - doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousFeesPaid) + doTheAccounting( + tradingSystemOrder, + tradingEngineOrder, + previousBaseAssetSizeFilled, + previousQuotedAssetSizeFilled, + previousBaseAssetFeesPaid, + previousQuotedAssetFeesPaid + ) /* If the Stage is Closing and this order is still open, we need to cancel it now */ if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') { @@ -462,8 +468,21 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE /* Based on the Trading System Definition */ if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid !== undefined) { if (tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage !== undefined) { - if (tradingEngineOrder.orderStatistics.feesPaid.value === tradingEngineOrder.orderStatistics.feesPaid.config.initialValue) { - tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100 + if (tradingEngineOrder.orderBaseAsset.feesPaid.value === tradingEngineOrder.orderBaseAsset.feesPaid.config.initialValue) { + + tradingEngineOrder.orderBaseAsset.feesPaid.value = + tradingEngineOrder.orderBaseAsset.size.value * + tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100 + + calculatedBasedOnTradingSystem = true + } + + if (tradingEngineOrder.orderQuotedAsset.feesPaid.value === tradingEngineOrder.orderQuotedAsset.feesPaid.config.initialValue) { + + tradingEngineOrder.orderQuotedAsset.feesPaid.value = + tradingEngineOrder.orderQuotedAsset.size.value * + tradingSystemOrder.simulatedExchangeEvents.simulatedFeesPaid.config.percentage / 100 + calculatedBasedOnTradingSystem = true } } @@ -474,19 +493,36 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE /* Fees are simulated based on the Session Paremeters */ switch (tradingEngineOrder.type) { case 'Market Order': { - tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * bot.SESSION.parameters.feeStructure.config.taker / 100 + + tradingEngineOrder.orderBaseAsset.feesPaid.value = + tradingEngineOrder.orderBaseAsset.size.value * + bot.SESSION.parameters.feeStructure.config.taker / 100 + + tradingEngineOrder.orderQuotedAsset.feesPaid.value = + tradingEngineOrder.orderQuotedAsset.size.value * + bot.SESSION.parameters.feeStructure.config.taker / 100 + break } case 'Limit Order': { - tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * bot.SESSION.parameters.feeStructure.config.maker / 100 + + tradingEngineOrder.orderBaseAsset.feesPaid.value = + tradingEngineOrder.orderBaseAsset.size.value * + bot.SESSION.parameters.feeStructure.config.maker / 100 + + tradingEngineOrder.orderQuotedAsset.feesPaid.value = + tradingEngineOrder.orderQuotedAsset.size.value * + bot.SESSION.parameters.feeStructure.config.maker / 100 + break } } } - tradingEngineOrder.orderStatistics.feesPaid.value = global.PRECISE(tradingEngineOrder.orderStatistics.feesPaid.value, 10) + tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10) + tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10) } - function orderFillingSimulation() { + function percentageFilledSimulation() { /* Order Filling Simulation */ if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) { if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) { @@ -515,34 +551,16 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE function sizeFilledSimulation() { /* Size Filled */ - tradingEngineOrder.orderStatistics.sizeFilled.value = tradingEngineOrder.size.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100 - tradingEngineOrder.orderStatistics.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.sizeFilled.value, 10) - } + tradingEngineOrder.orderBaseAsset.sizeFilled.value = + tradingEngineOrder.orderBaseAsset.size.value * + tradingEngineOrder.orderStatistics.percentageFilled.value / 100 - function amountReceivedSimulation() { - /* Amount Received */ - let calculatedBasedOnOrderDefinition = false + tradingEngineOrder.orderQuotedAsset.sizeFilled.value = + tradingEngineOrder.orderQuotedAsset.size.value * + tradingEngineOrder.orderStatistics.percentageFilled.value / 100 - /* Based on the Order Definition */ - if (tradingSystemOrder.simulatedExchangeEvents !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) { - if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) { - let simulatedActualRate = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id) - if (simulatedActualRate !== undefined) { - tradingEngineOrder.orderStatistics.amountReceived.value = tradingEngineOrder.orderStatistics.sizeFilled.value * simulatedActualRate - tradingEngineOrder.orderStatistics.amountReceived.value = global.PRECISE(tradingEngineOrder.orderStatistics.amountReceived.value, 10) - calculatedBasedOnOrderDefinition = true - } - } - } - } - - /* Based on Candle Close Value */ - if (calculatedBasedOnOrderDefinition === false) { - let simulatedActualRate = tradingEngine.current.candle.close.value - tradingEngineOrder.orderStatistics.amountReceived.value = tradingEngineOrder.orderStatistics.sizeFilled.value * simulatedActualRate - tradingEngineOrder.orderStatistics.amountReceived.value = global.PRECISE(tradingEngineOrder.orderStatistics.amountReceived.value, 10) - } + tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10) + tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10) } } @@ -776,24 +794,57 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE tradingEngine.current.balance.quotedAsset.value = global.PRECISE(tradingEngine.current.balance.quotedAsset.value, 10) } - function simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) { + } - /* Filter by Session Type */ - switch (bot.SESSION.type) { - case 'Backtesting Session': { - break - } - case 'Live Trading Session': { - return - } - case 'Fordward Testing Session': { - return - } - case 'Paper Trading Session': { - break - } + function simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) { + + /* Filter by Session Type */ + switch (bot.SESSION.type) { + case 'Backtesting Session': { + break } + case 'Live Trading Session': { + return + } + case 'Fordward Testing Session': { + return + } + case 'Paper Trading Session': { + break + } + } + /* Close this Order */ + tradingEngineOrder.status.value = 'Closed' + tradingEngineOrder.exitType.value = exitType + + /* Initialize this */ + tradingEngine.current.distanceToEvent.closeOrder.value = 1 + + recalculateStageSize(tradingEngineOrder) + } + + async function exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) { + + /* Filter by Session Type */ + switch (bot.SESSION.type) { + case 'Backtesting Session': { + return + } + case 'Live Trading Session': { + break + } + case 'Fordward Testing Session': { + break + } + case 'Paper Trading Session': { + return + } + } + + /* Check if we can cancel the order at the Exchange. */ + let result = await exchangeAPIModule.cancelOrder(tradingSystemOrder, tradingEngineOrder) + if (result === true) { /* Close this Order */ tradingEngineOrder.status.value = 'Closed' tradingEngineOrder.exitType.value = exitType @@ -801,85 +852,74 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE /* Initialize this */ tradingEngine.current.distanceToEvent.closeOrder.value = 1 - /* Since the order was cancelled, we remove the unfilled amount of the order from here */ - let unfilledPercentage = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value - let unfilledSize = tradingEngineOrder.size.value * unfilledPercentage / 100 - stageOrdersSize.value = stageOrdersSize.value - unfilledSize - stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10) + /* + Perhaps the order was filled a bit more between the last time we checked and when it was cancelled. + To sync our accounting, we need to check the order one last time and if it changed, fix it. + */ + + let order = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder) + + if (order === undefined) { return } + + syncWithExchange(tradingSystemOrder, tradingEngineOrder, order) + + recalculateStageSize() } + } - async function exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) { + function recalculateStageSize(tradingEngineOrder) { + /* + Since the order is Cancelled, we need to adjust the stage size. Remember that the Stage Size + accumulates for each asset, the order size placed at the exchange. A cancelation means that + only the part filled can be considered placed, so we need to substract from the stage size + the remainder. To achieve this with the information we currently have, we are going first + to unaccount the order size, and the account only the sizeFilled + the feesPaid. + */ + tradingEngineStage.stageBaseAsset.size.value = + tradingEngineStage.stageBaseAsset.size.value - + tradingEngineOrder.orderBaseAsset.size.value + tradingEngineStage.stageQuotedAsset.size.value = + tradingEngineStage.stageQuotedAsset.size.value - + tradingEngineOrder.orderQuotedAsset.size.value - /* Filter by Session Type */ - switch (bot.SESSION.type) { - case 'Backtesting Session': { - return + tradingEngineStage.stageBaseAsset.size.value = + tradingEngineStage.stageBaseAsset.size.value + + tradingEngineOrder.orderBaseAsset.sizeFilled.value + + tradingEngineOrder.orderBaseAsset.feesPaid.value + tradingEngineStage.stageQuotedAsset.size.value = + tradingEngineStage.stageQuotedAsset.size.value + + tradingEngineOrder.orderQuotedAsset.sizeFilled.value + + tradingEngineOrder.orderQuotedAsset.feesPaid.value + + tradingEngineStage.stageBaseAsset.size.value = global.PRECISE(tradingEngineStage.stageBaseAsset.size.value, 10) + tradingEngineStage.stageQuotedAsset.size.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.size.value, 10) + } + + function checkOrderEvent(event, order, executionAlgorithm, executionNode) { + if (event !== undefined) { + for (let k = 0; k < event.situations.length; k++) { + let situation = event.situations[k] + let passed + if (situation.conditions.length > 0) { + passed = true } - case 'Live Trading Session': { - break - } - case 'Fordward Testing Session': { - break - } - case 'Paper Trading Session': { - return - } - } - /* Check if we can cancel the order at the Exchange. */ - let result = await exchangeAPIModule.cancelOrder(tradingSystemOrder, tradingEngineOrder) - if (result === true) { - /* Close this Order */ - tradingEngineOrder.status.value = 'Closed' - tradingEngineOrder.exitType.value = exitType + passed = tradingSystem.checkConditions(situation, passed) - /* Initialize this */ - tradingEngine.current.distanceToEvent.closeOrder.value = 1 + tradingSystem.values.push([situation.id, passed]) + if (passed) { + tradingSystem.highlights.push(situation.id) + tradingSystem.highlights.push(event.id) + tradingSystem.highlights.push(order.id) + tradingSystem.highlights.push(executionAlgorithm.id) + tradingSystem.highlights.push(executionNode.id) - /* Since the order was cancelled, we remove the unfilled amount of the order from here */ - let unfilledPercentage = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value - let unfilledSize = tradingEngineOrder.size.value * unfilledPercentage / 100 - stageOrdersSize.value = stageOrdersSize.value - unfilledSize - stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10) - - /* - Perhaps the order was filled a bit more between the last time we checked and when it was cancelled. - To sync our accounting, we need to check the order one last time and if it changed, fix it. - */ - - let order = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder) - - if (order === undefined) { return } - - syncWithExchange(tradingSystemOrder, tradingEngineOrder, order) - } - } - - function checkOrderEvent(event, order, executionAlgorithm, executionNode) { - if (event !== undefined) { - for (let k = 0; k < event.situations.length; k++) { - let situation = event.situations[k] - let passed - if (situation.conditions.length > 0) { - passed = true - } - - passed = tradingSystem.checkConditions(situation, passed) - - tradingSystem.values.push([situation.id, passed]) - if (passed) { - tradingSystem.highlights.push(situation.id) - tradingSystem.highlights.push(event.id) - tradingSystem.highlights.push(order.id) - tradingSystem.highlights.push(executionAlgorithm.id) - tradingSystem.highlights.push(executionNode.id) - - announcementsModule.makeAnnoucements(event) - return situation.name // if the event is triggered, we return the name of the situation that passed - } + announcementsModule.makeAnnoucements(event) + return situation.name // if the event is triggered, we return the name of the situation that passed } } } } } +} diff --git a/LowFrequencyTrading/TradingPosition.js b/LowFrequencyTrading/TradingPosition.js index 0f1d3e074..6e3ef5790 100644 --- a/LowFrequencyTrading/TradingPosition.js +++ b/LowFrequencyTrading/TradingPosition.js @@ -9,10 +9,6 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng closePosition: closePosition, applyStopLossFormula: applyStopLossFormula, applyTakeProfitFormula: applyTakeProfitFormula, - preventStopLossDistortion: preventStopLossDistortion, - applySlippageToStopLoss: applySlippageToStopLoss, - preventTakeProfitDistortion: preventTakeProfitDistortion, - applySlippageToTakeProfit: applySlippageToTakeProfit, updateStopLoss: updateStopLoss, updateTakeProfit: updateTakeProfit, initializeSizeAndRate: initializeSizeAndRate, @@ -20,6 +16,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng updateStatus: updateStatus, updateCounters: updateCounters, updateStatistics: updateStatistics, + updateResults: updateResults, resetPosition: resetPosition, initialize: initialize, finalize: finalize @@ -179,71 +176,6 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng } } - 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 @@ -264,45 +196,47 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng function updateStatistics() { - if (bot.sessionAndMarketBaseAssetsAreEqual) { - /* Profit Loss Calculation */ - tradingEngine.current.position.positionStatistics.profitLoss.value = - tradingEngine.current.balance.baseAsset.value - - tradingEngine.previous.balance.baseAsset.value + - tradingEngine.current.balance.quotedAsset.value / tradingEngine.current.position.endRate.value - - tradingEngine.previous.balance.quotedAsset.value / tradingEngine.current.position.beginRate.value - - /* ROI Calculation */ - tradingEngine.current.position.positionStatistics.ROI.value = - tradingEngine.current.position.positionStatistics.profitLoss.value * 100 / - tradingEngine.current.position.size.value - } else { - /* Profit Loss Calculation */ - tradingEngine.current.position.positionStatistics.profitLoss.value = - tradingEngine.current.balance.baseAsset.value * tradingEngine.current.position.endRate.value - - tradingEngine.previous.balance.baseAsset.value * tradingEngine.current.position.beginRate.value + - tradingEngine.current.balance.quotedAsset.value - - tradingEngine.previous.balance.quotedAsset.value - - /* ROI Calculation */ - tradingEngine.current.position.positionStatistics.ROI.value = - tradingEngine.current.position.positionStatistics.profitLoss.value * 100 / - (tradingEngine.current.position.size.value * tradingEngine.current.position.beginRate.value) - } - /* Days Calculation */ - tradingEngine.current.position.positionStatistics.days.value = 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 - tradingEngine.current.position.positionStatistics.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionStatistics.profitLoss.value, 10) - tradingEngine.current.position.positionStatistics.ROI.value = global.PRECISE(tradingEngine.current.position.positionStatistics.ROI.value, 10) tradingEngine.current.position.positionStatistics.days.value = global.PRECISE(tradingEngine.current.position.positionStatistics.days.value, 10) + } + + function updateResults() { + /* Profit Loss Calculation */ + tradingEngine.current.position.positionBaseAsset.profitLoss.value = + tradingEngine.current.balance.baseAsset.value - + tradingEngine.previous.balance.baseAsset.value + + tradingEngine.current.position.positionQuotedAsset.profitLoss.value = + tradingEngine.current.balance.quotedAsset.value - + tradingEngine.previous.balance.quotedAsset.value + + tradingEngine.current.position.positionBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.profitLoss.value, 10) + + /* ROI Calculation */ + tradingEngine.current.position.positionBaseAsset.ROI.value = + tradingEngine.current.position.positionBaseAsset.profitLoss.value * 100 / + tradingEngine.current.position.positionBaseAsset.size.value + + tradingEngine.current.position.positionQuotedAsset.ROI.value = + tradingEngine.current.position.positionQuotedAsset.profitLoss.value * 100 / + tradingEngine.current.position.positionQuotedAsset.size.value + + tradingEngine.current.position.positionBaseAsset.ROI.value = global.PRECISE(tradingEngine.current.position.positionBaseAsset.ROI.value, 10) /* Hit Fail Calculation */ - if (tradingEngine.current.position.positionStatistics.ROI.value > 0) { - tradingEngine.current.position.positionStatistics.hitFail.value = 'HIT' + if (tradingEngine.current.position.positionBaseAsset.ROI.value > 0) { + tradingEngine.current.position.positionBaseAsset.hitFail.value = 'HIT' } else { - tradingEngine.current.position.positionStatistics.hitFail.value = 'FAIL' + tradingEngine.current.position.positionBaseAsset.hitFail.value = 'FAIL' + } + if (tradingEngine.current.position.positionQuotedAsset.ROI.value > 0) { + tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'HIT' + } else { + tradingEngine.current.position.positionQuotedAsset.hitFail.value = 'FAIL' } } } \ No newline at end of file diff --git a/LowFrequencyTrading/TradingSystem.js b/LowFrequencyTrading/TradingSystem.js index a66ba79b3..45c6a0d11 100644 --- a/LowFrequencyTrading/TradingSystem.js +++ b/LowFrequencyTrading/TradingSystem.js @@ -902,8 +902,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM ) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Stop Loss was hit.') - tradingPositionModule.preventStopLossDistortion() - tradingPositionModule.applySlippageToStopLoss() tradingPositionModule.closingPosition('Stop Loss') tradingStrategyModule.updateStageStatus('Close Stage', 'Opening') tradingStrategyModule.updateStageStatus('Manage Stage', 'Closed') @@ -918,8 +916,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM ) { logger.write(MODULE_NAME, '[INFO] checkStopLossOrTakeProfitWasHit -> Take Profit was hit.') - tradingPositionModule.preventTakeProfitDistortion() - tradingPositionModule.applySlippageToTakeProfit() tradingPositionModule.closingPosition('Take Profit') tradingStrategyModule.updateStageStatus('Close Stage', 'Opening') tradingStrategyModule.updateStageStatus('Manage Stage', 'Closed') @@ -997,28 +993,12 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM function exitPositionAndStrategy() { - /* Needed for Episode Statistics */ + /* Position Statistics & Results */ tradingPositionModule.updateStatistics() + tradingPositionModule.updateResults() - /* Recalculating Episode Counters */ - if (tradingEngine.current.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 - } + updatingEpisodeAssets() + updatingEpisodeStatistics() /* Taking Position Snapshot */ if (bot.SESSION.type === 'Backtesting Session') { @@ -1047,6 +1027,119 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM /* Distance to Events Updates */ tradingEngine.current.distanceToEvent.closePosition.value = 1 tradingEngine.current.distanceToEvent.triggerOff.value = 1 + + function updatingEpisodeAssets() { + + /* Updating Hits & Fails */ + if (tradingEngine.current.position.positionBaseAsset.profitLoss.value > 0) { + tradingEngine.episode.episodeBaseAsset.hits.value++ + } else { + tradingEngine.episode.episodeBaseAsset.fails.value++ + } + if (tradingEngine.current.position.positionQuotedAsset.profitLoss.value > 0) { + tradingEngine.episode.episodeQuotedAsset.hits.value++ + } else { + tradingEngine.episode.episodeQuotedAsset.fails.value++ + } + + /* Updating Profit Loss */ + tradingEngine.episode.episodeBaseAsset.profitLoss.value = + tradingEngine.current.balance.baseAsset.value - + sessionParameters.sessionBaseAsset.config.initialBalance + + tradingEngine.episode.episodeQuotedAsset.profitLoss.value = + tradingEngine.current.balance.quotedAsset.value - + sessionParameters.sessionQuotedAsset.config.initialBalance + + tradingEngine.episode.episodeBaseAsset.profitLoss.value = global.PRECISE(tradingEngine.episode.episodeBaseAsset.profitLoss.value, 10) + tradingEngine.episode.episodeQuotedAsset.profitLoss.value = global.PRECISE(tradingEngine.episode.episodeQuotedAsset.profitLoss.value, 10) + + /* Updating ROI */ + tradingEngine.episode.episodeBaseAsset.ROI.value = + (sessionParameters.sessionBaseAsset.config.initialBalance + tradingEngine.episode.episodeBaseAsset.profitLoss.value) / + sessionParameters.sessionBaseAsset.config.initialBalance - 1 + + tradingEngine.episode.episodeQuotedAsset.ROI.value = + (sessionParameters.sessionQuotedAsset.config.initialBalance + tradingEngine.episode.episodeQuotedAsset.profitLoss.value) / + sessionParameters.sessionQuotedAsset.config.initialBalance - 1 + + tradingEngine.episode.episodeBaseAsset.ROI.value = global.PRECISE(tradingEngine.episode.episodeBaseAsset.ROI.value, 10) + tradingEngine.episode.episodeQuotedAsset.ROI.value = global.PRECISE(tradingEngine.episode.episodeQuotedAsset.ROI.value, 10) + + /* Updating Hit Ratio */ + tradingEngine.episode.episodeBaseAsset.hitRatio.value = + tradingEngine.episode.episodeBaseAsset.hits.value / + tradingEngine.episode.episodeCounters.positions.value + + tradingEngine.episode.episodeQuotedAsset.hitRatio.value = + tradingEngine.episode.episodeQuotedAsset.hits.value / + tradingEngine.episode.episodeCounters.positions.value + + tradingEngine.episode.episodeBaseAsset.hitRatio.value = global.PRECISE(tradingEngine.episode.episodeBaseAsset.hitRatio.value, 10) + tradingEngine.episode.episodeQuotedAsset.hitRatio.value = global.PRECISE(tradingEngine.episode.episodeQuotedAsset.hitRatio.value, 10) + + /* Updating Anualized Rate Of Return */ + tradingEngine.episode.episodeBaseAsset.anualizedRateOfReturn.value = + tradingEngine.episode.episodeBaseAsset.ROI.value / + tradingEngine.episode.episodeStatistics.days.value * 365 + + tradingEngine.episode.episodeQuotedAsset.anualizedRateOfReturn.value = + tradingEngine.episode.episodeQuotedAsset.ROI.value / + tradingEngine.episode.episodeStatistics.days.value * 365 + + tradingEngine.episode.episodeBaseAsset.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.episode.episodeBaseAsset.anualizedRateOfReturn.value, 10) + tradingEngine.episode.episodeQuotedAsset.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.episode.episodeQuotedAsset.anualizedRateOfReturn.value, 10) + + /* Updating Hit or Fail */ + if (tradingEngine.episode.episodeBaseAsset.profitLoss.value > 0) { + tradingEngine.episode.episodeBaseAsset.hitFail.value = 'Hit' + } else { + tradingEngine.episode.episodeBaseAsset.hitFail.value = 'Fail' + } + if (tradingEngine.episode.episodeQuotedAsset.profitLoss.value > 0) { + tradingEngine.episode.episodeQuotedAsset.hitFail.value = 'Hit' + } else { + tradingEngine.episode.episodeQuotedAsset.hitFail.value = 'Fail' + } + } + + function updatingEpisodeStatistics() { + + /* Updating Profit Loss */ + tradingEngine.episode.episodeStatistics.profitLoss.value = + tradingEngine.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.candle.close.value + + tradingEngine.episode.episodeQuotedAsset.profitLoss.value + + tradingEngine.episode.episodeStatistics.profitLoss.value = global.PRECISE(tradingEngine.episode.episodeStatistics.profitLoss.value, 10) + + /* Updating ROI */ + tradingEngine.episode.episodeStatistics.ROI.value = + ( + sessionParameters.sessionBaseAsset.config.initialBalance * tradingEngine.current.candle.close.value + + tradingEngine.episode.episodeBaseAsset.profitLoss.value * tradingEngine.current.candle.close.value + + sessionParameters.sessionQuotedAsset.config.initialBalance + + tradingEngine.episode.episodeQuotedAsset.profitLoss.value + ) / ( + sessionParameters.sessionBaseAsset.config.initialBalance * tradingEngine.current.candle.close.value + + sessionParameters.sessionQuotedAsset.config.initialBalance + ) - 1 + + tradingEngine.episode.episodeStatistics.ROI.value = global.PRECISE(tradingEngine.episode.episodeStatistics.ROI.value, 10) + + /* Updating Anualized Rate Of Return */ + tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = + tradingEngine.episode.episodeStatistics.ROI.value / + tradingEngine.episode.episodeStatistics.days.value * 365 + + tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value = global.PRECISE(tradingEngine.episode.episodeStatistics.anualizedRateOfReturn.value, 10) + + /* Updating Hit or Fail */ + if (tradingEngine.episode.episodeStatistics.profitLoss.value > 0) { + tradingEngine.episode.episodeStatistics.hitFail.value = 'Hit' + } else { + tradingEngine.episode.episodeStatistics.hitFail.value = 'Fail' + } + } } }