Half Iteration upgrading the Trading Engine (not running)

This commit is contained in:
Luis Fernando Molina committed 2020-08-01 19:37:00 +02:00
1 parent a03b025b64
commit 0b581fe837
20 files changed
+1877 -885

No files matched your search

+11 -1
View File
@@ -74,10 +74,20 @@
async function createOrder(tradingSystemOrder, tradingEngineOrder) {
let price = tradingEngineOrder.rate.value // CCXT: how much quote currency you are willing to pay for a trade lot of base currency (for limit orders only)
let amount = tradingEngineOrder.size.value // CCXT: how much of currency you want to trade
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
}
switch (tradingSystemOrder.type) {
case 'Market Buy Order': {
+440 -177
View File
@@ -43,11 +43,49 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
announcementsModule = undefined
}
async function checkExecution(executionNode, stageIsOpening, stageIsClosing, stageSizeLimit, stageOrdersSize, stageFilledSize) {
async function checkExecution(
executionNode,
stageIsOpening,
stageIsClosing,
stageSizeLimitInBaseAsset,
stageSizeLimitInQuotedAsset,
tradingEngineStage
) {
/* Enforcing Precision Limit */
stageSizeLimit.value = global.PRECISE(stageSizeLimit.value, 10)
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
/* 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 }
/*
The user choice of asset to define the size of the position,
defines the asset that will track the information kept about the
stage at the trading engine data structure. We set this asset
to be used here in this way:
*/
let tradingEngineStageAsset
if (tradingEngine.current.position.positionBaseAsset.size.value > 0) {
tradingEngineStageAsset = tradingEngineStage.stageBaseAsset
} else {
tradingEngineStageAsset = tradingEngineStage.stageQuotedAsset
}
/*
The asset type of the position size chosen by the user, also defines
the stage limit to be on the same asset type.
*/
let stageSizeLimit
if (tradingEngine.current.position.positionBaseAsset.size.value > 0) {
stageSizeLimit = stageSizeLimitInBaseAsset
} else {
stageSizeLimit = stageSizeLimitInQuotedAsset
}
await checkExecutionAlgorithms(executionNode)
@@ -79,7 +117,6 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
if (tradingEngineOrder.begin === undefined) { continue }
if (tradingEngineOrder.end === undefined) { continue }
if (tradingEngineOrder.rate === undefined) { continue }
if (tradingEngineOrder.size === undefined) { continue }
if (tradingEngineOrder.status === undefined) { continue }
if (tradingEngineOrder.algorithmName === undefined) { continue }
if (tradingEngineOrder.orderCounters === undefined) { continue }
@@ -89,21 +126,55 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
if (tradingEngineOrder.orderStatistics.actualRate === undefined) { continue }
if (tradingEngineOrder.orderStatistics.feesPaid === undefined) { continue }
if (tradingEngineOrder.orderBaseAsset.size === undefined) { continue }
if (tradingEngineOrder.orderBaseAsset.sizeFilled === undefined) { continue }
if (tradingEngineOrder.orderBaseAsset.amountReceived === undefined) { continue }
if (tradingEngineOrder.orderBaseAsset.feesPaid === undefined) { continue }
if (tradingEngineOrder.orderQuotedAsset.size === undefined) { continue }
if (tradingEngineOrder.orderQuotedAsset.sizeFilled === undefined) { continue }
if (tradingEngineOrder.orderQuotedAsset.amountReceived === undefined) { continue }
if (tradingEngineOrder.orderQuotedAsset.feesPaid === undefined) { continue }
/*
The asset type of the position size chosen by the user, defines
the asset at the order data strutcture where information is going to
be stored.
*/
let traingEngineOrderAsset
if (tradingEngine.current.position.positionBaseAsset.size.value > 0) {
traingEngineOrderAsset = tradingEngineOrder.orderBaseAsset
} else {
traingEngineOrderAsset = tradingEngineOrder.orderQuotedAsset
}
switch (tradingEngineOrder.status.value) {
case 'Not Open': {
{
/* Check if we need to Create this Order */
/* When the stage is closing we can not create new orders */
if (stageIsClosing === true) { continue }
/*
Check if we can create an order based on the config value for spawnMultipleOrders.
Trading System Orders that cannot spawn more than one Trading Engine Order needs to check if
at the Trading Engine Order the lock is Open or Closed.
*/
if (tradingSystemOrder.config.spawnMultipleOrders !== true) {
if (tradingEngineOrder.identifier.value === 'Closed') {
continue
}
}
/* Check if we need to Create this Order */
let situationName = checkOrderEvent(tradingSystemOrder.createOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (situationName !== undefined) {
/* Open a new order */
await openOrder(executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName)
await tryToOpenOrder(executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName)
}
}
break
}
case 'Open': {
/* Update this order properties */
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
tradingEngineOrder.orderCounters.periods.value++
tradingEngineOrder.orderStatistics.days.value = tradingEngineOrder.orderCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS
@@ -114,13 +185,22 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
/* Check Events that happens at the Exchange, if needed. */
await checkExchangeEvents(tradingSystemOrder, tradingEngineOrder)
/* If the stage is closing or the order is not still Open, we wont be cancelling orders based on defined events */
/*
In the previous steps, we might have discovered that the order was cancelled
at the exchange, or filled, so the order might still not be Open.
If the stage is closing or the order is not Open, we wont be cancelling orders
based on defined events.
*/
if (stageIsClosing !== true && tradingEngineOrder.status.value === 'Open') {
/* Check if we need to Cancel this Order */
let situationName = checkOrderEvent(tradingSystemOrder.cancelOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (situationName !== undefined) {
/* Simulate Order Cancelation, if needed. */
simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
/* Cancel the order at the Exchange, if needed. */
await exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
}
}
@@ -129,31 +209,24 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
}
}
async function openOrder(executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName) {
/* Order Size Calculation */
tradingEngineOrder.size.value = tradingSystem.formulas.get(executionAlgorithm.positionSize.formula.id) * tradingSystemOrder.config.positionSizePercentage / 100
if (stageOrdersSize.value + tradingEngineOrder.size.value > stageSizeLimit.value) {
/* We reduce the size to the remaining size of the position. */
tradingEngineOrder.size.value = stageSizeLimit.value - stageOrdersSize.value
tradingEngineOrder.size.value = global.PRECISE(tradingEngineOrder.size.value, 10)
}
async function tryToOpenOrder(executionAlgorithm, tradingSystemOrder, tradingEngineOrder, situationName) {
/* Check Order Size */
if (tradingEngineOrder.size.value <= 0) { return }
calculateOrderRate()
calculateOrderSize()
/* Order Rate Calculation */
tradingEngineOrder.rate.value = tradingEngine.current.position.rate.value // By default this is the order rate.
if (tradingSystemOrder.positionRate !== undefined) {
if (tradingSystemOrder.positionRate.formula !== undefined) {
tradingEngineOrder.rate.value = tradingSystem.formulas.get(tradingSystemOrder.positionRate.formula.id)
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
}
}
/* 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 */
let result = await createOrderAtExchange(tradingSystemOrder, tradingEngineOrder)
if (result !== true) { return }
/* Update Stage Counters */
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
tradingEngineStage.stageQuotedAsset.size.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.size.value, 10)
/* Updating Episode Counters */
tradingEngine.episode.episodeCounters.orders.value++
@@ -170,34 +243,131 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
tradingEngineOrder.situationName.value = situationName
/* Update Stage Orders Size */
stageOrdersSize.value = stageOrdersSize.value + tradingEngineOrder.size.value
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
}
function calculateOrderRate() {
/* Order Rate Calculation */
tradingEngineOrder.rate.value = tradingEngine.current.position.rate.value // By default this is the order rate.
if (tradingSystemOrder.positionRate !== undefined) {
if (tradingSystemOrder.positionRate.formula !== undefined) {
tradingEngineOrder.rate.value = tradingSystem.formulas.get(tradingSystemOrder.positionRate.formula.id)
async function createOrderAtExchange(tradingSystemOrder, tradingEngineOrder) {
if (tradingEngineOrder.rate.value === undefined) {
const errorText = 'Rate cannot be undefined. Fix this please.'
tradingSystem.errors.push([tradingSystemOrder.positionRate.formula.id, errorText])
throw (errorText)
}
/* Filter by Session Type */
switch (bot.SESSION.type) {
case 'Backtesting Session': {
return true
}
case 'Live Trading Session': {
break
}
case 'Fordward Testing Session': {
break
}
case 'Paper Trading Session': {
return true
if (tradingEngineOrder.rate.value <= 0) {
const errorText = 'Rate cannot be less or equal to zero. Fix this please.'
tradingSystem.errors.push([tradingSystemOrder.positionRate.formula.id, errorText])
throw (errorText)
}
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
}
}
}
let orderId = await exchangeAPIModule.createOrder(tradingSystemOrder, tradingEngineOrder)
function calculateOrderSize() {
/*
If the position size was defined in Base Asset, then
the algorithm needs to define its size also in Base Asset.
The same if it was deined in Quoted Asset.
*/
let sizeFormula
if (tradingEngine.current.position.positionBaseAsset.size.value > 0) {
/* Position was defined in Base Asset */
if (executionAlgorithm.sizeInBaseAsset !== undefined) {
if (executionAlgorithm.sizeInBaseAsset.formula !== undefined) {
sizeFormula = executionAlgorithm.sizeInBaseAsset.formula
} else {
const errorText = 'Size In Base Asset needs a child Formula. Fix this please.'
tradingSystem.errors.push([executionAlgorithm.sizeInBaseAsset.id, errorText])
throw (errorText)
}
} else {
const errorText = 'Execution Algorithm needs a child Size In Base Asset. Fix this please.'
tradingSystem.errors.push([executionAlgorithm.id, errorText])
throw (errorText)
}
} else {
/* Position was defined in Quoted Asset */
if (executionAlgorithm.sizeInQuotedAsset !== undefined) {
if (executionAlgorithm.sizeInQuotedAsset.formula !== undefined) {
sizeFormula = executionAlgorithm.sizeInQuotedAsset.formula
} else {
const errorText = 'Size In Quoted Asset needs a child Formula. Fix this please.'
tradingSystem.errors.push([executionAlgorithm.sizeInQuotedAsset.id, errorText])
throw (errorText)
}
} else {
const errorText = 'Execution Algorithm needs a child Size In Quoted Asset. Fix this please.'
tradingSystem.errors.push([executionAlgorithm.id, errorText])
throw (errorText)
}
}
if (orderId !== undefined) {
tradingEngineOrder.exchangeId.value = orderId
return true
/* Order Size Calculation */
let algorithmSize = tradingSystem.formulas.get(sizeFormula.id)
if (algorithmSize === undefined) {
const errorText = 'Execution Algorithm Size cannot be undefined. Fix this please.'
tradingSystem.errors.push([sizeFormula.id, errorText])
throw (errorText)
}
/* Validate that this config exists */
if (tradingSystemOrder.config.positionSizePercentage === undefined) {
const errorText = 'Config positionSizePercentage does not exist. Fix this please.'
tradingSystem.errors.push([tradingSystemOrder.id, errorText])
throw (errorText)
}
traingEngineOrderAsset.size.value = algorithmSize * tradingSystemOrder.config.positionSizePercentage / 100
traingEngineOrderAsset.size.value = global.PRECISE(traingEngineOrderAsset.size.value, 10)
/* Check against the Stage Size Limit */
if (
tradingEngineStageAsset.size.value + traingEngineOrderAsset.size.value > stageSizeLimit.value) {
/* We reduce the size to the remaining size of the position. */
traingEngineOrderAsset.size.value = stageSizeLimit.value - tradingEngineStageAsset.value
traingEngineOrderAsset.size.value = global.PRECISE(traingEngineOrderAsset.size.value, 10)
}
/* We are going to ESTIMATE the size in the oposite asset type, because we will need it later */
switch (traingEngineOrderAsset.type) {
case 'Order Base Asset': {
tradingEngineOrder.orderQuotedAsset.size = tradingEngineOrder.orderBaseAsset.size * tradingEngineOrder.rate
tradingEngineOrder.size.value = global.PRECISE(tradingEngineOrder.size.value, 10)
}
case 'Order Quoted Asset': {
tradingEngineOrder.orderBaseAsset.size = tradingEngineOrder.orderQuotedAsset.size / tradingEngineOrder.rate
tradingEngineOrder.size.value = global.PRECISE(tradingEngineOrder.size.value, 10)
}
}
}
async function createOrderAtExchange(tradingSystemOrder, tradingEngineOrder) {
/* Filter by Session Type */
switch (bot.SESSION.type) {
case 'Backtesting Session': {
return true
}
case 'Live Trading Session': {
break
}
case 'Fordward Testing Session': {
break
}
case 'Paper Trading Session': {
return true
}
}
let orderId = await exchangeAPIModule.createOrder(tradingSystemOrder, tradingEngineOrder)
if (orderId !== undefined) {
tradingEngineOrder.exchangeId.value = orderId
return true
}
}
}
@@ -222,7 +392,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
/* Filter by what is defined at the Strategy */
if (tradingSystemOrder.simulatedExchangeEvents === undefined) { return }
let previousSizeFilled = tradingEngineOrder.orderStatistics.sizeFilled.value
let previousSizeFilled = tradingEngineOrderAsset.sizeFilled.value
let previousPercentageFilled = tradingEngineOrder.orderStatistics.percentageFilled.value
let previousAmountReceived = tradingEngineOrder.orderStatistics.amountReceived.value
let previousFeesPaid = tradingEngineOrder.orderStatistics.feesPaid.value
@@ -233,7 +403,7 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
sizeFilledSimulation()
amountReceivedSimulation()
doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid)
doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousFeesPaid)
/* If the Stage is Closing and this order is still open, we need to cancel it now */
if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') {
@@ -443,129 +613,187 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
function syncWithExchange(tradingSystemOrder, tradingEngineOrder, order) {
let previousSizeFilled = tradingEngineOrder.orderStatistics.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
/* Fees Paid */
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value - order.amount
tradingEngineOrder.orderStatistics.feesPaid.value = global.PRECISE(tradingEngineOrder.orderStatistics.feesPaid.value, 10)
/* Percentage Filled */
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (order.filled + order.remaining)
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
/* Size Filled */
tradingEngineOrder.orderStatistics.sizeFilled.value = order.filled
tradingEngineOrder.orderStatistics.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.sizeFilled.value, 10)
/* Amount Received */
tradingEngineOrder.orderStatistics.amountReceived.value = order.cost
tradingEngineOrder.orderStatistics.amountReceived.value = global.PRECISE(tradingEngineOrder.orderStatistics.amountReceived.value, 10)
/* Actual Rate */
/* Actual Rate Calculation */
tradingEngineOrder.orderStatistics.actualRate.value = order.price
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid)
/* Fees Paid Calculation */
/*
As a response from the exchange we can not always get the fees.
For that reason we need to estimate them base of the information that we do have.
CCXT provides order.amount which represents the size we set for the order minus the
fees taken by the exchange, all denominated in Base Asset.
In this way if we substract to the order size this order.amount
we can get the fees. All this is denominated in base asset because that is how CCXT works.
The fees then can be estimated in Quoted Asset using the Actual Rate.
*/
tradingEngineOrder.orderBaseAsset.feesPaid.value = tradingEngineOrder.orderBaseAsset.size.value - order.amount
tradingEngineOrder.orderBaseAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.feesPaid.value, 10)
tradingEngineOrder.orderQuotedAsset.feesPaid.value = tradingEngineOrder.orderBaseAsset.feesPaid.value * tradingEngineOrder.orderStatistics.actualRate.value
tradingEngineOrder.orderQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.feesPaid.value, 10)
/* Percentage Filled Calculation */
tradingEngineOrder.orderStatistics.percentageFilled.value = order.filled * 100 / (order.filled + order.remaining)
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
/* Size Filled Calculation */
/*
CCXT returns order.filled with an amount denominated in Base Asset. We will
take it from there for our Order Base Asset. For our Order Quoted Asset we
will use the field order.cost.
*/
tradingEngineOrder.orderBaseAsset.sizeFilled.value = order.filled
tradingEngineOrder.orderBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderBaseAsset.sizeFilled.value, 10)
tradingEngineOrder.orderQuotedAsset.sizeFilled.value = order.cost
tradingEngineOrder.orderQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderQuotedAsset.sizeFilled.value, 10)
doTheAccounting(
tradingSystemOrder,
tradingEngineOrder,
previousBaseAssetSizeFilled,
previousQuotedAssetSizeFilled,
previousBaseAssetFeesPaid,
previousQuotedAssetFeesPaid
)
}
function doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid) {
function doTheAccounting(
tradingSystemOrder,
tradingEngineOrder,
previousBaseAssetSizeFilled,
previousQuotedAssetSizeFilled,
previousBaseAssetFeesPaid,
previousQuotedAssetFeesPaid
) {
/* Unaccount for the previous Stage Filled Size */
stageFilledSize.value = stageFilledSize.value - tradingEngineOrder.size.value * previousPercentageFilled / 100
/* Account for the current Stage Filled Size */
stageFilledSize.value = stageFilledSize.value + tradingEngineOrder.size.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
updateStageAssets()
updateBalances()
function updateStageAssets() {
/* Stage Base Asset: Undo the previous accounting */
tradingEngineStage.stageBaseAsset.sizeFilled.value =
tradingEngineStage.stageBaseAsset.value -
previousBaseAssetSizeFilled
/* Balances Update */
switch (true) {
case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': {
tradingEngineStage.stageBaseAsset.feesPaid.value =
tradingEngineStage.feesPaid.value -
previousBaseAssetFeesPaid
/* Undo the previous accounting */
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - previousFeesPaid - previousSizeFilled
/* Account the current filling and fees */
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngineOrder.orderStatistics.sizeFilled.value + tradingEngineOrder.orderStatistics.feesPaid.value
/* Stage Base Asset: Account the current filling and fees */
tradingEngineStage.stageBaseAsset.sizeFilled.value =
tradingEngineStage.stageBaseAsset.value +
tradingEngineOrder.orderBaseAsset.sizeFilled.value
/* Undo the previous accounting */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + previousAmountReceived
/* Account the current filling and fees */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value - tradingEngineOrder.orderStatistics.amountReceived.value
tradingEngineStage.stageBaseAsset.feesPaid.value =
tradingEngineStage.feesPaid.value +
tradingEngineOrder.orderBaseAsset.feesPaid.value
break
}
case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': {
/* Stage Quote Asset: Undo the previous accounting */
tradingEngineStage.stageQuotedAsset.sizeFilled.value =
tradingEngineStage.stageQuotedAsset.value -
previousQuotedAssetSizeFilled
/* Undo the previous accounting */
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + previousFeesPaid + previousSizeFilled
/* Account the current filling and fees */
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngineOrder.orderStatistics.sizeFilled.value - tradingEngineOrder.orderStatistics.feesPaid.value
tradingEngineStage.stageQuotedAsset.feesPaid.value =
tradingEngineStage.feesPaid.value -
previousQuotedAssetFeesPaid
/* Undo the previous accounting */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value - previousAmountReceived
/* Account the current filling and fees */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngineOrder.orderStatistics.amountReceived.value
/* Stage Quote Asset: Account the current filling and fees */
tradingEngineStage.stageQuotedAsset.sizeFilled.value =
tradingEngineStage.stageQuotedAsset.value +
tradingEngineOrder.orderQuotedAsset.sizeFilled.value
break
}
}
}
tradingEngineStage.stageQuotedAsset.feesPaid.value =
tradingEngineStage.feesPaid.value +
tradingEngineOrder.orderQuotedAsset.feesPaid.value
function simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) {
tradingEngineStage.stageBaseAsset.sizeFilled.value = global.PRECISE(tradingEngineStage.stageBaseAsset.sizeFilled.value, 10)
tradingEngineStage.stageBaseAsset.feesPaid.value = global.PRECISE(tradingEngineStage.stageBaseAsset.feesPaid.value, 10)
/* 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
}
tradingEngineStage.stageQuotedAsset.sizeFilled.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.sizeFilled.value, 10)
tradingEngineStage.stageQuotedAsset.feesPaid.value = global.PRECISE(tradingEngineStage.stageQuotedAsset.feesPaid.value, 10)
}
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = exitType
function updateBalances() {
/* Balances Update */
switch (true) {
case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': {
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
/* Balance Base Asset: Undo the previous accounting */
tradingEngine.current.balance.baseAsset.value =
tradingEngine.current.balance.baseAsset.value -
previousBaseAssetSizeFilled
/* 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)
}
/* Balance Base Asset: Account the current filling and fees */
tradingEngine.current.balance.baseAsset.value =
tradingEngine.current.balance.baseAsset.value +
tradingEngineOrder.orderBaseAsset.sizeFilled.value
async function exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) {
/* Balance Quoted Asset: Undo the previous accounting */
tradingEngine.current.balance.quotedAsset.value =
tradingEngine.current.balance.quotedAsset.value +
previousQuotedAssetSizeFilled +
previousQuotedAssetFeesPaid
/* 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
/* Balance Quoted Asset: Account the current filling and fees */
tradingEngine.current.balance.quotedAsset.value =
tradingEngine.current.balance.quotedAsset.value -
tradingEngineOrder.orderQuotedAsset.sizeFilled.value -
tradingEngineOrder.orderQuotedAsset.feesPaid.value
break
}
case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': {
/* Balance Base Asset: Undo the previous accounting */
tradingEngine.current.balance.baseAsset.value =
tradingEngine.current.balance.baseAsset.value +
previousBaseAssetSizeFilled +
previousBaseAssetFeesPaid
/* Balance Base Asset: Account the current filling and fees */
tradingEngine.current.balance.baseAsset.value =
tradingEngine.current.balance.baseAsset.value -
tradingEngineOrder.orderBaseAsset.sizeFilled.value -
tradingEngineOrder.orderBaseAsset.feesPaid.value
/* Balance Quoted Asset: Undo the previous accounting */
tradingEngine.current.balance.quotedAsset.value =
tradingEngine.current.balance.quotedAsset.value -
previousQuotedAssetSizeFilled
/* Balance Quoted Asset: Account the current filling and fees */
tradingEngine.current.balance.quotedAsset.value =
tradingEngine.current.balance.quotedAsset.value +
tradingEngineOrder.orderQuotedAsset.sizeFilled.value
break
}
}
tradingEngine.current.balance.baseAsset.value = global.PRECISE(tradingEngine.current.balance.baseAsset.value, 10)
tradingEngine.current.balance.quotedAsset.value = global.PRECISE(tradingEngine.current.balance.quotedAsset.value, 10)
}
/* Check if we can cancel the order at the Exchange. */
let result = await exchangeAPIModule.cancelOrder(tradingSystemOrder, tradingEngineOrder)
if (result === true) {
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
@@ -578,45 +806,80 @@ exports.newTradingExecution = function newTradingExecution(bot, logger, tradingE
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
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
}
}
passed = tradingSystem.checkConditions(situation, passed)
/* 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
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)
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
announcementsModule.makeAnnoucements(event)
return situation.name // if the event is triggered, we return the name of the situation that passed
/* 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
}
}
}
}
}
}
}
+119 -10
View File
@@ -15,7 +15,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
applySlippageToTakeProfit: applySlippageToTakeProfit,
updateStopLoss: updateStopLoss,
updateTakeProfit: updateTakeProfit,
updateSizeAndRate: updateSizeAndRate,
initializeSizeAndRate: initializeSizeAndRate,
updateEnds: updateEnds,
updateStatus: updateStatus,
updateCounters: updateCounters,
@@ -41,18 +41,26 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
}
function openPosition(situationName) {
/* Recording the opening at the Trading Engine Data Structure */
tradingEngine.current.position.status.value = 'Open'
tradingEngine.current.position.serialNumber.value = tradingEngine.episode.episodeCounters.positions.value
tradingEngine.current.position.identifier.value = global.UNIQUE_ID()
tradingEngine.current.position.begin.value = tradingEngine.current.candle.begin.value
tradingEngine.current.position.end.value = tradingEngine.current.candle.end.value
tradingEngine.current.position.situationName.value = situationName
/* Initializing Stop and Take Profit Stage / Phase */
tradingEngine.current.position.stopLoss.stopLossStage.value = 'Open Stage'
tradingEngine.current.position.takeProfit.takeProfitStage.value = 'Open Stage'
tradingEngine.current.position.stopLoss.stopLossPhase.value = 0
tradingEngine.current.position.takeProfit.takeProfitPhase.value = 0
/* Updating Episode Counters */
tradingEngine.episode.episodeCounters.positions.value++
/* Inicializing this counter */
tradingEngine.current.distanceToEvent.takePosition.value = 1
}
function closingPosition(exitType) {
@@ -91,11 +99,84 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
tradingEngine.current.position.takeProfit.takeProfitStage.value = stage
}
function updateSizeAndRate(size, rate) {
tradingEngine.current.position.size.value = global.PRECISE(size, 10)
tradingEngine.current.position.rate.value = global.PRECISE(rate, 10)
function initializeSizeAndRate() {
setPositionSize()
setPositionRate()
tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value
function setPositionSize() {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
/* Basic Validation */
if (
strategy.openStage.initialDefinition.positionSizeInBaseAsset !== undefined &&
strategy.openStage.initialDefinition.positionSizeInQuotedAsset !== undefined
) {
const errorText = 'Only Position Size In Base Asset or Position Size In Quoted Asset is allowed. Remove one of them please.'
tradingSystem.errors.push([strategy.openStage.initialDefinition.id, errorText])
throw (errorText)
}
/* Position In Base Asset */
if (strategy.openStage.initialDefinition.positionSizeInBaseAsset !== undefined) {
if (strategy.openStage.initialDefinition.positionSizeInBaseAsset.formula !== undefined) {
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionSizeInBaseAsset.formula.id)
if (value === undefined) {
const errorText = 'Position Size In Base Asset cannot be undefined. Fix this please.'
tradingSystem.errors.push([strategy.openStage.initialDefinition.positionSizeInBaseAsset.formula.id, errorText])
throw (errorText)
}
if (value === undefined) {
const errorText = 'Position Size In Base Asset cannot be zero. Fix this please.'
tradingSystem.errors.push([strategy.openStage.initialDefinition.positionSizeInBaseAsset.formula.id, errorText])
throw (errorText)
}
tradingEngine.current.position.positionBaseAsset.size.value = global.PRECISE(value, 10)
} else {
const errorText = 'You need to specify a Formula for this.'
tradingSystem.errors.push([strategy.openStage.initialDefinition.positionSizeInBaseAsset.id, errorText])
throw (errorText)
}
}
/* Position In Quoted Asset */
if (strategy.openStage.initialDefinition.positionSizeInQuotedAsset !== undefined) {
if (strategy.openStage.initialDefinition.positionSizeInQuotedAsset.formula !== undefined) {
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionSizeInQuotedAsset.formula.id)
if (value === undefined) {
const errorText = 'Position Size In Quoted Asset cannot be undefined. Fix this please.'
tradingSystem.errors.push([strategy.openStage.initialDefinition.positionSizeInQuotedAsset.formula.id, errorText])
throw (errorText)
}
if (value === undefined) {
const errorText = 'Position Size In Quoted Asset cannot be zero. Fix this please.'
tradingSystem.errors.push([strategy.openStage.initialDefinition.positionSizeInQuotedAsset.formula.id, errorText])
throw (errorText)
}
tradingEngine.current.position.positionQuotedAsset.size.value = global.PRECISE(value, 10)
} else {
const errorText = 'You need to specify a Formula for this.'
tradingSystem.errors.push([strategy.openStage.initialDefinition.positionSizeInQuotedAsset.id, errorText])
throw (errorText)
}
}
}
function setPositionRate() {
const DEFAULT_VALUE = tradingEngine.current.candle.close.value
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
if (strategy.openStage === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionRate === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionRate.formula === undefined) return DEFAULT_VALUE
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionRate.formula.id)
if (value === undefined) return DEFAULT_VALUE
return value
}
}
function preventStopLossDistortion() {
@@ -182,14 +263,42 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
}
function updateStatistics() {
if (bot.sessionAndMarketBaseAssetsAreEqual) {
tradingEngine.current.position.positionStatistics.profitLoss.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.previous.balance.baseAsset.value
tradingEngine.current.position.positionStatistics.ROI.value = tradingEngine.current.position.positionStatistics.profitLoss.value * 100 / tradingEngine.current.position.size.value
/* 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 {
tradingEngine.current.position.positionStatistics.profitLoss.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.previous.balance.quotedAsset.value
tradingEngine.current.position.positionStatistics.ROI.value = tradingEngine.current.position.positionStatistics.profitLoss.value * 100 / tradingEngine.current.position.size.value
/* 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)
}
tradingEngine.current.position.positionStatistics.days.value = tradingEngine.current.position.positionCounters.periods.value * sessionParameters.timeFrame.config.value / global.ONE_DAY_IN_MILISECONDS
/* Days Calculation */
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)
/* Hit Fail Calculation */
if (tradingEngine.current.position.positionStatistics.ROI.value > 0) {
tradingEngine.current.position.positionStatistics.hitFail.value = 'HIT'
} else {
@@ -63,22 +63,28 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng
}
}
function updateStageStatus(stage, status) {
function updateStageStatus(stage, status, exitType) {
switch (stage) {
case 'Trigger Stage': {
tradingEngine.current.strategy.triggerStageStatus.value = status
tradingEngine.current.strategyTriggerStage.status.value = status
break
}
case 'Open Stage': {
tradingEngine.current.strategy.openStageStatus.value = status
tradingEngine.current.strategyOpenStage.status.value = status
if (exitType !== undefined) {
tradingEngine.current.strategyOpenStage.exitType.value = exitType
}
break
}
case 'Manage Stage': {
tradingEngine.current.strategy.manageStageStatus.value = status
tradingEngine.current.strategyManageStage.status.value = status
break
}
case 'Close Stage': {
tradingEngine.current.strategy.closeStageStatus.value = status
tradingEngine.current.strategyCloseStage.status.value = status
if (exitType !== undefined) {
tradingEngine.current.strategyCloseStage.exitType.value = exitType
}
break
}
}
+135 -94
View File
@@ -143,27 +143,27 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
executionNode = stageNode.openExecution
resetExecution(executionNode)
resetExecution(executionNode, tradingEngine.current.strategyOpenStage.status.value)
stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].closeStage
executionNode = stageNode.closeExecution
resetExecution(executionNode)
resetExecution(executionNode, tradingEngine.current.strategyCloseStage.status.value)
function resetExecution(executionNode) {
function resetExecution(executionNode, stageStatus) {
resetExecutionAlgorithms(executionNode)
function resetExecutionAlgorithms(executionNode) {
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
let executionAlgorithm = executionNode.executionAlgorithms[i]
resetOrders(executionAlgorithm.marketBuyOrders, executionAlgorithm, executionNode)
resetOrders(executionAlgorithm.marketSellOrders, executionAlgorithm, executionNode)
resetOrders(executionAlgorithm.limitBuyOrders, executionAlgorithm, executionNode)
resetOrders(executionAlgorithm.limitSellOrders, executionAlgorithm, executionNode)
resetOrders(executionAlgorithm.marketBuyOrders)
resetOrders(executionAlgorithm.marketSellOrders)
resetOrders(executionAlgorithm.limitBuyOrders)
resetOrders(executionAlgorithm.limitSellOrders)
}
}
function resetOrders(orders, executionAlgorithm, executionNode) {
function resetOrders(orders) {
for (let i = 0; i < orders.length; i++) {
let tradingSystemOrder = orders[i]
if (tradingSystemOrder.referenceParent === undefined) { continue }
@@ -172,6 +172,14 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
if (tradingEngineOrder.status.value === 'Closed') {
/* We reset the order data structure inside the Trading Engine to its initial value */
tradingEngineOrder.initialize(tradingEngineOrder)
if (tradingSystemOrder.config.spawnMultipleOrders !== true) {
/*
We close the lock so as to prevent this data structure to be used again during this same stage execution.
*/
if (stageStatus === 'Open') {
tradingEngineOrder.lock.value = 'Closed'
}
}
}
}
}
@@ -205,7 +213,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
/* Here we check if there is a formula to be evaluated */
if (node.type === 'Formula' && evaluating === 'Formulas') {
if ((node.type === 'Formula' || node.type === 'Announcement Formula') && evaluating === 'Formulas') {
if (node.code !== undefined) {
/* We will eval this formula */
if (isDescendent === true) {
@@ -322,13 +330,18 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
}
tradingSystem.formulas.set(node.id, value)
if (error !== undefined) {
tradingSystem.errors.push([node.id, error])
return
}
if (value !== undefined) {
if (node.type === 'Formula' && isNaN(value)) {
tradingSystem.errors.push([node.id, 'Formula needs to return a number.'])
return
}
tradingSystem.values.push([node.id, value])
tradingSystem.formulas.set(node.id, value)
}
logger.write(MODULE_NAME, '[INFO] evalFormula -> value = ' + value)
@@ -409,7 +422,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
function checkTriggerOff() {
if (tradingEngine.current.strategy.triggerStageStatus.value === 'Open') {
if (tradingEngine.current.strategyTriggerStage.status.value === 'Open') {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let triggerStage = strategy.triggerStage
@@ -446,7 +459,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
function checkTakePosition() {
if (
tradingEngine.current.strategy.triggerStageStatus.value === 'Open'
tradingEngine.current.strategyTriggerStage.status.value === 'Open'
) {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let triggerStage = strategy.triggerStage
@@ -471,12 +484,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
tradingSystem.highlights.push(triggerStage.takePosition.id)
tradingSystem.highlights.push(triggerStage.id)
/* Updating Episode Counters */
tradingEngine.episode.episodeCounters.positions.value++
/* Inicializing this counter */
tradingEngine.current.distanceToEvent.takePosition.value = 1
tradingPositionModule.openPosition(situation.name)
announcementsModule.makeAnnoucements(triggerStage.takePosition)
@@ -503,7 +510,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
async function openStage() {
/* Abort Open Stage Check */
if (tradingEngine.current.strategy.openStageStatus.value === 'Open' && tradingEngine.current.strategy.closeStageStatus.value === 'Open') {
if (tradingEngine.current.strategyOpenStage.status.value === 'Open' && tradingEngine.current.strategyCloseStage.status.value === 'Open') {
/*
if the Close stage is opened while the open stage is still open that means that
we need to stop placing orders, check what happened to the orders already placed,
@@ -513,7 +520,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
/* Opening Status Procedure */
if (tradingEngine.current.strategy.openStageStatus.value === 'Opening') {
if (tradingEngine.current.strategyOpenStage.status.value === 'Opening') {
/* This procedure is intended to run only once */
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
@@ -525,28 +532,33 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Initial Definition')
evalFormulas(stageNode, 'Initial Definition')
getReadyToTakePosition()
calculateTakeProfit()
calculateStopLoss()
/* Remember the balance we had before taking the position to later calculate profit or loss */
tradingEngine.previous.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value
tradingEngine.previous.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value
/* Check Execution in opening stage node */
/* Position size and rate */
tradingPositionModule.initializeSizeAndRate()
/* Check Execution at open stage node */
evalConditions(stageNode, 'Open Execution')
evalFormulas(stageNode, 'Open Execution')
await tradingExecutionModule.checkExecution(executionNode, true, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
await tradingExecutionModule.checkExecution(
executionNode,
true,
false,
tradingEngine.current.position.positionBaseAsset.size, // Stage Size Limit
tradingEngine.current.position.positionQuotedAsset.size, // Stage Size Limit
tradingEngine.current.position.strategyOpenStage
)
/* From here on, the state is officially Open */
/* From here on, the state is officialy Open */
tradingStrategyModule.updateStageStatus('Open Stage', 'Open')
return
}
/* Open Status Procedure */
if (tradingEngine.current.strategy.openStageStatus.value === 'Open') {
if (tradingEngine.current.strategyOpenStage.status.value === 'Open') {
/*
While the Open Stage is Open, we do our regular stuff: place orders and check
what happened to the orders already placed.
@@ -558,20 +570,35 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Open Execution')
evalFormulas(stageNode, 'Open Execution')
await tradingExecutionModule.checkExecution(executionNode, false, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
await tradingExecutionModule.checkExecution(
executionNode,
false,
false,
tradingEngine.current.position.positionBaseAsset.size, // Stage Size Limit
tradingEngine.current.position.positionQuotedAsset.size, // Stage Size Limit
tradingEngine.current.position.strategyOpenStage
)
/*
The Open is finished when the fillSize reaches the Position Size.
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
at the exchange that orders were filled.
*/
if (tradingEngine.current.position.openStageFilledSize.value === tradingEngine.current.position.size.value) {
tradingStrategyModule.updateStageStatus('Open Stage', 'Closed')
if (
tradingEngine.current.strategyOpenStage.stageBaseAsset.filledSize.value +
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
tradingEngine.current.position.positionBaseAsset.size.value * 0.999) {
tradingStrategyModule.updateStageStatus('Open Stage', 'Closed', 'Position Size Filled')
} else {
/* Check the Close Stage Event */
evalConditions(stageNode, 'Close Stage Event')
if (checkStopStageEvent(stageNode) === true) {
tradingStrategyModule.updateStageStatus('Open Stage', 'Closing', 'Close Stage Event')
}
}
}
/* Closing Status Procedure */
if (tradingEngine.current.strategy.openStageStatus.value === 'Closing') {
if (tradingEngine.current.strategyOpenStage.status.value === 'Closing') {
/*
During the closing stage status, we do not place new orders, just check
if the ones placed were filled, and cancel the ones not filled.
@@ -586,71 +613,41 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Open Execution')
evalFormulas(stageNode, 'Open Execution')
await tradingExecutionModule.checkExecution(executionNode, false, true, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
await tradingExecutionModule.checkExecution(
executionNode,
false,
true,
tradingEngine.current.position.positionBaseAsset.size, // Stage Size Limit
tradingEngine.current.position.positionQuotedAsset.size, // Stage Size Limit
tradingEngine.current.position.strategyOpenStage
)
}
/*
The Closing is finished when the fillSize reaches the ordersSize.
The Closing is finished when the fillSize + feesPaid reaches the ordersSize.
This can happens either because we update the filledSize 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.position.openStageFilledSize.value === tradingEngine.current.position.openStageOrdersSize.value) {
if (
tradingEngine.current.strategyOpenStage.stageBaseAsset.filledSize.value +
tradingEngine.current.strategyOpenStage.stageBaseAsset.feesPaid.value >=
tradingEngine.current.position.positionBaseAsset.size.value * 0.999) {
tradingStrategyModule.updateStageStatus('Open Stage', 'Closed')
}
}
function getReadyToTakePosition() {
/* Position size and rate */
tradingPositionModule.updateSizeAndRate(getPositionSize(), getPositionRate())
function getPositionSize() {
let balance
if (bot.sessionAndMarketBaseAssetsAreEqual) {
balance = tradingEngine.current.balance.baseAsset.value
} else {
balance = tradingEngine.current.balance.quotedAsset.value
}
const DEFAULT_VALUE = balance
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
if (strategy.openStage === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionSize === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionSize.formula === undefined) return DEFAULT_VALUE
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionSize.formula.id)
if (value === undefined) return DEFAULT_VALUE
return value
}
function getPositionRate() {
const DEFAULT_VALUE = tradingEngine.current.candle.close.value
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
if (strategy.openStage === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionRate === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionRate.formula === undefined) return DEFAULT_VALUE
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionRate.formula.id)
if (value === undefined) return DEFAULT_VALUE
return value
}
}
}
function manageStage() {
/* Opening Status Procedure */
if (tradingEngine.current.strategy.manageStageStatus.value === 'Opening') {
if (tradingEngine.current.strategyManageStage.status.value === 'Opening') {
/* We jump the management at the loop cycle where the position is being taken. */
tradingStrategyModule.updateStageStatus('Manage Stage', 'Open')
return
}
/* Open Status Procedure */
if (tradingEngine.current.strategy.manageStageStatus.value === 'Open') {
if (tradingEngine.current.strategyManageStage.status.value === 'Open') {
let strategy = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value]
let manageStage = strategy.manageStage
evalConditions(manageStage, 'Manage Stage')
@@ -936,7 +933,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
async function closeStage() {
/* Opening Status Procedure */
if (tradingEngine.current.strategy.closeStageStatus.value === 'Opening') {
if (tradingEngine.current.strategyCloseStage.status.value === 'Opening') {
/*
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
@@ -948,7 +945,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
/* Open Status Procedure */
if (tradingEngine.current.strategy.closeStageStatus.value === 'Open') {
if (tradingEngine.current.strategyCloseStage.status.value === 'Open') {
/*
This will happen as long as the Close Stage is Open.
*/
@@ -958,23 +955,40 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Close Execution')
evalFormulas(stageNode, 'Close Execution')
await tradingExecutionModule.checkExecution(executionNode, false, false, tradingEngine.current.position.openStageFilledSize, tradingEngine.current.position.closeStageOrdersSize, tradingEngine.current.position.closeStageFilledSize)
await tradingExecutionModule.checkExecution(
executionNode,
false,
false,
tradingEngine.current.strategyOpenStage.stageBaseAsset.sizeFilled, // Stage Size Limit
tradingEngine.current.strategyOpenStage.stageQuotedAsset.sizeFilled, // Stage Size Limit
tradingEngine.current.position.strategyCloseStage
)
/* Close the Stage Validation */
if (tradingEngine.current.position.closeStageFilledSize.value === tradingEngine.current.position.openStageFilledSize.value) {
/*
The Close Stage is closed when the fillSize reaches the filledSize of the Open Stage.
*/
tradingStrategyModule.updateStageStatus('Close Stage', 'Closed')
/*
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
at the exchange that orders were filled.
*/
if (
tradingEngine.current.strategyCloseStage.stageBaseAsset.filledSize.value +
tradingEngine.current.strategyCloseStage.stageBaseAsset.feesPaid.value >=
tradingEngine.current.strategyOpenStage.stageBaseAsset.filledSize.value * 0.999) {
tradingStrategyModule.updateStageStatus('Close Stage', 'Closed', 'Open Stage fillSize Filled')
} else {
/* Check the Close Stage Event */
evalConditions(stageNode, 'Close Stage Event')
if (checkStopStageEvent(stageNode) === true) {
tradingStrategyModule.updateStageStatus('Close Stage', 'Closing', 'Close Stage Event')
}
}
}
/* Exit Position Validation */
if (
tradingEngine.current.strategy.triggerStageStatus.value === 'Closed' &&
tradingEngine.current.strategy.openStageStatus.value === 'Closed' &&
tradingEngine.current.strategy.manageStageStatus.value === 'Closed' &&
tradingEngine.current.strategy.closeStageStatus.value === 'Closed'
tradingEngine.current.strategyTriggerStage.status.value === 'Closed' &&
tradingEngine.current.strategyOpenStage.status.value === 'Closed' &&
tradingEngine.current.strategyManageStage.status.value === 'Closed' &&
tradingEngine.current.strategyCloseStage.status.value === 'Closed'
) {
/* Exiting Everything now. */
@@ -1100,6 +1114,33 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
}
function checkStopStageEvent(stage) {
/* Check the Close Stage Event. */
let stopStageEvent = stage.stopStageEvent
if (stopStageEvent !== undefined) {
for (let k = 0; k < stopStageEvent.situations.length; k++) {
let situation = stopStageEvent.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(stopStageEvent.id)
tradingSystem.highlights.push(stage.id)
announcementsModule.makeAnnoucements(stopStageEvent)
return true
}
}
}
}
function isItInside(elementWithTimestamp, elementWithBeginEnd) {
/*
The function is to allow in Conditions and Formulas to easily know when an element with timestamp (like the ones of single files) are inside the time range