437 lines
18 KiB
JavaScript
437 lines
18 KiB
JavaScript
/* 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":"Brings Trades Records from the Exchange","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"},"id":"3aae75f5-67ae-4ae5-9586-ec5640f79b18"}]},"id":"54444b07-eee2-4fa8-9183-8fea3b5ff9de"}'
|
|
try {
|
|
global.USER_DEFINITION = JSON.parse(argument)
|
|
} catch (err) {
|
|
console.log(err.stack)
|
|
}
|
|
}
|
|
|
|
|
|
require('dotenv').config();
|
|
|
|
global.DEFINITION = require(process.env.INTER_PROCESS_FILES_PATH + '/Definition');
|
|
global.WRITE_LOGS_TO_FILES = process.env.WRITE_LOGS_TO_FILES
|
|
|
|
/* 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' }
|
|
|
|
/*
|
|
We need to count how many process instances we deployd and how many of them have already finished their job, either
|
|
because they just finished or because there was a request to stop the proceses. In this way, once we reach the
|
|
amount of instances started, we can safelly destroy the rest of the objects running and let this nodejs process die.
|
|
*/
|
|
|
|
global.ENDED_PROCESSES_COUNTER = 0
|
|
global.TOTAL_PROCESS_INSTANCES_CREATED = 0
|
|
|
|
/* Here we listen for the message to stop this Task / Process comming from the Task Manager, which is the paret of this node js process. */
|
|
process.on('message', message => {
|
|
if (message === 'Stop this Task') {
|
|
|
|
process.env.STOP_GRACEFULLY = true;
|
|
|
|
/*
|
|
There are some process that might no be able to end grafully, for example the ones schedulle to process information in a future day or month.
|
|
In order to be sure that the process will be terminated, we schedulle one forced exit in 2 minutes from now.
|
|
*/
|
|
console.log('[INFO] Task Server -> server -> process.on -> Executing order received from Task Manager to Stop this Task. Nodejs process will be exited in less than 2 minutes.')
|
|
setTimeout(global.EXIT_NODE_PROCESS, 120000);
|
|
}
|
|
});
|
|
|
|
global.EXIT_NODE_PROCESS = function exitProcess() {
|
|
|
|
/* Cleaning Before Exiting. */
|
|
clearInterval(global.HEARTBEAT_INTERVAL_HANDLER)
|
|
|
|
for (let i = 0; i < global.USER_DEFINITION.bot.processes.length; i++) {
|
|
let code = global.USER_DEFINITION.bot.processes[i].code
|
|
|
|
/* Delete the event handler for each process. */
|
|
|
|
let key = code.devTeam + "-" + code.codeName + "-" + code.process
|
|
let event = {
|
|
reason: 'Signal Received to Terminate this Process.'
|
|
}
|
|
global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Process Terminated', event)
|
|
global.SYSTEM_EVENT_HANDLER.deleteEventHandler(key)
|
|
}
|
|
|
|
global.SYSTEM_EVENT_HANDLER.finalize()
|
|
global.SYSTEM_EVENT_HANDLER = undefined
|
|
console.log("[INFO] Task Server -> exitProcessInstance -> Task Server Stopped.");
|
|
|
|
process.exit()
|
|
}
|
|
|
|
let notFirstSequence = false;
|
|
|
|
/* Setting up the global Event Handler */
|
|
|
|
const EVENT_HANDLER_MODULE = require('../Libraries/SystemEventsClient/SystemEventHandler.js');
|
|
global.SYSTEM_EVENT_HANDLER = EVENT_HANDLER_MODULE.newSystemEventHandler()
|
|
global.SYSTEM_EVENT_HANDLER.initialize('Task Server', bootLoader)
|
|
|
|
|
|
function bootLoader() {
|
|
|
|
for (let i = 0; i < global.USER_DEFINITION.bot.processes.length; i++) {
|
|
let code = global.USER_DEFINITION.bot.processes[i].code
|
|
|
|
/* Add to the execution sequence list. */
|
|
sequenceList.push(code)
|
|
|
|
/* Create the event handler for each process. This event handlers are where the status reports updated events are raised. */
|
|
|
|
let key = code.devTeam + "-" + code.bot + "-" + code.process
|
|
global.SYSTEM_EVENT_HANDLER.createEventHandler(key)
|
|
}
|
|
|
|
/* Heartbeat sent to the UI */
|
|
|
|
let key = global.USER_DEFINITION.name + '-' + global.USER_DEFINITION.type + '-' + global.USER_DEFINITION.id
|
|
|
|
global.SYSTEM_EVENT_HANDLER.createEventHandler(key)
|
|
global.HEARTBEAT_INTERVAL_HANDLER = setInterval(hearBeat, 1000)
|
|
|
|
function hearBeat() {
|
|
|
|
/* The heartbeat event is raised at the event handler of the instance of this task, created at the UI. */
|
|
let event = {
|
|
seconds: (new Date()).getSeconds()
|
|
}
|
|
global.SYSTEM_EVENT_HANDLER.raiseEvent(key, 'Heartbeat', event)
|
|
}
|
|
|
|
startSequence()
|
|
}
|
|
|
|
/* Old Run.js code follows... */
|
|
|
|
function startSequence() {
|
|
|
|
for (let processIndex = 0; processIndex < global.USER_DEFINITION.bot.processes.length; processIndex++) {
|
|
let execution = sequenceList[processIndex];
|
|
|
|
process.env.STOP_GRACEFULLY = false;
|
|
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 (global.DEFINITION) {
|
|
if (global.DEFINITION.personalData) {
|
|
if (global.DEFINITION.personalData.exchangeAccounts) {
|
|
if (global.DEFINITION.personalData.exchangeAccounts.length > 0) {
|
|
let exchangeAccount = global.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
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
readExecutionConfiguration(execution, processIndex);
|
|
}
|
|
}
|
|
|
|
async function readExecutionConfiguration(execution, processIndex) {
|
|
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 (global.DEFINITION !== undefined) {
|
|
if (global.DEFINITION.simulationParams !== undefined) {
|
|
if (global.DEFINITION.simulationParams.beginDatetime !== undefined) {
|
|
initialDatetime = new Date(global.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 (global.DEFINITION.simulationParams.timePeriodDailyArray !== undefined) {
|
|
if (global.DEFINITION.simulationParams.timePeriodDailyArray.length === 1) {
|
|
timePeriodFilter = global.DEFINITION.simulationParams.timePeriodDailyArray[0]
|
|
botProcess = "Multi-Period"
|
|
}
|
|
}
|
|
if (global.DEFINITION.simulationParams.timePeriodMarketArray !== undefined) {
|
|
if (global.DEFINITION.simulationParams.timePeriodMarketArray.length === 1) {
|
|
timePeriodFilter = global.DEFINITION.simulationParams.timePeriodMarketArray[0]
|
|
botProcess = "Multi-Period"
|
|
}
|
|
}
|
|
}
|
|
if (global.DEFINITION.tradingSystem !== undefined) {
|
|
if (global.DEFINITION.tradingSystem.parameters !== undefined) {
|
|
if (global.DEFINITION.tradingSystem.parameters.timeRange !== undefined) {
|
|
if (global.DEFINITION.tradingSystem.parameters.timeRange.code !== undefined) {
|
|
try {
|
|
let code = JSON.parse(global.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) {
|
|
global.DEFINITION.tradingSystem.parameters.timeRange.error = err.message
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Get the initial balance from the global.DEFINITION */
|
|
let tradingSystem = global.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) {
|
|
global.DEFINITION.tradingSystem.parameters.baseAsset.error = err.message
|
|
|
|
process.env.INITIAL_BALANCE_ASSET_A = 0 // default
|
|
process.env.INITIAL_BALANCE_ASSET_B = 0.001 // default
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// General Financial Being Configuration
|
|
global.DEV_TEAM = process.env.DEV_TEAM
|
|
global.CURRENT_BOT_REPO = process.env.BOT + "-" + process.env.TYPE + "-Bot"
|
|
|
|
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,
|
|
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(processIndex);
|
|
}
|
|
|
|
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(processIndex) {
|
|
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(processIndex)
|
|
}
|
|
}
|
|
|