Trading Execution Implemented

This commit is contained in:
Luis Fernando Molina committed 2020-07-20 16:18:43 +02:00
1 parent 1838628258
commit b98650af8d
3 files changed
+450 -629

No files matched your search

+39 -53
View File
@@ -5,10 +5,9 @@
let bot = BOT
let thisObject = {
getTicker: getTicker,
getOpenOrders: getOpenOrders,
getOrder: getOrder,
createOrder: createOrder,
cancelOrder: cancelOrder,
initialize: initialize,
finalize: finalize
};
@@ -53,64 +52,27 @@
exchange = undefined
}
async function getTicker(callBackFunction) {
try {
logInfo("getTicker -> Entering function.");
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
let ticker
async function getOrder(tradingSystemOrder, tradingEngineOrder) {
if (exchange.has['fetchTicker']) {
ticker = await (exchange.fetchTicker(symbol))
callBackFunction(global.DEFAULT_OK_RESPONSE, ticker)
} else {
logError("getTicker -> Exchange does not support fetchTicker command.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
let orderId = tradingEngineOrder.exchangeId.value
} catch (err) {
logError("getTicker -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
/* Basic Logging */
logInfo("getOrder -> Entering function. orderId = " + orderId);
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
/* Basic Validations */
if (exchange.has['fetchOrder'] === false) {
logError("getOrder -> Exchange does not support fetchOrder command.");
return
}
}
async function getOpenOrders(market, callBackFunction) {
try {
logInfo("getOpenOrders -> Entering function. market = " + JSON.stringify(market));
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
let openOrders
if (exchange.has['fetchOpenOrders']) {
openOrders = await (exchange.fetchOpenOrders(symbol))
callBackFunction(global.DEFAULT_OK_RESPONSE, openOrders)
} else {
logError("getOpenOrders -> Exchange does not support fetchOpenOrders command.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
} catch (err) {
logError("getOpenOrders -> Error = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
async function getOrder(orderId, market, callBackFunction) {
try {
logInfo("getOrder -> Entering function. orderId = " + orderId);
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
let order
if (exchange.has['fetchOrder']) {
order = await (exchange.fetchOrder(orderId, symbol))
callBackFunction(global.DEFAULT_OK_RESPONSE, order)
} else {
logError("getOrder -> Exchange does not support fetchOrder command.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
let order = await (exchange.fetchOrder(orderId, symbol))
return order
} catch (err) {
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
logError("getOrder -> Error = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
@@ -171,6 +133,30 @@
}
}
async function cancelOrder(tradingSystemOrder, tradingEngineOrder) {
let orderId = tradingEngineOrder.exchangeId.value
/* Basic Logging */
logInfo("getOrder -> Entering function. orderId = " + orderId);
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
/* Basic Validations */
if (exchange.has['fetchOrder'] === false) {
logError("getOrder -> Exchange does not support fetchOrder command.");
return
}
try {
let order = await (exchange.cancelOrder(orderId, symbol))
return true
} catch (err) {
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
logError("getOrder -> Error = " + err.message);
}
}
function logInfo(message) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] ' + message) }
}
+395 -261
View File
@@ -5,313 +5,447 @@ exports.newTradingExecution = function newTradingExecution(bot, logger) {
const MODULE_NAME = 'Trading Execution'
let thisObject = {
takePosition: takePosition,
closePosition: closePosition,
checkExecution: checkExecution,
initialize: initialize,
finalize: finalize
}
let tradingEngine
let tradingSystem
return thisObject
function initialize() {
tradingSystem = bot.simulationState.tradingSystem
tradingEngine = bot.simulationState.tradingEngine
}
function finalize() {
tradingSystem = undefined
tradingEngine = undefined
}
function takePosition() {
/* Check that we are in LIVE or FORWARD MODE */
if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') {
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> Taking a Position in ' + bot.SESSION.type)
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.size.value = ' + tradingEngine.current.position.size.value)
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value)
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> slippageAmount = ' + slippageAmount)
logger.write(MODULE_NAME, '[PERSIST] runSimulation -> loop -> takePositionNow -> tradingEngine.current.position.rate.value = ' + tradingEngine.current.position.rate.value)
function checkExecution(executionNode, stageIsOpening, stageIsClosing, stageSizeLimit, stageOrdersSize, stageFilledSize) {
/* We see if we need to put the actual order at the exchange. */
if (variable.executionContext !== undefined) {
switch (variable.executionContext.status) {
case 'Without a Position': { // We need to put the order because It was not put yet.
if (strategy.openStage !== undefined) {
if (strategy.openStage.openExecution !== undefined) {
putOpeningOrder()
return
/* Enforcing Precision Limit */
stageSizeLimit.value = global.PRECISE(stageSizeLimit.value, 10)
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
checkExecutionAlgorithms(executionNode)
function checkExecutionAlgorithms(executionNode) {
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
let executionAlgorithm = executionNode.executionAlgorithms[i]
checkOrders(executionAlgorithm.marketBuyOrders, executionAlgorithm, executionNode)
checkOrders(executionAlgorithm.marketSellOrders, executionAlgorithm, executionNode)
checkOrders(executionAlgorithm.limitBuyOrders, executionAlgorithm, executionNode)
checkOrders(executionAlgorithm.limitSellOrders, executionAlgorithm, executionNode)
}
}
function checkOrders(orders, executionAlgorithm, executionNode) {
for (let i = 0; i < orders.length; i++) {
let tradingSystemOrder = orders[i]
/* Trading System Validations */
if (tradingSystemOrder.config.positionSizePercentage === undefined) { continue }
if (tradingSystemOrder.referenceParent === undefined) { continue }
let tradingEngineOrder = tradingEngineModule.getNodeById(tradingSystemOrder.referenceParent.id)
/* Trading Engine Validations */
if (tradingEngineOrder.serialNumber === undefined) { continue }
if (tradingEngineOrder.identifier === undefined) { continue }
if (tradingEngineOrder.exchangeId === undefined) { continue }
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 }
if (tradingEngineOrder.orderCounters.periods === undefined) { continue }
if (tradingEngineOrder.orderStatistics.days === undefined) { continue }
if (tradingEngineOrder.orderStatistics.percentageFilled === undefined) { continue }
if (tradingEngineOrder.orderStatistics.actualRate === undefined) { continue }
if (tradingEngineOrder.orderStatistics.feesPaid === undefined) { continue }
switch (tradingEngineOrder.status.value) {
case 'Not Open': {
{
/* Check if we need to Create this Order */
if (stageIsClosing === true) { continue }
let situationName = checkOrderEvent(tradingSystemOrder.createOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (situationName !== undefined) {
/* Open a new order */
openOrder(tradingSystemOrder, tradingEngineOrder, situationName)
}
}
break
}
case 'Position Closed': { // Waiting for a confirmation that the position was closed.
if (strategy.openStage !== undefined) {
if (strategy.openStage.openExecution !== undefined) {
putOpeningOrder()
return
case 'Open': {
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
/* Simulate Events that happens at the Exchange, if needed. */
simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder)
/* Check Events that happens at the Exchange, if needed. */
checkExchangeEvents(tradingSystemOrder, tradingEngineOrder)
/* If the stage is closing or the order is not still 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) {
simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Cancel Event')
}
}
break
}
case 'Taking Position': { // Waiting for a confirmation that the position was taken.
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is Taking Position.')
break
}
case 'In a Position': { // This should mean that we already put the order at the exchange.
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Exiting code block because status is In a Position.')
break
}
}
} else { // The context does not exist so it means we are not in a position.
if (strategy.openStage !== undefined) {
if (strategy.openStage.openExecution !== undefined) {
putOpeningOrder()
return
}
}
}
} else {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> takePositionNow -> Not trading live.')
}
}
function openOrder(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)
}
function putOpeningOrder() {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Entering function.')
/* Check Order Size */
if (tradingEngineOrder.size.value <= 0) { return }
/* We wont take a position unless we are withing the sessionParameters.timeRange.config.initialDatetime and the sessionParameters.timeRange.config.finalDatetime range */
if (sessionParameters.timeRange.config.initialDatetime !== undefined) {
if (candle.end < sessionParameters.timeRange.config.initialDatetime) {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because current candle ends before the start date. -> sessionParameters.timeRange.config.initialDatetime = ' + sessionParameters.timeRange.config.initialDatetime)
takePositionAtSimulation()
return
/* Place Order at the Exchange */
if (createOrderAtExchange(tradingSystemOrder, tradingEngineOrder) !== true) { return }
/* Updating Episode Counters */
tradingEngine.episode.episodeCounters.orders.value++
/* Initialize this */
tradingEngine.current.distanceToEvent.createOrder.value = 1
/* Create Order Procedure */
tradingEngineOrder.status.value = 'Open'
tradingEngineOrder.identifier.value = global.UNIQUE_ID()
tradingEngineOrder.begin.value = tradingEngine.current.candle.begin.value
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
tradingEngineOrder.serialNumber.value = tradingEngine.episode.episodeCounters.orders.value
tradingEngineOrder.orderName.value = tradingSystemOrder.name
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
tradingEngineOrder.situationName.value = situationName
/* 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)
}
}
/* Update Stage Orders Size */
stageOrdersSize.value = stageOrdersSize.value + tradingEngineOrder.size.value
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
}
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 = exchangeAPIModule.createOrder(tradingSystemOrder, tradingEngineOrder)
if (orderId !== undefined) {
tradingEngineOrder.exchangeId.value = orderId
return true
}
}
/* We wont take a position if we are past the final datetime */
if (sessionParameters.timeRange.config.finalDatetime !== undefined) {
if (candle.begin > sessionParameters.timeRange.config.finalDatetime) {
logger.write(MODULE_NAME, '[INFO] runSimulation -> putOpeningOrder -> Not placing the trade at the exchange because current candle begins after the end date. -> sessionParameters.timeRange.config.finalDatetime = ' + sessionParameters.timeRange.config.finalDatetime)
takePositionAtSimulation()
return
function simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder) {
/* 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
}
}
/* Filter by what is defined at the Strategy */
if (tradingSystemOrder.simulatedExchangeEvents === undefined) { return }
/* Partial Fill Simulation */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) {
/* Percentage Filled Calculation */
let percentageFilled = tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability * 100
if (tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled > 100) {
percentageFilled = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
}
tradingEngineOrder.orderStatistics.percentageFilled.value = tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled
if (tradingEngineOrder.orderStatistics.percentageFilled.value === 100) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Filled'
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
}
/* Filled Size Calculation */
stageFilledSize.value = stageFilledSize.value + tradingEngineOrder.size.value * percentageFilled / 100
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') {
simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Closing Stage')
}
}
}
/* Actual Rate Simulation */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) {
if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) {
tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id)
}
}
}
/* Fees Paid Simulation */
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
}
}
}
}
/* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */
if (variable.executionContext !== undefined) {
if (variable.executionContext.periods !== undefined) {
if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because it was already placed at a previous execution.')
takePositionAtSimulation()
function checkExchangeEvents(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 order = exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder)
if (order === undefined) { return }
const AT_EXCHANGE_STATUS = {
OPEN: 'open',
CLOSED: 'closed',
CANCELLED: 'canceled'
}
/* Status Checks */
if (tradingEngineOrder.orderStatistics.percentageFilled.value === 100 && order.status === AT_EXCHANGE_STATUS.CLOSED) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Filled'
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
}
if (tradingEngineOrder.orderStatistics.percentageFilled.value < 100 && order.status === AT_EXCHANGE_STATUS.CLOSED) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Closed at the Exchange'
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
}
if (order.status === AT_EXCHANGE_STATUS.CANCELLED) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Cancelled at the Exchange'
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
}
syncWithExchange(tradingSystemOrder, tradingEngineOrder, order)
/* Forced Cancellation Check */
if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') {
exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Closing Stage')
}
}
function syncWithExchange(tradingSystemOrder, tradingEngineOrder, order) {
/* Percentage Filled */
let currentPercentageFilled = tradingEngineOrder.orderStatistics.percentageFilled.value
let percentageFilled = order.filled * 100 / tradingEngineOrder.size
percentageFilled = global.PRECISE(percentageFilled.value, 10)
tradingEngineOrder.orderStatistics.percentageFilled.value = percentageFilled
/* Filled Size Calculation */
stageFilledSize.value = stageFilledSize.value - tradingEngineOrder.size.value * currentPercentageFilled / 100 // First remove the current filled size
stageFilledSize.value = stageFilledSize.value + tradingEngineOrder.size.value * percentageFilled / 100 // Second add the new filled size
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
/* Actual Rate Simulation */
tradingEngineOrder.orderStatistics.actualRate.value = order.cost / order.filled
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
/* Fees Paid Simulation */
tradingEngineOrder.orderStatistics.feesPaid.value = order.fee.rate
tradingEngineOrder.orderStatistics.feesPaid.value = global.PRECISE(tradingEngineOrder.orderStatistics.feesPaid.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
}
}
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = exitType
/* 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)
}
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
}
}
}
/* We are not going to place orders based on outdated information. The next filter prevents firing orders when backtesting. */
if (currentDay) {
let today = new Date(Math.trunc((new Date().valueOf()) / global.ONE_DAY_IN_MILISECONDS) * global.ONE_DAY_IN_MILISECONDS)
let processDay = new Date(Math.trunc(currentDay.valueOf() / global.ONE_DAY_IN_MILISECONDS) * global.ONE_DAY_IN_MILISECONDS)
if (today.valueOf() !== processDay.valueOf()) {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the current candle belongs to the previous day and that is considered simulation and not live trading.')
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> today = ' + today)
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> processDay = ' + processDay)
takePositionAtSimulation()
return
/* Check if we can cancel the order at the Exchange. */
if (exchangeAPIModule.exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder) === true) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = exitType
/* 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 = exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder)
if (order === undefined) { return }
const AT_EXCHANGE_STATUS = {
OPEN: 'open',
CLOSED: 'closed',
CANCELLED: 'canceled'
}
syncWithExchange(tradingSystemOrder, tradingEngineOrder, order)
}
}
let orderPrice
let amountA
let amountB
let orderSide
if (bot.sessionAndMarketBaseAssetsAreEqual) {
orderSide = 'sell'
orderPrice = tradingEngine.current.position.rate.value
amountA = tradingEngine.current.position.size.value * orderPrice
amountB = tradingEngine.current.position.size.value
} else {
orderSide = 'buy'
orderPrice = tradingEngine.current.position.rate.value
amountA = tradingEngine.current.position.size.value
amountB = tradingEngine.current.position.size.value / orderPrice
}
variable.executionContext = {
status: 'Taking Position',
periods: tradingEngine.episode.episodeCounters.periods.value
}
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to create order.')
exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated)
function onOrderCreated(err, order) {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Entering function.')
try {
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ')
variable.executionContext = {
status: 'In a Position',
periods: tradingEngine.episode.episodeCounters.periods.value,
amountA: amountA,
amountB: amountB,
orderId: order.id
}
takePositionAtSimulation()
return
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 global.DEFAULT_FAIL_RESPONSE.result: {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ')
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message)
strategy.openStage.openExecution.error = err.message
afterLoop()
return
}
case global.DEFAULT_RETRY_RESPONSE.result: {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ')
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = ' + err.message)
strategy.openStage.openExecution.error = err.message
afterLoop()
return
passed = 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
}
}
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
}
}
function closePosition() {
/* Check that we are in LIVE MODE */
if (bot.SESSION.type === 'Live Trading Session' || bot.SESSION.type === 'Fordward Testion Session') {
/* We see if we need to put the actual order at the exchange. */
if (variable.executionContext !== undefined) {
switch (variable.executionContext.status) {
case 'Without a Position': { // No way to close anything at the exchange.
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Without a Position.')
break
}
case 'In a Position': { // This should mean that we already put the order at the exchange.
if (strategy.closeStage !== undefined) {
if (strategy.closeStage.closeExecution !== undefined) {
putClosingOrder()
return
}
}
break
}
case 'Closing Position': { // Waiting for a confirmation that the position was taken.
if (strategy.closeStage !== undefined) {
if (strategy.closeStage.closeExecution !== undefined) {
putClosingOrder()
return
}
}
break
}
case 'Position Closed': { //
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Closing a Position -> Exiting code block because status is Position Closed.')
break
}
}
}
}
}
function putClosingOrder() {
/* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */
if (variable.executionContext !== undefined) {
if (variable.executionContext.periods !== undefined) {
if (tradingEngine.episode.episodeCounters.periods.value <= variable.executionContext.periods) {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because this closing was already submited at a previous execution.')
closePositionAtSimulation()
return
}
}
}
let orderPrice
let amountA
let amountB
let orderSide
if (bot.sessionAndMarketBaseAssetsAreEqual) {
orderSide = 'buy'
orderPrice = ticker.last + 100 // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff.
amountA = tradingEngine.current.balance.quotedAsset.value
amountB = tradingEngine.current.balance.quotedAsset.value / orderPrice
} else {
orderSide = 'sell'
orderPrice = ticker.last - 100 // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff.
amountA = tradingEngine.current.balance.baseAsset.value * orderPrice
amountB = tradingEngine.current.balance.baseAsset.value
}
variable.executionContext = {
status: 'Closing Position',
periods: tradingEngine.episode.episodeCounters.periods.value
}
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange.')
exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated)
function onOrderCreated(err, order) {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Entering function.')
try {
switch (err.result) {
case global.DEFAULT_OK_RESPONSE.result: {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE ')
variable.executionContext = {
status: 'Position Closed',
periods: tradingEngine.episode.episodeCounters.periods.value,
amountA: amountA,
amountB: amountB,
orderId: order.id
}
closePositionAtSimulation()
return
}
case global.DEFAULT_FAIL_RESPONSE.result: {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE ')
/* We will assume that the problem is temporary, and expect that it will work at the next execution. */
strategy.closeStage.closeExecution.error = err.message
afterLoop()
return
}
case global.DEFAULT_RETRY_RESPONSE.result: {
logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE ')
strategy.closeStage.closeExecution.error = err.message
afterLoop()
return
}
}
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Unexpected Response -> Message = ' + err.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
} catch (err) {
logger.write(MODULE_NAME, '[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> err = ' + err.stack)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
return
}
}
}
+16 -315
View File
@@ -23,8 +23,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
let tradingEngine
let sessionParameters
let conditions = new Map()
let formulas = new Map()
tradingSystem.conditions = new Map()
tradingSystem.formulas = new Map()
const EXCHANGE_API_MODULE = require('./ExchangeAPI.js')
let exchangeAPIModule = EXCHANGE_API_MODULE.newExchangeAPI(bot, logger)
@@ -80,8 +80,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
chart = undefined
conditions = undefined
formulas = undefined
tradingSystem.conditions = undefined
tradingSystem.formulas = undefined
tradingSystem.highlights = undefined
tradingSystem.errors = undefined
@@ -270,7 +270,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
}
conditions.set(node.id, value)
tradingSystem.conditions.set(node.id, value)
if (value === true) {
tradingSystem.highlights.push(node.id)
@@ -311,7 +311,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
}
formulas.set(node.id, value)
tradingSystem.formulas.set(node.id, value)
if (error !== undefined) {
tradingSystem.errors.push([node.id, error])
@@ -524,7 +524,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Open Execution')
evalFormulas(stageNode, 'Open Execution')
checkExecution(executionNode, true, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
tradingExecutionModule.checkExecution(executionNode, true, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
/* From here on, the state is officially Open */
tradingStrategyModule.updateStageStatus('Open Stage', 'Open')
@@ -544,7 +544,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Open Execution')
evalFormulas(stageNode, 'Open Execution')
checkExecution(executionNode, false, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
tradingExecutionModule.checkExecution(executionNode, false, false, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
/*
The Open is finished when the fillSize reaches the Position Size.
@@ -572,7 +572,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Open Execution')
evalFormulas(stageNode, 'Open Execution')
checkExecution(executionNode, false, true, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
tradingExecutionModule.checkExecution(executionNode, false, true, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
}
/*
@@ -609,7 +609,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
if (strategy.openStage.initialDefinition.positionSize === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionSize.formula === undefined) return DEFAULT_VALUE
let value = formulas.get(strategy.openStage.initialDefinition.positionSize.formula.id)
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionSize.formula.id)
if (value === undefined) return DEFAULT_VALUE
return value
}
@@ -623,7 +623,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
if (strategy.openStage.initialDefinition.positionRate === undefined) return DEFAULT_VALUE
if (strategy.openStage.initialDefinition.positionRate.formula === undefined) return DEFAULT_VALUE
let value = formulas.get(strategy.openStage.initialDefinition.positionRate.formula.id)
let value = tradingSystem.formulas.get(strategy.openStage.initialDefinition.positionRate.formula.id)
if (value === undefined) return DEFAULT_VALUE
return value
}
@@ -958,7 +958,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
evalConditions(stageNode, 'Close Execution')
evalFormulas(stageNode, 'Close Execution')
checkExecution(executionNode, false, false, tradingEngine.current.position.openStageFilledSize, tradingEngine.current.position.closeStageOrdersSize, tradingEngine.current.position.closeStageFilledSize)
tradingExecutionModule.checkExecution(executionNode, false, false, tradingEngine.current.position.openStageFilledSize, tradingEngine.current.position.closeStageOrdersSize, tradingEngine.current.position.closeStageFilledSize)
/* Close the Stage Validation */
if (tradingEngine.current.position.closeStageFilledSize.value === tradingEngine.current.position.openStageFilledSize.value) {
@@ -1071,7 +1071,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
if (phase !== undefined) {
if (phase.formula !== undefined) {
let previousValue = tradingEngine.current.position.stopLoss.value
tradingPositionModule.applyStopLossFormula(formulas, phase.formula.id)
tradingPositionModule.applyStopLossFormula(tradingSystem.formulas, phase.formula.id)
if (tradingEngine.current.position.stopLoss.value !== previousValue) {
announcementsModule.makeAnnoucements(phase)
@@ -1103,7 +1103,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
if (phase !== undefined) {
if (phase.formula !== undefined) {
let previousValue = tradingEngine.current.position.takeProfit.value
tradingPositionModule.applyTakeProfitFormula(formulas, phase.formula.id)
tradingPositionModule.applyTakeProfitFormula(tradingSystem.formulas, phase.formula.id)
if (tradingEngine.current.position.takeProfit.value !== previousValue) {
announcementsModule.makeAnnoucements(phase)
@@ -1112,305 +1112,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
}
function checkExecution(executionNode, stageIsOpening, stageIsClosing, stageSizeLimit, stageOrdersSize, stageFilledSize) {
/* Enforcing Precision Limit */
stageSizeLimit.value = global.PRECISE(stageSizeLimit.value, 10)
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
checkExecutionAlgorithms(executionNode)
function checkExecutionAlgorithms(executionNode) {
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
let executionAlgorithm = executionNode.executionAlgorithms[i]
checkOrders(executionAlgorithm.marketBuyOrders, executionAlgorithm, executionNode)
checkOrders(executionAlgorithm.marketSellOrders, executionAlgorithm, executionNode)
checkOrders(executionAlgorithm.limitBuyOrders, executionAlgorithm, executionNode)
checkOrders(executionAlgorithm.limitSellOrders, executionAlgorithm, executionNode)
}
}
function checkOrders(orders, executionAlgorithm, executionNode) {
for (let i = 0; i < orders.length; i++) {
let tradingSystemOrder = orders[i]
/* Basic Validations */
if (tradingSystemOrder.config.positionSizePercentage === undefined) { continue }
if (tradingSystemOrder.referenceParent === undefined) { continue }
let tradingEngineOrder = tradingEngineModule.getNodeById(tradingSystemOrder.referenceParent.id)
if (tradingEngineOrder.identifier === undefined) { continue }
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 }
if (tradingEngineOrder.orderCounters.periods === undefined) { continue }
if (tradingEngineOrder.orderStatistics.days === undefined) { continue }
if (tradingEngineOrder.orderStatistics.percentageFilled === undefined) { continue }
if (tradingEngineOrder.orderStatistics.actualRate === undefined) { continue }
if (tradingEngineOrder.orderStatistics.feesPaid === undefined) { continue }
switch (tradingEngineOrder.status.value) {
case 'Not Open': {
{
/* Check if we need to Create this Order */
if (stageIsClosing === true) { continue }
let situationName = checkOrderEvent(tradingSystemOrder.createOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (situationName !== undefined) {
/* Order Size Calculation */
tradingEngineOrder.size.value = 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)
}
if (tradingEngineOrder.size.value <= 0) { continue }
/* Place Order at the Exchange */
if (placeOrderAtExchange(tradingSystemOrder, tradingEngineOrder) !== true) { continue }
/* Updating Episode Counters */
tradingEngine.episode.episodeCounters.orders.value++
/* Initialize this */
tradingEngine.current.distanceToEvent.createOrder.value = 1
/* Create Order Procedure */
tradingEngineOrder.status.value = 'Open'
tradingEngineOrder.identifier.value = global.UNIQUE_ID()
tradingEngineOrder.begin.value = tradingEngine.current.candle.begin.value
tradingEngineOrder.end.value = tradingEngine.current.candle.end.value
tradingEngineOrder.serialNumber.value = tradingEngine.episode.episodeCounters.orders.value
tradingEngineOrder.orderName.value = tradingSystemOrder.name
tradingEngineOrder.algorithmName.value = executionAlgorithm.name
tradingEngineOrder.situationName.value = situationName
/* 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 = formulas.get(tradingSystemOrder.positionRate.formula.id)
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
}
}
/* Update Stage Orders Size */
stageOrdersSize.value = stageOrdersSize.value + tradingEngineOrder.size.value
stageOrdersSize.value = global.PRECISE(stageOrdersSize.value, 10)
}
}
break
}
case 'Open': {
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
/* If the stage is closing we wont be moving or cancelling orders based on defined events */
if (stageIsClosing !== true) {
/* Check if we need to Cancel this Order */
let situationName = checkOrderEvent(tradingSystemOrder.cancelOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (situationName !== undefined) {
/* 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)
/* Cancel the Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Cancelled by Event'
/* Initialize this */
tradingEngine.current.distanceToEvent.cancelOrder.value = 1
break
}
/* Check if we need to Move this Order */
if (tradingSystemOrder.moveOrderEvents !== undefined) {
for (let j = 0; j < tradingSystemOrder.moveOrderEvents.length; j++) {
let moveOrderEvent = tradingSystemOrder.moveOrderEvents[j]
let situationName = checkOrderEvent(moveOrderEvent, tradingSystemOrder, executionAlgorithm, executionNode)
if (situationName !== undefined) {
if (moveOrderEvent.positionRate !== undefined) {
if (moveOrderEvent.positionRate.formula !== undefined) {
/* Move Order to a different Rate */
tradingEngineOrder.rate.value = formulas.get(moveOrderEvent.positionRate.formula.id)
tradingEngineOrder.rate.value = global.PRECISE(tradingEngineOrder.rate.value, 10)
/* Initialize this */
tradingEngine.current.distanceToEvent.moveOrder.value = 1
break
}
}
}
}
}
}
/* Simulate Events that happens at the Exchange, if needed. */
simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder)
}
}
}
}
function placeOrderAtExchange(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 = exchangeAPIModule.createOrder(tradingSystemOrder, tradingEngineOrder)
if (orderId !== undefined) {
tradingEngineOrder.exchangeId.value = orderId
return true
}
}
function simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder) {
/* 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
}
}
/* Filter by what is defined at the Strategy */
if (tradingSystemOrder.simulatedExchangeEvents === undefined) { return }
/* Partial Fill Simulation */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) {
/* Percentage Filled Calculation */
let percentageFilled = tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability * 100
if (tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled > 100) {
percentageFilled = 100 - tradingEngineOrder.orderStatistics.percentageFilled.value
}
tradingEngineOrder.orderStatistics.percentageFilled.value = tradingEngineOrder.orderStatistics.percentageFilled.value + percentageFilled
if (tradingEngineOrder.orderStatistics.percentageFilled.value === 100) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Filled'
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
}
/* Filled Size Calculation */
stageFilledSize.value = stageFilledSize.value + tradingEngineOrder.size.value * percentageFilled / 100
stageFilledSize.value = global.PRECISE(stageFilledSize.value, 10)
if (stageIsClosing === true && tradingEngineOrder.status.value !== 'Closed') {
if (tradingEngineOrder.orderStatistics.percentageFilled.value === 0) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Cancelled by Closing Stage'
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
}
if (tradingEngineOrder.orderStatistics.percentageFilled.value > 0 && tradingEngineOrder.orderStatistics.percentageFilled.value < 100) {
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = 'Partially Filled'
/* 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)
}
}
}
/* Actual Rate Simulation */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) {
if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) {
tradingEngineOrder.orderStatistics.actualRate.value = formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id)
}
}
}
/* Fees Paid Simulation */
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
}
}
}
}
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 = 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
}
}
}
}
}
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
@@ -1429,8 +1130,8 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
let condition = situation.conditions[m]
let value = false
if (conditions.get(condition.id) !== undefined) {
value = conditions.get(condition.id)
if (tradingSystem.conditions.get(condition.id) !== undefined) {
value = tradingSystem.conditions.get(condition.id)
}
if (value === true) {
tradingSystem.highlights.push(condition.id)