Implementation Finished

This commit is contained in:
Luis Fernando Molina committed 2020-07-23 14:35:23 +02:00
1 parent e84f8a6ed1
commit 6ec5580cd4
32 files changed
+1335 -6639

No files matched your search

-152
View File
@@ -1,152 +0,0 @@
exports.newExchangeAPI = function newExchangeAPI(logger, BOT) {
let MODULE_NAME = "Exchange API";
let LOG_INFO = true;
let apiClient;
let bot = BOT
let thisObject = {
initialize: initialize,
getTicker: getTicker,
getOpenOrders: getOpenOrders,
getOrder: getOrder,
createOrder: createOrder,
getMaxDecimalPositions: getMaxDecimalPositions
};
let exchangeId
let options = {}
let exchange
const ccxt = require('ccxt')
return thisObject;
function initialize() {
exchangeId = bot.exchange.toLowerCase()
let key = process.env.KEY
let secret = process.env.SECRET
if (key === "undefined") { key = undefined }
if (secret === "undefined") { secret = undefined }
const exchangeClass = ccxt[exchangeId]
const exchangeConstructorParams = {
'apiKey': key,
'secret': secret,
'timeout': 30000,
'enableRateLimit': true,
verbose: false,
options: options
}
exchange = new exchangeClass(exchangeConstructorParams)
}
function getMaxDecimalPositions() {
return 8
}
async function getTicker(market, callBackFunction) {
try {
logInfo("getTicker -> Entering function.");
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
let ticker
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);
}
} catch (err) {
logError("getTicker -> err = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
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);
}
} catch (err) {
logError("getOrder -> Error = " + err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
async function createOrder(market, side, price, cost, amount, callBackFunction) {
try {
logInfo("createOrder -> Entering function.");
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
logInfo("createOrder -> market = " + market.assetA + "_" + market.assetB);
logInfo("createOrder -> side = " + side);
logInfo("createOrder -> cost = " + cost);
logInfo("createOrder -> amount = " + amount);
logInfo("createOrder -> price = " + price);
if (side !== "buy" && side !== "sell") {
logError("createOrder -> side must be either 'buy' or 'sell'.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
let type = 'market'
let order
if (exchange.has['createOrder']) {
order = await (exchange.createOrder(symbol, type, side, amount))
callBackFunction(global.DEFAULT_OK_RESPONSE, order)
} else {
logError("createOrder -> Exchange does not support createOrder command.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
} catch (err) {
logError("createOrder -> err = " + JSON.stringify(err.stack) || err.message);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function logInfo(message) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] ' + message) }
}
function logError(message) {
logger.write(MODULE_NAME, '[ERROR] ' + message)
}
};
@@ -0,0 +1,162 @@
exports.newExchangeAPI = function newExchangeAPI(bot, logger) {
let MODULE_NAME = "Exchange API";
let thisObject = {
getOrder: getOrder,
createOrder: createOrder,
cancelOrder: cancelOrder,
initialize: initialize,
finalize: finalize
};
let tradingSystem
let exchangeId
let options = {}
let exchange
const ccxt = require('ccxt')
return thisObject;
function initialize() {
tradingSystem = bot.simulationState.tradingSystem
exchangeId = bot.exchange.toLowerCase()
let key = bot.KEY
let secret = bot.SECRET
const exchangeClass = ccxt[exchangeId]
const exchangeConstructorParams = {
'apiKey': key,
'secret': secret,
'timeout': 30000,
'enableRateLimit': true,
verbose: false,
options: options
}
exchange = new exchangeClass(exchangeConstructorParams)
}
function finalize() {
tradingSystem = undefined
exchangeId = undefined
options = undefined
exchange = undefined
}
async function getOrder(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.fetchOrder(orderId, symbol)
return order
} catch (err) {
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
logError("getOrder -> Error = " + err.message);
}
}
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
switch (tradingSystemOrder.type) {
case 'Market Buy Order': {
type = 'market'
side = 'buy'
break
}
case 'Market Sell Order': {
type = 'market'
side = 'sell'
break
}
case 'Limit Buy Order': {
type = 'limit'
side = 'buy'
break
}
case 'Limit Sell Order': {
type = 'limit'
side = 'sell'
break
}
}
/* Basic Logging */
logInfo("createOrder -> symbol = " + symbol);
logInfo("createOrder -> side = " + side);
logInfo("createOrder -> type = " + type);
logInfo("createOrder -> amount = " + amount);
logInfo("createOrder -> price = " + price);
/* Basic Validations */
if (side !== "buy" && side !== "sell") {
logError("createOrder -> side must be either 'buy' or 'sell'.");
return
}
if (exchange.has['createOrder'] === false) {
logError("createOrder -> Exchange does not support createOrder command.");
return
}
try {
let order = await (exchange.createOrder(symbol, type, side, amount, price))
return order.id
} catch (err) {
tradingSystem.errors.push([tradingSystemOrder.id, err.message])
logError("getOrder -> Error = " + err.message);
}
}
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) {
logger.write(MODULE_NAME, '[INFO] ' + message)
}
function logError(message) {
logger.write(MODULE_NAME, '[ERROR] ' + message)
}
};
+1 -1
View File
@@ -244,7 +244,7 @@ exports.newSnapshots = function newSnapshots(bot, logger) {
}
}
} catch (err) {
tradingSystem.errors.push(nodeWithCode.id, err.message)
tradingSystem.errors.push([nodeWithCode.id, err.message])
logger.write(MODULE_NAME, '[ERROR] runSimulation -> addCodeToSnapshot -> nodeWithCode.code = ' + nodeWithCode.code)
logger.write(MODULE_NAME, '[ERROR] runSimulation -> addCodeToSnapshot -> err = ' + err.stack)
}
@@ -190,12 +190,6 @@ exports.newTradingEngine = function newTradingEngine(bot, logger) {
tradingEngine.current.distanceToEvent.createOrder.value++
}
if (
tradingEngine.current.distanceToEvent.moveOrder.value > 0
) {
tradingEngine.current.distanceToEvent.moveOrder.value++
}
if (
tradingEngine.current.distanceToEvent.cancelOrder.value > 0
) {
+528 -261
View File
@@ -1,317 +1,584 @@
exports.newTradingExecution = function newTradingExecution(bot, logger) {
exports.newTradingExecution = function newTradingExecution(bot, logger, tradingEngineModule) {
/*
The Trading Execution modules manages the execution of orders against the exchanges.
*/
const MODULE_NAME = 'Trading Execution'
let thisObject = {
takePosition: takePosition,
closePosition: closePosition,
checkExecution: checkExecution,
initialize: initialize,
finalize: finalize
}
let tradingEngine
let tradingSystem
let sessionParameters
const EXCHANGE_API_MODULE = require('./ExchangeAPI.js')
let exchangeAPIModule = EXCHANGE_API_MODULE.newExchangeAPI(bot, logger)
const ANNOUNCEMENTS_MODULE = require('./Announcements.js')
let announcementsModule = ANNOUNCEMENTS_MODULE.newAnnouncements(bot, logger)
return thisObject
function initialize() {
tradingSystem = bot.simulationState.tradingSystem
tradingEngine = bot.simulationState.tradingEngine
sessionParameters = bot.SESSION.parameters
exchangeAPIModule.initialize()
announcementsModule.initialize()
}
function finalize() {
tradingSystem = undefined
tradingEngine = undefined
sessionParameters = undefined
exchangeAPIModule.finalize()
exchangeAPIModule = undefined
announcementsModule.finalize()
announcementsModule = 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)
async 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)
await checkExecutionAlgorithms(executionNode)
async function checkExecutionAlgorithms(executionNode) {
for (let i = 0; i < executionNode.executionAlgorithms.length; i++) {
let executionAlgorithm = executionNode.executionAlgorithms[i]
await checkOrders(executionAlgorithm.marketBuyOrders, executionAlgorithm, executionNode)
await checkOrders(executionAlgorithm.marketSellOrders, executionAlgorithm, executionNode)
await checkOrders(executionAlgorithm.limitBuyOrders, executionAlgorithm, executionNode)
await checkOrders(executionAlgorithm.limitSellOrders, executionAlgorithm, executionNode)
}
}
async 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 */
await openOrder(executionAlgorithm, 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. */
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 */
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')
await 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.')
}
}
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)
}
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
/* 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)
}
}
/* Place Order at the Exchange */
let result = await createOrderAtExchange(tradingSystemOrder, tradingEngineOrder)
if (result !== 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
/* Update Stage Orders Size */
stageOrdersSize.value = stageOrdersSize.value + tradingEngineOrder.size.value
stageOrdersSize.value = global.PRECISE(stageOrdersSize.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
}
}
/* 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 }
let previousSizeFilled = tradingEngineOrder.orderStatistics.sizeFilled.value
let previousPercentageFilled = tradingEngineOrder.orderStatistics.percentageFilled.value
let previousAmountReceived = tradingEngineOrder.orderStatistics.amountReceived.value
let previousFeesPaid = tradingEngineOrder.orderStatistics.feesPaid.value
actualRateSimulation()
feesPaidSimulation()
orderFillingSimulation()
sizeFilledSimulation()
amountReceivedSimulation()
doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, 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') {
simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Closing Stage')
}
function actualRateSimulation() {
/* Actual Rate Simulation */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula !== undefined) {
/* Calculate this only once for this order */
if (tradingEngineOrder.orderStatistics.actualRate.value === tradingEngineOrder.orderStatistics.actualRate.config.initialValue) {
tradingEngineOrder.orderStatistics.actualRate.value = tradingSystem.formulas.get(tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate.formula.id)
}
}
} else {
switch (tradingEngineOrder.type) {
case 'Market Order': {
/* Actual Rate is simulated based on the Session Paremeters */
let slippageAmount = tradingEngineOrder.rate.value * bot.SESSION.parameters.slippage.config.positionRate / 100
switch (tradingSystemOrder.type) {
case 'Market Sell Order': {
tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value - slippageAmount
break
}
case 'Market Buy Order': {
tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value + slippageAmount
break
}
}
break
}
case 'Limit Order': {
/* In Limit Orders the actual rate is the rate of the order, there is no slippage */
tradingEngineOrder.orderStatistics.actualRate.value = tradingEngineOrder.rate.value
break
}
}
}
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
}
function feesPaidSimulation() {
/* 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
}
}
} else {
/* Fees are simulated based on the Session Paremeters */
switch (tradingEngineOrder.type) {
case 'Market Order': {
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * bot.SESSION.parameters.feeStructure.config.taker / 100
break
}
case 'Limit Order': {
tradingEngineOrder.orderStatistics.feesPaid.value = tradingEngineOrder.size.value * bot.SESSION.parameters.feeStructure.config.maker / 100
break
}
}
}
tradingEngineOrder.orderStatistics.feesPaid.value = global.PRECISE(tradingEngineOrder.orderStatistics.feesPaid.value, 10)
}
function orderFillingSimulation() {
/* Order Filling Simulation */
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill !== undefined) {
if (tradingSystemOrder.simulatedExchangeEvents.simulatedPartialFill.config.fillProbability !== undefined) {
/* Percentage Filled */
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
tradingEngineOrder.orderStatistics.percentageFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.percentageFilled.value, 10)
/* Check if we need to close */
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
}
}
}
}
function sizeFilledSimulation() {
/* Size Filled */
tradingEngineOrder.orderStatistics.sizeFilled.value = tradingEngineOrder.size.value * tradingEngineOrder.orderStatistics.percentageFilled.value / 100
tradingEngineOrder.orderStatistics.sizeFilled.value = global.PRECISE(tradingEngineOrder.orderStatistics.sizeFilled.value, 10)
}
function amountReceivedSimulation() {
/* Amount Received */
tradingEngineOrder.orderStatistics.amountReceived.value = tradingEngineOrder.orderStatistics.sizeFilled.value * tradingSystemOrder.simulatedExchangeEvents.simulatedActualRate
tradingEngineOrder.orderStatistics.amountReceived.value = global.PRECISE(tradingEngineOrder.orderStatistics.amountReceived.value, 10)
}
}
/* 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()
async 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 = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder)
if (order === undefined) { return }
const AT_EXCHANGE_STATUS = {
OPEN: 'open',
CLOSED: 'closed',
CANCELLED: 'canceled'
}
/* Status Checks */
if (order.remaining === 0 && 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 (order.remaining > 0 && 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') {
await exchangeCancelOrder(tradingSystemOrder, tradingEngineOrder, 'Closing Stage')
}
}
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
/* 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 */
tradingEngineOrder.orderStatistics.actualRate.value = order.price
tradingEngineOrder.orderStatistics.actualRate.value = global.PRECISE(tradingEngineOrder.orderStatistics.actualRate.value, 10)
doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid)
}
function doTheAccounting(tradingSystemOrder, tradingEngineOrder, previousSizeFilled, previousPercentageFilled, previousAmountReceived, previousFeesPaid) {
/* 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)
/* Balances Update */
switch (true) {
case tradingSystemOrder.type === 'Market Buy Order' || tradingSystemOrder.type === 'Limit Buy Order': {
/* 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
/* Undo the previous accounting */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value + previousAmountReceived
/* Account the current filling and fees */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngineOrder.orderStatistics.amountReceived.value
break
}
case tradingSystemOrder.type === 'Market Sell Order' || tradingSystemOrder.type === 'Limit Sell Order': {
/* 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
/* Undo the previous accounting */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value - previousAmountReceived
/* Account the current filling and fees */
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngineOrder.orderStatistics.amountReceived.value
break
}
}
}
function simulateCancelOrder(tradingSystemOrder, tradingEngineOrder, exitType) {
/* Filter by Session Type */
switch (bot.SESSION.type) {
case 'Backtesting Session': {
break
}
case 'Live Trading Session': {
return
}
case 'Fordward Testing Session': {
return
}
case 'Paper Trading Session': {
break
}
}
/* Close this Order */
tradingEngineOrder.status.value = 'Closed'
tradingEngineOrder.exitType.value = exitType
/* Initialize this */
tradingEngine.current.distanceToEvent.closeOrder.value = 1
/* 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)
}
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
}
}
}
/* 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. */
let result = await exchangeAPIModule.cancelOrder(tradingSystemOrder, tradingEngineOrder)
if (result === 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 = await exchangeAPIModule.getOrder(tradingSystemOrder, tradingEngineOrder)
if (order === undefined) { return }
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 = 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
}
}
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,7 +16,6 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
updateStopLoss: updateStopLoss,
updateTakeProfit: updateTakeProfit,
updateSizeAndRate: updateSizeAndRate,
applySlippageToRate: applySlippageToRate,
updateEnds: updateEnds,
updateStatus: updateStatus,
updateCounters: updateCounters,
@@ -44,6 +43,7 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
function openPosition(situationName) {
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
@@ -94,16 +94,8 @@ exports.newTradingPosition = function newTradingPosition(bot, logger, tradingEng
function updateSizeAndRate(size, rate) {
tradingEngine.current.position.size.value = global.PRECISE(size, 10)
tradingEngine.current.position.rate.value = global.PRECISE(rate, 10)
}
function applySlippageToRate() {
let slippageAmount = tradingEngine.current.position.rate.value * bot.SESSION.parameters.slippage.config.positionRate / 100
if (bot.sessionAndMarketBaseAssetsAreEqual) {
tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value - slippageAmount
} else {
tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value + slippageAmount
}
tradingEngine.current.position.beginRate.value = tradingEngine.current.position.rate.value
}
function preventStopLossDistortion() {
@@ -119,7 +119,14 @@ exports.newTradingRecords = function newTradingRecords(bot, logger) {
the Root Node specifically for this property only.
*/
if (recordProperty.config.nodePath !== undefined) {
propertyRootNode = eval(recordProperty.config.nodePath)
try {
propertyRootNode = eval(recordProperty.config.nodePath)
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] appendRecords -> scanRecordDefinition -> Error at Record Property Definition -> recordProperty.name = " + recordProperty.name);
logger.write(MODULE_NAME, "[ERROR] appendRecords -> scanRecordDefinition -> Error at Record Property Definition -> recordProperty.config.nodePath = " + recordProperty.config.nodePath);
logger.write(MODULE_NAME, "[ERROR] appendRecords -> scanRecordDefinition -> Error at Record Property Definition -> err.stack = " + err.stack);
throw 'Can not continue with a Definition Error like this.'
}
}
/*
The Target Node is the node from where we are going to exctract the value.
@@ -42,10 +42,6 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
const MAX_TAKE_PROFIT_VALUE = Number.MAX_SAFE_INTEGER
/* These are the Modules we will need to run the Simulation */
const EXCHANGE_API = require('../ExchangeAPI');
exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, bot);
exchangeAPI.initialize();
const TRADING_ENGINE_MODULE = require('./TradingEngine.js')
let tradingEngineModule = TRADING_ENGINE_MODULE.newTradingEngine(bot, logger)
tradingEngineModule.initialize()
@@ -112,7 +108,7 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
loop()
}
function loop() {
async function loop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, '[INFO] runSimulation -> loop -> Processing candle # ' + tradingEngine.current.candle.index.value) }
let candle = candles[tradingEngine.current.candle.index.value] // This is the current candle the Simulation is working at.
@@ -150,13 +146,13 @@ exports.newTradingSimulation = function newTradingSimulation(bot, logger, UTILIT
tradingSystemModule.triggerStage()
/* Run the Open Stage */
tradingSystemModule.openStage()
await tradingSystemModule.openStage()
/* Run the Manage Stage */
tradingSystemModule.manageStage()
/* Run the Close Stage */
tradingSystemModule.closeStage()
await tradingSystemModule.closeStage()
/* Add new records to the process output */
tradingRecordsModule.appendRecords()
@@ -30,6 +30,7 @@ exports.newTradingStrategy = function newTradingStrategy(bot, logger, tradingEng
function openStrategy(index, situationName, strategyName) {
tradingEngine.current.strategy.status.value = 'Open'
tradingEngine.current.strategy.serialNumber.value = tradingEngine.episode.episodeCounters.strategies.value
tradingEngine.current.strategy.identifier.value = global.UNIQUE_ID()
tradingEngine.current.strategy.begin.value = tradingEngine.current.candle.begin.value
tradingEngine.current.strategy.end.value = tradingEngine.current.candle.end.value
tradingEngine.current.strategy.beginRate.value = tradingEngine.current.candle.min.value
+56 -359
View File
@@ -18,29 +18,25 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
let chart
let tradingSystem
let tradingEngine
let sessionParameters
let conditions = new Map()
let formulas = new Map()
const TRADING_STRATEGY_MODULE = require('./TradingStrategy.js')
let tradingStrategyModule = TRADING_STRATEGY_MODULE.newTradingStrategy(bot, logger, tradingEngineModule)
const TRADING_POSITION_MODULE = require('./TradingPosition.js')
let tradingPositionModule = TRADING_POSITION_MODULE.newTradingPosition(bot, logger, tradingEngineModule)
const TRADING_ENGINE_EXECUTION = require('./TradingExecution.js')
let tradingExecutionModule = TRADING_ENGINE_EXECUTION.newTradingExecution(bot, logger, tradingEngineModule)
const ANNOUNCEMENTS_MODULE = require('./Announcements.js')
let announcementsModule = ANNOUNCEMENTS_MODULE.newAnnouncements(bot, logger)
const SNAPSHOTS_MODULE = require('./Snapshots.js')
let snapshotsModule = SNAPSHOTS_MODULE.newSnapshots(bot, logger)
const TRADING_ENGINE_EXECUTION = require('./TradingExecution.js')
let tradingExecutionModule = TRADING_ENGINE_EXECUTION.newTradingExecution(bot, logger)
return thisObject
function initialize() {
@@ -48,11 +44,35 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
tradingEngine = bot.simulationState.tradingEngine
sessionParameters = bot.SESSION.parameters
tradingSystem.conditions = new Map()
tradingSystem.formulas = new Map()
tradingStrategyModule.initialize()
tradingPositionModule.initialize()
announcementsModule.initialize()
snapshotsModule.initialize()
tradingExecutionModule.initialize()
/* Adding Functions used elsewhere to Trading System Definition */
tradingSystem.checkConditions = function (situation, passed) {
/* Given a Situation, we check if all children conditions are true or not */
for (let m = 0; m < situation.conditions.length; m++) {
let condition = situation.conditions[m]
let value = false
if (tradingSystem.conditions.get(condition.id) !== undefined) {
value = tradingSystem.conditions.get(condition.id)
}
if (value === true) {
tradingSystem.highlights.push(condition.id)
}
else {
passed = false
}
tradingSystem.values.push([condition.id, value])
}
return passed
}
}
function finalize() {
@@ -73,8 +93,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
@@ -263,7 +283,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)
@@ -304,7 +324,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])
@@ -354,7 +374,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
passed = true
}
passed = checkConditions(situation, passed)
passed = tradingSystem.checkConditions(situation, passed)
tradingSystem.values.push([situation.id, passed])
if (passed) {
@@ -407,7 +427,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
passed = true
}
passed = checkConditions(situation, passed)
passed = tradingSystem.checkConditions(situation, passed)
tradingSystem.values.push([situation.id, passed])
if (passed) {
@@ -445,7 +465,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
passed = true
}
passed = checkConditions(situation, passed)
passed = tradingSystem.checkConditions(situation, passed)
tradingSystem.values.push([situation.id, passed])
if (passed) {
@@ -483,7 +503,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
}
function openStage() {
async function openStage() {
/* Abort Open Stage Check */
if (tradingEngine.current.strategy.openStageStatus.value === 'Open' && tradingEngine.current.strategy.closeStageStatus.value === 'Open') {
/*
@@ -496,13 +516,14 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
/* Opening Status Procedure */
if (tradingEngine.current.strategy.openStageStatus.value === 'Opening') {
/* Reset the Exchange Orders data structure to its initial value */
tradingEngine.exchangeOrders.initialize(tradingEngine.exchangeOrders)
/* This procedure is intended to run only once */
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
let executionNode = stageNode.openExecution
/* Reset the Exchange Orders data structure to its initial value */
tradingEngine.exchangeOrders.initialize(tradingEngine.exchangeOrders)
evalConditions(stageNode, 'Initial Definition')
evalFormulas(stageNode, 'Initial Definition')
@@ -511,13 +532,15 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
calculateTakeProfit()
calculateStopLoss()
takePosition()
/* 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 mode */
/* Check Execution in opening stage node */
evalConditions(stageNode, 'Open Execution')
evalFormulas(stageNode, 'Open Execution')
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.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
/* From here on, the state is officially Open */
tradingStrategyModule.updateStageStatus('Open Stage', 'Open')
@@ -537,7 +560,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)
await 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.
@@ -552,7 +575,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
/* Closing Status Procedure */
if (tradingEngine.current.strategy.openStageStatus.value === 'Closing') {
/*
During the closing status, we do not place new orders, just check
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.
*/
let stageNode = tradingSystem.tradingStrategies[tradingEngine.current.strategy.index.value].openStage
@@ -565,7 +588,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)
await tradingExecutionModule.checkExecution(executionNode, false, true, tradingEngine.current.position.size, tradingEngine.current.position.openStageOrdersSize, tradingEngine.current.position.openStageFilledSize)
}
/*
@@ -584,9 +607,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
/* Position size and rate */
tradingPositionModule.updateSizeAndRate(getPositionSize(), getPositionRate())
/* We take what was calculated at the formula of position rate and apply the slippage. */
tradingPositionModule.applySlippageToRate()
function getPositionSize() {
let balance
if (bot.sessionAndMarketBaseAssetsAreEqual) {
@@ -602,7 +622,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
}
@@ -616,30 +636,11 @@ 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
}
}
function takePosition() {
tradingEngine.previous.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value
tradingEngine.previous.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value
let feePaid = 0
if (bot.sessionAndMarketBaseAssetsAreEqual) {
feePaid = tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngine.current.position.size.value * tradingEngine.current.position.rate.value - feePaid
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value - tradingEngine.current.position.size.value
} else {
feePaid = tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngine.current.position.size.value / tradingEngine.current.position.rate.value - feePaid
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value - tradingEngine.current.position.size.value
}
}
}
function manageStage() {
@@ -714,7 +715,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
passed = true
}
passed = checkConditions(situation, passed)
passed = tradingSystem.checkConditions(situation, passed)
tradingSystem.values.push([situation.id, passed])
if (passed) {
@@ -744,7 +745,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
passed = true
}
passed = checkConditions(situation, passed)
passed = tradingSystem.checkConditions(situation, passed)
tradingSystem.values.push([situation.id, passed])
if (passed) {
@@ -821,7 +822,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
passed = true
}
passed = checkConditions(situation, passed)
passed = tradingSystem.checkConditions(situation, passed)
tradingSystem.values.push([situation.id, passed])
if (passed) {
@@ -851,7 +852,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
passed = true
}
passed = checkConditions(situation, passed)
passed = tradingSystem.checkConditions(situation, passed)
tradingSystem.values.push([situation.id, passed])
if (passed) {
@@ -927,7 +928,7 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
}
}
function closeStage() {
async function closeStage() {
/* Opening Status Procedure */
if (tradingEngine.current.strategy.closeStageStatus.value === 'Opening') {
/*
@@ -951,7 +952,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)
await 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) {
@@ -976,18 +977,6 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
function exitPositionAndStrategy() {
/* New Balances Calculation */
let feePaid = 0
if (bot.sessionAndMarketBaseAssetsAreEqual) {
feePaid = tradingEngine.current.balance.quotedAsset.value / tradingEngine.current.position.endRate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
tradingEngine.current.balance.baseAsset.value = tradingEngine.current.balance.baseAsset.value + tradingEngine.current.balance.quotedAsset.value / tradingEngine.current.position.endRate.value - feePaid
tradingEngine.current.balance.quotedAsset.value = 0
} else {
feePaid = tradingEngine.current.balance.baseAsset.value * tradingEngine.current.position.endRate.value * bot.SESSION.parameters.feeStructure.config.taker / 100
tradingEngine.current.balance.quotedAsset.value = tradingEngine.current.balance.quotedAsset.value + tradingEngine.current.balance.baseAsset.value * tradingEngine.current.position.endRate.value - feePaid
tradingEngine.current.balance.baseAsset.value = 0
}
/* Needed for Episode Statistics */
tradingPositionModule.updateStatistics()
@@ -1064,7 +1053,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)
@@ -1096,7 +1085,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)
@@ -1105,278 +1094,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 }
/* 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 State 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. */
if (stageIsOpening === false) {
simulateExchangeEvents(tradingSystemOrder, tradingEngineOrder)
}
}
}
}
}
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
@@ -1388,25 +1105,5 @@ exports.newTradingSystem = function newTradingSystem(bot, logger, tradingEngineM
return false
}
}
function checkConditions(situation, passed) {
/* Given a Situation, we check if all children conditions are true or not */
for (let m = 0; m < situation.conditions.length; m++) {
let condition = situation.conditions[m]
let value = false
if (conditions.get(condition.id) !== undefined) {
value = conditions.get(condition.id)
}
if (value === true) {
tradingSystem.highlights.push(condition.id)
}
else {
passed = false
}
tradingSystem.values.push([condition.id, value])
}
return passed
}
}
+5 -9
View File
@@ -225,18 +225,14 @@
/* Bot Type */
botConfig.type = global.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.type
/* Setting the Key to use */
process.env.KEY = undefined
process.env.SECRET = undefined
if (botConfig.processNode) {
if (botConfig.processNode.marketReference) {
if (botConfig.processNode.marketReference.keyInstance !== undefined) {
if (botConfig.processNode.marketReference.keyInstance.referenceParent !== undefined) {
let key = botConfig.processNode.marketReference.keyInstance.referenceParent
if (botConfig.processNode.marketReference.keyReference !== undefined) {
if (botConfig.processNode.marketReference.keyReference.referenceParent !== undefined) {
let key = botConfig.processNode.marketReference.keyReference.referenceParent
process.env.KEY = key.config.codeName
process.env.SECRET = key.config.secret
botConfig.KEY = key.config.codeName
botConfig.SECRET = key.config.secret
}
}
}
+30 -25
View File
@@ -161,41 +161,46 @@
}
}
}
if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined) { // if the initialDatetime is missing we create a default one.
bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date()).valueOf()
} else {
bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()
}
if (bot.SESSION.parameters.timeRange.config.finalDatetime === undefined) { // if the finalDatetime is missing we create a default one.
bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date()).valueOf()
} else {
bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()
}
/* Session Type Forced Values */
let today = new Date((new Date()).valueOf())
let aYearAgo = today - global.ONE_YEAR_IN_MILISECONDS
let aYearFromNow = today + global.ONE_YEAR_IN_MILISECONDS
switch (bot.SESSION.type) {
case 'Live Trading Session': {
if (bot.SESSION.parameters.timeRange.config.finalDatetime.valueOf() > (new Date()).valueOf()) {
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date()
}
case 'Backtesting Session': {
useDefaultDatetimes(aYearAgo, today)
break
}
case 'Live Trading Session': {
bot.SESSION.parameters.timeRange.config.initialDatetime = new Date()
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date((new Date()).valueOf() + global.ONE_YEAR_IN_MILISECONDS)
useDefaultDatetimes(today, aYearFromNow)
break
}
case 'Fordward Testing Session': {
bot.SESSION.parameters.timeRange.config.initialDatetime = new Date()
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date((new Date()).valueOf() + global.ONE_YEAR_IN_MILISECONDS)
useDefaultDatetimes(today, aYearFromNow)
break
}
case 'Paper Trading Session': {
bot.SESSION.parameters.timeRange.config.initialDatetime = new Date()
bot.SESSION.parameters.timeRange.config.finalDatetime = new Date((new Date()).valueOf() + global.ONE_YEAR_IN_MILISECONDS)
useDefaultDatetimes(today, aYearFromNow)
break
}
}
function useDefaultDatetimes(initialDefault, finalDefault) {
/* Initial Datetime */
if (bot.SESSION.parameters.timeRange.config.initialDatetime === undefined) {
bot.SESSION.parameters.timeRange.config.initialDatetime = initialDefault
} else {
bot.SESSION.parameters.timeRange.config.initialDatetime = (new Date(bot.SESSION.parameters.timeRange.config.initialDatetime)).valueOf()
}
/* Final Datetime */
if (bot.SESSION.parameters.timeRange.config.finalDatetime === undefined) {
bot.SESSION.parameters.timeRange.config.finalDatetime = finalDefault
} else {
bot.SESSION.parameters.timeRange.config.finalDatetime = (new Date(bot.SESSION.parameters.timeRange.config.finalDatetime)).valueOf()
}
}
/* Time Frame */
if (bot.SESSION.parameters.timeFrame === undefined) {
let errorMessage = "Session Parameters Node with no Time Frame."
@@ -300,18 +305,18 @@
}
function startLiveTrading(message) {
if (process.env.KEY === undefined || process.env.SECRET === undefined) { // TODO Remove this from Process .env
if (bot.KEY === undefined || bot.SECRET === undefined) {
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[ERROR] initialize -> startLiveTrading -> Key 'codeName' or 'secret' not provided. Plese check that and try again."); }
return false
}
bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf())
pProcessConfig.liveWaitTime = sessionParameters.timeFrame.config.value
pProcessConfig.liveWaitTime = bot.SESSION.parameters.timeFrame.config.value
return true
}
function startFordwardTesting(message) {
if (process.env.KEY === undefined || process.env.SECRET === undefined) {
if (bot.KEY === undefined || bot.SECRET === undefined) {
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startFordwardTesting -> Key name or Secret not provided, not possible to run the process in Forward Testing mode."); }
console.log("Key 'codeName' or 'secret' not provided. Plese check that and try again.")
return false
@@ -335,14 +340,14 @@
bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance = bot.SESSION.parameters.sessionBaseAsset.config.maximumBalance * balancePercentage / 100
bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance = bot.SESSION.parameters.sessionQuotedAsset.config.maximumBalance * balancePercentage / 100
pProcessConfig.normalWaitTime = sessionParameters.timeFrame.config.value
pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value
return true
}
function startPaperTrading(message) {
bot.tradingProcessDate = new Date(bot.SESSION.parameters.timeRange.config.initialDatetime.valueOf())
pProcessConfig.normalWaitTime = sessionParameters.timeFrame.config.value
pProcessConfig.normalWaitTime = bot.SESSION.parameters.timeFrame.config.value
return true
}
+2
View File
@@ -47,12 +47,14 @@ global.PRECISE = function (floatNumber, precision) {
process.on('uncaughtException', function (err) {
console.log('[ERROR] Task Server -> server -> uncaughtException -> err.message = ' + err.message)
console.log('[ERROR] Task Server -> server -> uncaughtException -> err.stack = ' + err.stack)
console.log(err.stack)
global.EXIT_NODE_PROCESS()
})
process.on('unhandledRejection', (reason, p) => {
console.log('[ERROR] Task Server -> server -> unhandledRejection -> reason = ' + JSON.stringify(reason))
console.log('[ERROR] Task Server -> server -> unhandledRejection -> p = ' + JSON.stringify(p))
console.log(reason.stack)
global.EXIT_NODE_PROCESS()
})