Files
Superalgos/server.js
T

518 lines
20 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":"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)
}
}