Running again
This commit is contained in:
1 parent
0b581fe837
commit
84e927e420
10 files changed
+545
-368
No files matched your search
@@ -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': {
|
||||
|
||||
@@ -53,16 +53,16 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
|
||||
) {
|
||||
|
||||
/* Trading Stage Validations */
|
||||
if (tradingEngineStage.stageBaseAsset === undefined) { continue }
|
||||
if (tradingEngineStage.stageBaseAsset.size === undefined) { continue }
|
||||
if (tradingEngineStage.stageBaseAsset.sizeFilled === undefined) { continue }
|
||||
if (tradingEngineStage.stageBaseAsset.amountReceived === undefined) { continue }
|
||||
if (tradingEngineStage.stageBaseAsset.feesPaid === undefined) { continue }
|
||||
if (tradingEngineStage.stageQuotedAsset === undefined) { continue }
|
||||
if (tradingEngineStage.stageQuotedAsset.size === undefined) { continue }
|
||||
if (tradingEngineStage.stageQuotedAsset.sizeFilled === undefined) { continue }
|
||||
if (tradingEngineStage.stageQuotedAsset.amountReceived === undefined) { continue }
|
||||
if (tradingEngineStage.stageQuotedAsset.feesPaid === undefined) { continue }
|
||||
if (tradingEngineStage.stageBaseAsset === undefined) { return }
|
||||
if (tradingEngineStage.stageBaseAsset.size === undefined) { return }
|
||||
if (tradingEngineStage.stageBaseAsset.sizeFilled === undefined) { return }
|
||||
if (tradingEngineStage.stageBaseAsset.amountReceived === undefined) { return }
|
||||
if (tradingEngineStage.stageBaseAsset.feesPaid === undefined) { return }
|
||||
if (tradingEngineStage.stageQuotedAsset === undefined) { return }
|
||||
if (tradingEngineStage.stageQuotedAsset.size === undefined) { return }
|
||||
if (tradingEngineStage.stageQuotedAsset.sizeFilled === undefined) { return }
|
||||
if (tradingEngineStage.stageQuotedAsset.amountReceived === undefined) { return }
|
||||
if (tradingEngineStage.stageQuotedAsset.feesPaid === undefined) { return }
|
||||
|
||||
/*
|
||||
The user choice of asset to define the size of the position,
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,23 +16,27 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
|
||||
updateStatus: updateStatus,
|
||||
updateCounters: updateCounters,
|
||||
updateStatistics: updateStatistics,
|
||||
updateResults: updateResults,
|
||||
resetPosition: resetPosition,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let tradingEngine
|
||||
let tradingSystem
|
||||
let sessionParameters
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize() {
|
||||
tradingSystem = bot.simulationState.tradingSystem
|
||||
tradingEngine = bot.simulationState.tradingEngine
|
||||
sessionParameters = bot.SESSION.parameters
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
tradingEngine = undefined
|
||||
tradingSystem = undefined
|
||||
sessionParameters = undefined
|
||||
}
|
||||
|
||||
@@ -179,71 +179,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 +199,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'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,12 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
|
||||
We will use the codeName of the Record Property to match it with
|
||||
the any of the properties of the Root Node to get the Target Node.
|
||||
*/
|
||||
let targetNode = propertyRootNode[recordProperty.config.codeName]
|
||||
let targetNode
|
||||
try {
|
||||
targetNode = propertyRootNode[recordProperty.config.codeName]
|
||||
} catch (err) {
|
||||
console.log(err.stack)
|
||||
}
|
||||
/*
|
||||
If the codeName of the Record Property can not match the name of the property at
|
||||
the target node, the user can explicitly specify the property name at the configuration,
|
||||
@@ -167,7 +172,11 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
|
||||
value = targetNode.value
|
||||
|
||||
if (recordProperty.config.decimals !== undefined) {
|
||||
value = Number(value.toFixed(recordProperty.config.decimals))
|
||||
try {
|
||||
value = Number(value.toFixed(recordProperty.config.decimals))
|
||||
} catch (err) {
|
||||
console.log(err.stack)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
|
||||
@@ -62,9 +62,6 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
let heartBeatDate
|
||||
let previousHeartBeatDate
|
||||
|
||||
/* This determines where we are standing */
|
||||
bot.sessionAndMarketBaseAssetsAreEqual = (sessionParameters.sessionBaseAsset.referenceParent.referenceParent.config.codeName === bot.market.baseAsset)
|
||||
|
||||
initializeLoop()
|
||||
|
||||
function initializeLoop() {
|
||||
@@ -363,36 +360,49 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
|
||||
if (
|
||||
tradingEngine.current.strategy.index.value === tradingEngine.current.strategy.index.config.initialValue
|
||||
) {
|
||||
/*
|
||||
First thing to do is to check if we are below the minimum balance, or above the maximun balance.
|
||||
If we are, we are not gooing to trigger on again.
|
||||
/*
|
||||
We will perform this check only when we are not inside a position,
|
||||
because there the balances have shifted from their resting position.
|
||||
*/
|
||||
let minimumBalance
|
||||
let maximumBalance
|
||||
let balance
|
||||
|
||||
if (bot.sessionAndMarketBaseAssetsAreEqual) {
|
||||
balance = tradingEngine.current.balance.baseAsset.value
|
||||
minimumBalance = sessionParameters.sessionBaseAsset.config.minimumBalance
|
||||
maximumBalance = sessionParameters.sessionBaseAsset.config.maximumBalance
|
||||
} else {
|
||||
balance = tradingEngine.current.balance.quotedAsset.value
|
||||
minimumBalance = sessionParameters.sessionQuotedAsset.config.minimumBalance
|
||||
maximumBalance = sessionParameters.sessionQuotedAsset.config.maximumBalance
|
||||
let stopRunningDate = (new Date(tradingEngine.current.candle.begin.value)).toLocaleString()
|
||||
|
||||
if (sessionParameters.sessionBaseAsset.config.minimumBalance !== undefined) {
|
||||
if (tradingEngine.current.balance.baseAsset.value <= sessionParameters.sessionBaseAsset.config.minimumBalance) {
|
||||
const errorMessage = 'Min Balance reached @ ' + stopRunningDate
|
||||
tradingSystem.errors.push([tradingSystem.id, errorMessage])
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[WARN] runSimulation -> checkMinimunAndMaximunBalance -> ' + errorMessage) }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let stopRunningDate = new Date(tradingEngine.current.candle.begin.value)
|
||||
if (balance <= minimumBalance) {
|
||||
tradingSystem.error = 'Min Balance @ ' + stopRunningDate.toLocaleString()
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkMinimunAndMaximunBalance -> ' + tradingSystem.error) }
|
||||
return false
|
||||
if (sessionParameters.sessionBaseAsset.config.maximumBalance !== undefined) {
|
||||
if (tradingEngine.current.balance.baseAsset.value >= sessionParameters.sessionBaseAsset.config.maximumBalance) {
|
||||
const errorMessage = 'Max Balance reached @ ' + stopRunningDate
|
||||
tradingSystem.errors.push([tradingSystem.id, errorMessage])
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[WARN] runSimulation -> checkMinimunAndMaximunBalance -> ' + errorMessage) }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (balance >= maximumBalance) {
|
||||
tradingSystem.error = 'Max Balance @ ' + stopRunningDate.toLocaleString()
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> checkMinimunAndMaximunBalance -> ' + tradingSystem.error) }
|
||||
return false
|
||||
if (sessionParameters.sessionQuotedAsset.config.minimumBalance !== undefined) {
|
||||
if (tradingEngine.current.balance.quotedAsset.value <= sessionParameters.sessionQuotedAsset.config.minimumBalance) {
|
||||
const errorMessage = 'Min Balance reached @ ' + stopRunningDate
|
||||
tradingSystem.errors.push([tradingSystem.id, errorMessage])
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[WARN] runSimulation -> checkMinimunAndMaximunBalance -> ' + errorMessage) }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (sessionParameters.sessionQuotedAsset.config.maximumBalance !== undefined) {
|
||||
if (tradingEngine.current.balance.quotedAsset.value >= sessionParameters.sessionQuotedAsset.config.maximumBalance) {
|
||||
const errorMessage = 'Max Balance reached @ ' + stopRunningDate
|
||||
tradingSystem.errors.push([tradingSystem.id, errorMessage])
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[WARN] runSimulation -> checkMinimunAndMaximunBalance -> ' + errorMessage) }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -549,7 +549,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
false,
|
||||
tradingEngine.current.position.positionBaseAsset.size, // Stage Size Limit
|
||||
tradingEngine.current.position.positionQuotedAsset.size, // Stage Size Limit
|
||||
tradingEngine.current.position.strategyOpenStage
|
||||
tradingEngine.current.strategyOpenStage
|
||||
)
|
||||
|
||||
/* From here on, the state is officialy Open */
|
||||
@@ -576,15 +576,15 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
false,
|
||||
tradingEngine.current.position.positionBaseAsset.size, // Stage Size Limit
|
||||
tradingEngine.current.position.positionQuotedAsset.size, // Stage Size Limit
|
||||
tradingEngine.current.position.strategyOpenStage
|
||||
tradingEngine.current.strategyOpenStage
|
||||
)
|
||||
/*
|
||||
The Open is finished when the fillSize + feesPaid reaches the Position Size.
|
||||
This can happens at any time when we update the filledSize value when we see
|
||||
This can happens at any time when we update the sizeFilled value when we see
|
||||
at the exchange that orders were filled.
|
||||
*/
|
||||
if (
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.filledSize.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngine.current.position.positionBaseAsset.size.value * 0.999) {
|
||||
tradingStrategyModule.updateStageStatus('Open Stage', 'Closed', 'Position Size Filled')
|
||||
@@ -607,7 +607,10 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
let executionNode = stageNode.openExecution
|
||||
|
||||
/* Check if there are unfilled orders */
|
||||
if (tradingEngine.current.position.openStageOrdersSize.value !== tradingEngine.current.position.openStageFilledSize.value) {
|
||||
if (
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value <
|
||||
tradingEngine.current.position.positionBaseAsset.size.value * 0.999) {
|
||||
/* There are unfilled orders, we will check if they were executed, and cancel the ones that were not. */
|
||||
|
||||
evalConditions(stageNode, 'Open Execution')
|
||||
@@ -619,18 +622,18 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
true,
|
||||
tradingEngine.current.position.positionBaseAsset.size, // Stage Size Limit
|
||||
tradingEngine.current.position.positionQuotedAsset.size, // Stage Size Limit
|
||||
tradingEngine.current.position.strategyOpenStage
|
||||
tradingEngine.current.strategyOpenStage
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
The Closing is finished when the fillSize + feesPaid reaches the ordersSize.
|
||||
This can happens either because we update the filledSize value when we see
|
||||
This can happens either because we update the sizeFilled value when we see
|
||||
at the exchange that orders were filled, or we reduce the ordersSize when
|
||||
we cancel not yet filled orders.
|
||||
*/
|
||||
if (
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.filledSize.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngine.current.position.positionBaseAsset.size.value * 0.999) {
|
||||
tradingStrategyModule.updateStageStatus('Open Stage', 'Closed')
|
||||
@@ -664,15 +667,49 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
/* Stop Loss Management */
|
||||
checkStopPhasesEvents()
|
||||
calculateStopLoss()
|
||||
calculateStopLossPosition()
|
||||
|
||||
/* Take Profit Management */
|
||||
checkTakeProfitPhaseEvents()
|
||||
calculateTakeProfit()
|
||||
calculateTakeProfitPosition()
|
||||
|
||||
/* Checking if Stop or Take Profit were hit */
|
||||
checkStopLossOrTakeProfitWasHit()
|
||||
}
|
||||
|
||||
function calculateStopLossPosition() {
|
||||
/*
|
||||
The position of the Stop Loss (above or below the price) is needed in order to know
|
||||
later if the price hit the Stop Loss or not at every Simulation cycle. When we get
|
||||
the first values of the simulation of the Stop Loss we check if it is above or below
|
||||
the Position Rate, and we assign the values Above or Below to it.
|
||||
*/
|
||||
if (tradingEngine.current.position.stopLoss.stopLossPosition.value === tradingEngine.current.position.stopLoss.stopLossPosition.config.initialValue) {
|
||||
if (tradingEngine.current.position.stopLoss.value > tradingEngine.current.position.rate) {
|
||||
tradingEngine.current.position.stopLoss.stopLossPosition.value = 'Above'
|
||||
} else {
|
||||
tradingEngine.current.position.stopLoss.stopLossPosition.value = 'Below'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function calculateTakeProfitPosition() {
|
||||
/*
|
||||
The position of the Take Profit (above or below the price) is needed in order to know
|
||||
later if the price hit the Take Profit or not at every Simulation cycle. When we get
|
||||
the first values of the simulation of the Take Profits we check if it is above or below
|
||||
the Position Rates, and we assign the values Above or Below to it.
|
||||
*/
|
||||
if (tradingEngine.current.position.takeProfit.takeProfitPosition.value === tradingEngine.current.position.takeProfit.takeProfitPosition.config.initialValue) {
|
||||
if (tradingEngine.current.position.takeProfit.value > tradingEngine.current.position.rate) {
|
||||
tradingEngine.current.position.takeProfit.takeProfitPosition.value = 'Above'
|
||||
} else {
|
||||
tradingEngine.current.position.takeProfit.takeProfitPosition.value = 'Below'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkStopPhasesEvents() {
|
||||
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
|
||||
let openStage = strategy.openStage
|
||||
@@ -897,13 +934,17 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
|
||||
/* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */
|
||||
if (
|
||||
(bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.max.value >= tradingEngine.current.position.stopLoss.value) ||
|
||||
(!bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.min.value <= tradingEngine.current.position.stopLoss.value)
|
||||
(
|
||||
tradingEngine.current.position.stopLoss.stopLossPosition.value === 'Above' &&
|
||||
tradingEngine.current.candle.max.value >= tradingEngine.current.position.stopLoss.value
|
||||
) ||
|
||||
(
|
||||
tradingEngine.current.position.stopLoss.stopLossPosition.value === 'Below' &&
|
||||
tradingEngine.current.candle.min.value <= tradingEngine.current.position.stopLoss.value
|
||||
)
|
||||
) {
|
||||
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')
|
||||
@@ -913,13 +954,17 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
|
||||
/* Take Profit condition: Here we verify if the Take Profit was hit or not. */
|
||||
if (
|
||||
(bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.min.value <= tradingEngine.current.position.takeProfit.value) ||
|
||||
(!bot.sessionAndMarketBaseAssetsAreEqual && tradingEngine.current.candle.max.value >= tradingEngine.current.position.takeProfit.value)
|
||||
(
|
||||
tradingEngine.current.position.takeProfit.takeProfitPosition.value === 'Below' &&
|
||||
tradingEngine.current.candle.min.value <= tradingEngine.current.position.takeProfit.value
|
||||
) ||
|
||||
(
|
||||
tradingEngine.current.position.takeProfit.takeProfitPosition.value === 'Above' &&
|
||||
tradingEngine.current.candle.max.value >= tradingEngine.current.position.takeProfit.value
|
||||
)
|
||||
) {
|
||||
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')
|
||||
@@ -938,7 +983,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
This will happen only once, as soon as the Take Profit or Stop was hit.
|
||||
We wil not be placing orders at this time because we do not know
|
||||
the total filled size of the Open Stage. By skiping execution now
|
||||
we allow the open stage to get a final value for filledSize that we can use.
|
||||
we allow the open stage to get a final value for sizeFilled that we can use.
|
||||
*/
|
||||
tradingStrategyModule.updateStageStatus('Close Stage', 'Open')
|
||||
return
|
||||
@@ -961,18 +1006,18 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
|
||||
false,
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled, // Stage Size Limit
|
||||
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled, // Stage Size Limit
|
||||
tradingEngine.current.position.strategyCloseStage
|
||||
tradingEngine.current.strategyCloseStage
|
||||
)
|
||||
|
||||
/*
|
||||
The Close Stage is finished when the fillSize + feesPaid reaches the Open Fill Size.
|
||||
This can happens at any time when we update the filledSize value when we see
|
||||
This can happens at any time when we update the sizeFilled value when we see
|
||||
at the exchange that orders were filled.
|
||||
*/
|
||||
if (
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.filledSize.value +
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.sizeFilled.value +
|
||||
tradingEngine.current.strategyCloseStage.stageBaseAsset.feesPaid.value >=
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.filledSize.value * 0.999) {
|
||||
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled.value * 0.999) {
|
||||
tradingStrategyModule.updateStageStatus('Close Stage', 'Closed', 'Open Stage fillSize Filled')
|
||||
} else {
|
||||
/* Check the Close Stage Event */
|
||||
@@ -997,28 +1042,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 +1076,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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -229,12 +229,6 @@
|
||||
bot.sessionError(bot.SESSION.parameters.sessionBaseAsset, errorMessage)
|
||||
return false
|
||||
}
|
||||
if (bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance === undefined) {
|
||||
bot.SESSION.parameters.sessionBaseAsset.config.minimumBalance = 0
|
||||
}
|
||||
if (bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance === undefined) {
|
||||
bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance = Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
|
||||
/* Session Quoted Asset */
|
||||
if (bot.SESSION.parameters.sessionQuotedAsset === undefined) {
|
||||
@@ -249,12 +243,6 @@
|
||||
bot.sessionError(bot.SESSION.parameters.sessionQuotedAsset, errorMessage)
|
||||
return false
|
||||
}
|
||||
if (bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance === undefined) {
|
||||
bot.SESSION.parameters.sessionQuotedAsset.config.minimumBalance = 0
|
||||
}
|
||||
if (bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance === undefined) {
|
||||
bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance = Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
|
||||
/* Slippage */
|
||||
if (bot.SESSION.parameters.slippage === undefined) { // if the Slippage is missing we create a default one.
|
||||
|
||||
Reference in new issue
Block a user