Implementation Finished
This commit is contained in:
1 parent
e84f8a6ed1
commit
6ec5580cd4
32 files changed
+1335
-6639
No files matched your search
@@ -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)
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user