/* Callbacks default responses. */ global.DEFAULT_OK_RESPONSE = { result: "Ok", message: "Operation Succeeded" }; global.DEFAULT_FAIL_RESPONSE = { result: "Fail", message: "Operation Failed" }; global.DEFAULT_RETRY_RESPONSE = { result: "Retry", message: "Retry Later" }; global.CUSTOM_OK_RESPONSE = { result: "Ok, but check Message", message: "Custom Message" }; global.CUSTOM_FAIL_RESPONSE = { result: "Fail Because", message: "Custom Message" }; /* Process Events */ process.on('uncaughtException', function (err) { console.log('[INFO] Task Server -> server -> uncaughtException -> err.message = ' + err.message) console.log('[INFO] Task Server -> server -> uncaughtException -> err.stack = ' + err.stack) process.exit(1) }) process.on('unhandledRejection', (reason, p) => { console.log('[INFO] Task Server -> server -> unhandledRejection -> reason = ' + JSON.stringify(reason)) console.log('[INFO] Task Server -> server -> unhandledRejection -> p = ' + JSON.stringify(p)) process.exit(1) }) process.on('exit', function (code) { console.log('[INFO] Task Server -> server -> process.on.exit -> About to exit -> code = ' + code) }) /* Local Variables */ let sequenceList = [] let heartBeatInterval /* We read the first string sent as an argument when the process was created by the Task Manager. Ther we will find the information of the identity of this Task and know exactly what to run within this server instance. */ global.USER_DEFINITION = process.argv[2] if (global.USER_DEFINITION !== undefined) { console.log('[INFO] Task Server -> server -> global.USER_DEFINITION = ' + global.USER_DEFINITION) try { global.USER_DEFINITION = JSON.parse(global.USER_DEFINITION) } catch (err) { console.log('[ERROR] Task Server -> server -> global.USER_DEFINITION -> ' + err.stack) } } else { // I use this section to debug in standalone mode. let argument = '{"type":"Task","name":"Extract Trades from Poloniex","bot":{"type":"Sensor","name":"Charly","processes":[{"type":"Process","name":"Live-Trades","code":{"devTeam":"AAMasters","bot":"AACharly","mode":"noTime","resumeExecution":true,"type":"Sensor","exchangeName":"Poloniex","process":"Live-Trades"}},{"type":"Process","name":"Hole-Fixing","code":{"devTeam":"AAMasters","bot":"AACharly","mode":"oneMonth","resumeExecution":true,"type":"Sensor","exchangeName":"Poloniex","process":"Hole-Fixing","startYear":2019,"endYear":2019,"month":9}}]}}' try { global.USER_DEFINITION = JSON.parse(argument) } catch (err) { console.log(err.stack) } } const EVENT_HANDLER_MODULE = require('../Libraries/SystemEventsClient/SystemEventHandler.js'); global.SYSTEM_EVENT_HANDLER = EVENT_HANDLER_MODULE.newSystemEventHandler() global.SYSTEM_EVENT_HANDLER.initialize('Clone Executor', bootLoader) function bootLoader() { global.SYSTEM_EVENT_HANDLER.createEventHandler(global.USER_DEFINITION.name) /* tryToListenToCockpit() function tryToListenToCockpit() { global.SYSTEM_EVENT_HANDLER.listenToEvent('Cockpit-Restart-Simulation', 'Simulation Started', undefined, 'Clone Executor Boot Loader', onResponse, startSequence) function onResponse(message) { if (message.result !== global.DEFAULT_OK_RESPONSE.result) { setTimeout(tryToListenToCockpit, 3000) } } } */ for (let i = 0; i < global.USER_DEFINITION.bot.processes.length; i++) { let code = global.USER_DEFINITION.bot.processes[i].code sequenceList.push(code) } /* Heartbeat sent to the UI */ heartBeatInterval = setInterval(hearBeat, 1000) function hearBeat() { let key = global.USER_DEFINITION.name + '-' + global.USER_DEFINITION.type + '-' + global.USER_DEFINITION.id let event = { seconds: (new Date()).getSeconds() } global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Heartbeat', event) } startSequence() } /* Old Run.js code follows... */ require('dotenv').config(); global.DEFINITION = require(process.env.INTER_PROCESS_FILES_PATH + '/Definition'); const definition = global.DEFINITION const path = require('path'); global.SHALL_BOT_STOP = false global.FULL_LOG = process.env.FULL_LOG /* Default parameters can be changed by the execution configuration */ global.EXCHANGE_NAME = process.env.EXCHANGE_NAME global.MARKET = { assetA: 'USDT', assetB: 'BTC' } global.CLONE_EXECUTOR = { codeName: 'AACloud', version: '1.1' } let isRunSequence = false; global.sequenceStep = 0; let processedSteps = new Map(); let notFirstSequence = false; let runClonExecutor = true; if (process.env.RUN_SEQUENCE !== undefined) { isRunSequence = JSON.parse(process.env.RUN_SEQUENCE) } function startSequence () { if (isRunSequence) { process.on('message', message => { if (message === 'STOP') { runClonExecutor = false; /* Cleaning Before Exiting. */ clearInterval(heartBeatInterval) global.SYSTEM_EVENT_HANDLER.finalize() global.SYSTEM_EVENT_HANDLER = undefined console.log("[INFO] CloneExecutor -> Clone Executor Stopped."); } }); sequenceExecution(sequenceStep) } else { readExecutionConfiguration() } } async function sequenceExecution(currentStep) { let execution = sequenceList[currentStep]; process.env.STOP_GRACEFULLY = true; execution.devTeam ? process.env.DEV_TEAM = execution.devTeam : undefined; execution.bot ? process.env.BOT = execution.bot : undefined; execution.mode ? process.env.START_MODE = execution.mode : undefined; execution.resumeExecution = true; execution.type ? process.env.TYPE = execution.type : undefined; execution.process ? process.env.PROCESS = execution.process : undefined; execution.startYear ? process.env.MIN_YEAR = execution.startYear : undefined; execution.endYear ? process.env.MAX_YEAR = execution.endYear : undefined; execution.month ? process.env.MONTH = execution.month : undefined; execution.beginDatetime ? process.env.BEGIN_DATE_TIME = execution.beginDatetime : undefined; execution.endDatetime ? process.env.END_DATE_TIME = execution.endDatetime : undefined; execution.dataSet ? process.env.DATA_SET = execution.dataSet : undefined; execution.timePeriod ? process.env.TIME_PERIOD = execution.timePeriod : undefined; execution.baseAsset ? process.env.BASE_ASSET = execution.baseAsset : undefined; execution.balanceAssetA ? process.env.INITIAL_BALANCE_ASSET_A = execution.balanceAssetA : undefined; execution.balanceAssetB ? process.env.INITIAL_BALANCE_ASSET_B = execution.balanceAssetB : undefined; execution.type === 'Trading' ? process.env.CLONE_ID = 1 : undefined; execution.exchangeName ? global.EXCHANGE_NAME = execution.exchangeName : undefined; if (definition) { if (definition.personalData) { if (definition.personalData.exchangeAccounts) { if (definition.personalData.exchangeAccounts.length > 0) { let exchangeAccount = definition.personalData.exchangeAccounts[0] if (exchangeAccount.keys) { if (exchangeAccount.keys.length > 0) { let key = exchangeAccount.keys[0] process.env.KEY = key.name process.env.SECRET = key.code } } } } } } let stepKey = execution.devTeam + '.' + execution.bot + '.' + execution.process; if (processedSteps.has(stepKey)) { processedSteps.set(stepKey, processedSteps.get(stepKey) + 1); } else { processedSteps.set(stepKey, 0); } readExecutionConfiguration(execution); sequenceStep++; } function onExecutionFinish(result, finishStepKey) { if (global.sequenceStep < sequenceList.length && runClonExecutor) { sequenceExecution(sequenceStep); } else { setTimeout(function () { if (runClonExecutor) { console.log("[INFO] onExecutionFinish -> New round for sequence execution started."); global.sequenceStep = 0; processedSteps = new Map(); notFirstSequence = true; sequenceExecution(sequenceStep); } }, process.env.EXECUTION_LOOP_DELAY); } } async function readExecutionConfiguration(execution) { try { console.log("[INFO] Task Server -> server -> readExecutionConfiguration -> Entering function. "); let timePeriodFilter let botProcess /* Dates are taken initially from .env, but can be overwritten if they are defined by the user */ let initialDatetime = process.env.BEGIN_DATE_TIME let finalDatetime = process.env.END_DATE_TIME if (execution.type === 'Trading-Engine') { /* The Trading Engine only resumes its execution after the first sequence was completed. */ if (notFirstSequence === false) { execution.resumeExecution = false } /* We set the START MODE of the Trading Engine */ if (process.env.KEY === undefined || process.env.SECRET === undefined) { process.env.START_MODE = "backtest" // if we dont have keys, we swtich to backtest mode to avoid exchange errors. } else { process.env.START_MODE = "live" // If we have keys, then we are in live mode. } if (definition !== undefined) { if (definition.simulationParams !== undefined) { if (definition.simulationParams.beginDatetime !== undefined) { initialDatetime = new Date(definition.simulationParams.beginDatetime) /* The first override occurs here, with the simulation parameters */ } /* Here we only look for one timePeriod, in the future we will be able to process the whole array, but not for now. */ if (definition.simulationParams.timePeriodDailyArray !== undefined) { if (definition.simulationParams.timePeriodDailyArray.length === 1) { timePeriodFilter = definition.simulationParams.timePeriodDailyArray[0] botProcess = "Multi-Period" } } if (definition.simulationParams.timePeriodMarketArray !== undefined) { if (definition.simulationParams.timePeriodMarketArray.length === 1) { timePeriodFilter = definition.simulationParams.timePeriodMarketArray[0] botProcess = "Multi-Period" } } } if (definition.tradingSystem !== undefined) { if (definition.tradingSystem.parameters !== undefined) { if (definition.tradingSystem.parameters.timeRange !== undefined) { if (definition.tradingSystem.parameters.timeRange.code !== undefined) { try { let code = JSON.parse(definition.tradingSystem.parameters.timeRange.code) if (code.initialDatetime !== undefined) { initialDatetime = code.initialDatetime /* The second override occurs here, with the date explicitelly defined by the user */ } if (code.finalDatetime !== undefined) { finalDatetime = code.finalDatetime } } catch (err) { definition.tradingSystem.parameters.timeRange.error = err.message } } } } } /* Get the initial balance from the definition */ let tradingSystem = definition.tradingSystem if (tradingSystem) { if (tradingSystem.parameters !== undefined) { if (tradingSystem.parameters.baseAsset !== undefined) { let code try { code = JSON.parse(tradingSystem.parameters.baseAsset.code); if (code.name !== undefined) { baseAsset = code.name; if (baseAsset !== 'BTC' && baseAsset !== 'USDT') { /* using BTC as default */ baseAsset = 'BTC' } } if (baseAsset === 'BTC') { // NOTE: POLONIEX, the only exchange working so far, has Asset A and B inverted. We need to fix this. if (code.initialBalance !== undefined) { process.env.INITIAL_BALANCE_ASSET_B = code.initialBalance; process.env.INITIAL_BALANCE_ASSET_A = 0 } } else { if (code.initialBalance !== undefined) { process.env.INITIAL_BALANCE_ASSET_A = code.initialBalance; process.env.INITIAL_BALANCE_ASSET_B = 0 } } } catch (err) { definition.tradingSystem.parameters.baseAsset.error = err.message process.env.INITIAL_BALANCE_ASSET_A = 0 // default process.env.INITIAL_BALANCE_ASSET_B = 0.001 // default } } } } } } let startMode // General Financial Being Configuration global.DEV_TEAM = process.env.DEV_TEAM global.CURRENT_BOT_REPO = process.env.BOT + "-" + process.env.TYPE + "-Bot" if (process.env.TYPE === 'Trading' || process.env.TYPE === 'Trading-Engine') { let live = { run: 'false', resumeExecution: execution.resumeExecution, beginDatetime: new Date(definition.simulationParams.timestamp).toISOString(), endDatetime: finalDatetime } let backtest = { run: 'false', resumeExecution: execution.resumeExecution, beginDatetime: initialDatetime, endDatetime: finalDatetime } let competition = { run: 'false', resumeExecution: execution.resumeExecution, beginDatetime: initialDatetime, endDatetime: finalDatetime } startMode = { live: live, backtest: backtest, competition: competition } } else if (process.env.TYPE === 'Indicator' || process.env.TYPE === 'Sensor') { let allMonths = { run: "false", minYear: process.env.MIN_YEAR, maxYear: process.env.MAX_YEAR } let oneMonth = { run: "false", year: process.env.MIN_YEAR, month: process.env.MONTH } let noTime = { run: "false", beginDatetime: process.env.BEGIN_DATE_TIME, resumeExecution: execution.resumeExecution } let fixedInterval = { run: "false", interval: process.env.INTERVAL } startMode = { allMonths: allMonths, oneMonth: oneMonth, noTime: noTime, fixedInterval: fixedInterval } } else { console.log("[ERROR] readExecutionConfiguration -> Bot Type is invalid."); throw new Error("readExecutionConfiguration -> Bot Type is invalid.") } startMode[process.env.START_MODE].run = "true" if (botProcess === undefined) { botProcess = process.env.PROCESS } // Only use the .env when nothing comes at Definition.json let cloneToExecute = { enabled: "true", devTeam: process.env.DEV_TEAM, bot: process.env.BOT, process: botProcess, repo: global.CURRENT_BOT_REPO } let timePeriod if (timePeriodFilter === undefined) { timePeriod = process.env.TIME_PERIOD } else { timePeriod = timePeriodFilter } global.EXECUTION_CONFIG = { cloneToExecute: cloneToExecute, startMode: startMode, timePeriod: getTimePeriod(timePeriod), timePeriodFileStorage: timePeriod, timePeriodFilter: timePeriodFilter, dataSet: process.env.DATA_SET }; timePeriodFilter = undefined timePeriod = undefined global.CLONE_EXECUTOR = { codeName: 'AACloud', version: '1.1' } startRoot(); } catch (err) { console.log("[ERROR] readExecutionConfiguration -> err = " + err.stack); console.log("[ERROR] readExecutionConfiguration -> Please verify that the Start Mode for the type of Bot configured applies to that type."); } } function getTimePeriod(timePeriod) { if (timePeriod !== undefined) { try { let timePeriodMap = new Map() timePeriodMap.set('24-hs', 86400000) timePeriodMap.set('12-hs', 43200000) timePeriodMap.set('08-hs', 28800000) timePeriodMap.set('06-hs', 21600000) timePeriodMap.set('04-hs', 14400000) timePeriodMap.set('03-hs', 10800000) timePeriodMap.set('02-hs', 7200000) timePeriodMap.set('01-hs', 3600000) timePeriodMap.set('45-min', 2700000) timePeriodMap.set('40-min', 2400000) timePeriodMap.set('30-min', 1800000) timePeriodMap.set('20-min', 1200000) timePeriodMap.set('15-min', 900000) timePeriodMap.set('10-min', 600000) timePeriodMap.set('05-min', 300000) timePeriodMap.set('04-min', 240000) timePeriodMap.set('03-min', 180000) timePeriodMap.set('02-min', 120000) timePeriodMap.set('01-min', 60000) return timePeriodMap.get(timePeriod) } catch (error) { console.log('[WARN] Task Server -> server -> readExecutionConfiguration -> getTimePeriod -> Error: ', error) } } else { return undefined } } function startRoot() { console.log('[INFO] Task Server -> server -> startRoot -> Entering function. ') const ROOT_DIR = './' const ROOT_MODULE = require(ROOT_DIR + 'Root') let root = ROOT_MODULE.newRoot() let UI_COMMANDS = { beginDatetime: undefined, endDatetime: undefined, timePeriod: undefined, startMode: undefined, eventHandler: undefined } root.initialize(UI_COMMANDS, onInitialized) function onInitialized() { console.log('[INFO] Task Server -> server -> startRoot -> onInitialized -> Entering function. ') root.start(onExecutionFinish) } }