Merge pull request #198 from Superalgos/develop
Superalgos Beta Service Pack 1
This commit is contained in:
14 files changed
+366
-114
No files matched your search
@@ -2881,6 +2881,11 @@
|
||||
"actionFunction": "payload.onMenuItemClick"
|
||||
}
|
||||
],
|
||||
"isTitleAllwaysVisible": true,
|
||||
"title": [
|
||||
"Use Reference Parent Parent Parent",
|
||||
"Use Reference Parent Parent"
|
||||
],
|
||||
"addLeftIcons": true,
|
||||
"level": 3,
|
||||
"referenceAttachesTo": {
|
||||
|
||||
@@ -183,16 +183,20 @@
|
||||
function listenToEvent(eventHandlerName, eventType, callerId, handler, extraData) {
|
||||
let eventSubscriptionId = Math.trunc(Math.random() * 1000000)
|
||||
|
||||
let found = false
|
||||
/*
|
||||
We can not allow to have the exact same listener more than once. In case that we find a listener trying to listen again, the first record will
|
||||
be deleted and we are just going to consider the last one comming in.
|
||||
*/
|
||||
for (let i = 0; i < thisObject.listeners.length; i++) {
|
||||
if (eventHandlerName === thisObject.listeners[i][0] && eventType === thisObject.listeners[i][1] && callerId === thisObject.listeners[i][2]) {
|
||||
found = true
|
||||
thisObject.listeners.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (found === false) {
|
||||
thisObject.listeners.push([eventHandlerName, eventType, callerId, handler, extraData, eventSubscriptionId])
|
||||
}
|
||||
|
||||
thisObject.listeners.push([eventHandlerName, eventType, callerId, handler, extraData, eventSubscriptionId])
|
||||
|
||||
|
||||
return eventSubscriptionId
|
||||
}
|
||||
|
||||
@@ -66,7 +66,8 @@
|
||||
}
|
||||
|
||||
if (tasks.get(message.event.taskId) !== undefined) {
|
||||
//console.log('[WARN] BackendServers -> Task Manager Server -> runTask -> Task Already Running -> taskId = ' + message.event.taskId)
|
||||
let key = message.event.taskName + '-' + 'Task' + '-' + message.event.taskId
|
||||
eventsServerClient.raiseEvent(key, 'Running') // Meaning Task Running
|
||||
return
|
||||
}
|
||||
//console.log('[INFO] BackendServers -> Task Manager Server -> runTask -> Task Name = ' + message.event.taskName)
|
||||
|
||||
@@ -19,8 +19,14 @@ function newReferenceAttachDetach () {
|
||||
}
|
||||
|
||||
function completeDetachment (node) {
|
||||
if (node.payload.referenceParent.payload !== undefined && node.payload.referenceParent.payload.uiObject !== undefined) {
|
||||
node.payload.referenceParent.payload.uiObject.isShowing = false
|
||||
if (node.payload !== undefined !== undefined) {
|
||||
if (node.payload.referenceParent !== undefined) {
|
||||
if (node.payload.referenceParent.payload !== undefined) {
|
||||
if (node.payload.referenceParent.payload.uiObject !== undefined) {
|
||||
node.payload.referenceParent.payload.uiObject.isShowing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.payload.referenceParent = undefined
|
||||
|
||||
@@ -21,7 +21,7 @@ function newSessionFunctions () {
|
||||
}
|
||||
|
||||
if (node.payload.referenceParent === undefined) {
|
||||
node.payload.uiObject.setErrorMessage('Parent Process Instance need to reference a Process Definition.')
|
||||
node.payload.uiObject.setErrorMessage('Session needs to reference a Trading System.')
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -202,6 +202,8 @@ function newPlotter () {
|
||||
if (newMarketFile !== undefined) {
|
||||
marketFile = newMarketFile
|
||||
recalculate()
|
||||
} else {
|
||||
logger.write('[WARN] setTimeFrame -> Could not change to market file for timeFrame = ' + pTimeFrame)
|
||||
}
|
||||
} else {
|
||||
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame)
|
||||
@@ -209,6 +211,8 @@ function newPlotter () {
|
||||
if (newFileCursor !== undefined) {
|
||||
fileCursor = newFileCursor
|
||||
recalculate()
|
||||
} else {
|
||||
logger.write('[WARN] setTimeFrame -> Could not change to market file for timeFrame = ' + pTimeFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-3
@@ -73,9 +73,22 @@ function newDashboard () {
|
||||
}
|
||||
|
||||
function setBrowserEvents () {
|
||||
window.onbeforeunload = onBrowserClosed
|
||||
function onBrowserClosed () {
|
||||
// canvas.designSpace.workspace.stopAllRunningTasks()
|
||||
window.onbeforeunload = saveWorkspace
|
||||
|
||||
/* handles backspace and refresh(F5) from keyboard */
|
||||
window.manageBackRefresh = function (event) {
|
||||
var tag = event.target.tagName.toLowerCase()
|
||||
if (event.keyCode == 8 && tag != 'input' && tag != 'textarea') { // Backbutton pressed
|
||||
saveWorkspace()
|
||||
} else if (event.keyCode == 116) { // F5 pressed
|
||||
saveWorkspace()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', window.manageBackRefresh)
|
||||
|
||||
function saveWorkspace () {
|
||||
canvas.designSpace.workspace.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,6 +354,11 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
|
||||
return
|
||||
}
|
||||
|
||||
if (err.stack.toString().indexOf('ExchangeNotAvailable') >= 0) {
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + bot.exchange + " is not responding at the moment. I will save the data already fetched and try to reconnect later to fetch the rest of the missing data.");
|
||||
return
|
||||
}
|
||||
|
||||
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> err = " + err.stack);
|
||||
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
|
||||
abort = true
|
||||
|
||||
@@ -289,6 +289,35 @@
|
||||
let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas.
|
||||
let results = []
|
||||
|
||||
/*
|
||||
We are going to build a map structure so that users can easily get the current object of
|
||||
each data dependency based on the current element of the main data dependency.
|
||||
We will use as key for these maps the begin and end property, meaning only elements with exactly
|
||||
the same begin and end property will be matched.
|
||||
*/
|
||||
|
||||
const PROPERTIES_COUNT = Object.keys(products).length
|
||||
|
||||
let dataDependencies = {}
|
||||
if (PROPERTIES_COUNT > 1) { // only if we have secondary data dependencies.
|
||||
for (let i = 1; i < PROPERTIES_COUNT; i++) {
|
||||
let dataDependecyArray = Object.values(products)[i]
|
||||
let dataDependencyName = Object.keys(products)[i]
|
||||
let dataDependencyMap = new Map()
|
||||
for (let j = 0; j < dataDependecyArray.length; j++) {
|
||||
let record = dataDependecyArray[j]
|
||||
let key = record.begin.toString() + '-' + record.end.toString()
|
||||
dataDependencyMap.set(key, record)
|
||||
}
|
||||
dataDependencies[dataDependencyName] = dataDependencyMap
|
||||
}
|
||||
}
|
||||
|
||||
function getElement(dependencyName, currentRecordPrimaryDataDependency) {
|
||||
let key = currentRecordPrimaryDataDependency.begin.toString() + '-' + currentRecordPrimaryDataDependency.end.toString()
|
||||
return dataDependencies[dependencyName].get(key)
|
||||
}
|
||||
|
||||
/* This is Initialization Code */
|
||||
if (dataBuildingProcedure.initialization !== undefined) {
|
||||
if (dataBuildingProcedure.initialization.javascriptCode !== undefined) {
|
||||
|
||||
@@ -61,7 +61,7 @@ exports.newDebugLog = function newDebugLog() {
|
||||
|
||||
function persist() {
|
||||
|
||||
/* Here we actually write the content of the in-memory log to a blob */
|
||||
/* Here we actually write the content of the in-memory log to a file */
|
||||
|
||||
try {
|
||||
|
||||
@@ -96,7 +96,9 @@ exports.newDebugLog = function newDebugLog() {
|
||||
writeLog();
|
||||
|
||||
/* This is the implementation of the mechanism to auto-mantain logs. */
|
||||
thisObject.bot.LOGS_TO_DELETE_QUEUE.push(filePath + '/' + fileName)
|
||||
if (contentToPersist.indexOf('[ERROR]') < 0) {
|
||||
thisObject.bot.LOGS_TO_DELETE_QUEUE.push(filePath + '/' + fileName)
|
||||
}
|
||||
if (thisObject.bot.LOGS_TO_DELETE_QUEUE.length > thisObject.bot.DELETE_QUEUE_SIZE) {
|
||||
let fileToDelete = thisObject.bot.LOGS_TO_DELETE_QUEUE[0]
|
||||
thisObject.bot.LOGS_TO_DELETE_QUEUE.splice(0, 1)
|
||||
|
||||
+138
-34
@@ -24,13 +24,13 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
|
||||
return thisObject
|
||||
|
||||
function getTextFile(filePath, callBackFunction, noRetry) {
|
||||
function getTextFile(filePath, callBackFunction, noRetry, canUsePrevious) {
|
||||
|
||||
let currentRetryGetTextFile = 0
|
||||
|
||||
recursiveGetTextFile(filePath, callBackFunction, noRetry)
|
||||
recursiveGetTextFile(filePath, callBackFunction, noRetry, canUsePrevious)
|
||||
|
||||
function recursiveGetTextFile(filePath, callBackFunction, noRetry) {
|
||||
function recursiveGetTextFile(filePath, callBackFunction, noRetry, canUsePrevious) {
|
||||
|
||||
logger.write(MODULE_NAME, '[INFO] FileStorage -> getTextFile -> Entering Function.')
|
||||
|
||||
@@ -38,10 +38,13 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
|
||||
/* Choose path for either bots or data */
|
||||
let fileLocation
|
||||
let mustBeJason
|
||||
if (filePath.indexOf("/bots/") > 0) {
|
||||
fileLocation = process.env.BOTS_PATH + '/' + filePath
|
||||
mustBeJason = false
|
||||
} else {
|
||||
fileLocation = process.env.STORAGE_PATH + '/' + filePath
|
||||
mustBeJason = true
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -68,12 +71,12 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
}
|
||||
callBackFunction(customResponse)
|
||||
return
|
||||
} else {
|
||||
fileDoesNotExist = true
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Retrying. ')
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fileDoesNotExist = true
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> File does not exist. Retrying. ')
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
return
|
||||
}
|
||||
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Error reading file -> err = ' + err.stack)
|
||||
@@ -83,13 +86,60 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
return
|
||||
}
|
||||
|
||||
if (text.toString() === "") {
|
||||
/*
|
||||
It might happen that we try to read a file just at the moment it is being written by another process. It that case we might get
|
||||
an empty file. If we are not allowed to use the Previous version of the file in this case, then we will retry to read it again
|
||||
a little bit later.
|
||||
*/
|
||||
if (text.toString() === "" && canUsePrevious !== true) {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Read and Empty File -> filePath = ' + filePath)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
return
|
||||
}
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, text.toString())
|
||||
if (mustBeJason === false) {
|
||||
/* In this case there is nothing else to check. */
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, text.toString())
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
We are going to check if the file is a valir JSON object
|
||||
*/
|
||||
try {
|
||||
let jsonCheck = JSON.parse(text.toString())
|
||||
|
||||
/* The file was correctly read and could be parsed. */
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, text.toString())
|
||||
return
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (canUsePrevious === true) {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Could read the file, but could not parse it as it is not a valid JSON. Will try to read the PREVIOUS version instead. -> file = ' + fileLocation)
|
||||
|
||||
fs.readFile(fileLocation + '.Previous.json', onPreviousFileRead)
|
||||
|
||||
function onPreviousFileRead(err, text) {
|
||||
|
||||
if (err) {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> canUsePrevious -> Could not read the Previous file either. Giving up.')
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> canUsePrevious -> err = ' + err.stack)
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
/* Returning the previous file */
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, text.toString())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> Could read the file, but could not parse it as it is not a valid JSON.')
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> onFileRead -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
@@ -103,7 +153,7 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
if (currentRetryGetTextFile < MAX_RETRY) {
|
||||
currentRetryGetTextFile++
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> getTextFile -> retry -> Will try to read the file again -> Retry #: ' + currentRetryGetTextFile)
|
||||
recursiveGetTextFile(filePath, callBackFunction)
|
||||
recursiveGetTextFile(filePath, callBackFunction, noRetry, canUsePrevious)
|
||||
} else {
|
||||
currentRetryGetTextFile = 0
|
||||
|
||||
@@ -116,6 +166,7 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
callBackFunction(customResponse)
|
||||
return
|
||||
} else {
|
||||
|
||||
logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> Max retries reached reading a file. Giving up.')
|
||||
logger.write(MODULE_NAME, '[ERROR] FileStorage -> getTextFile -> retry -> file = ' + fileLocation)
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
@@ -126,13 +177,13 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
}
|
||||
}
|
||||
|
||||
function createTextFile(filePath, fileContent, callBackFunction) {
|
||||
function createTextFile(filePath, fileContent, callBackFunction, keepPrevious) {
|
||||
|
||||
let currentRetryWriteTextFile = 0
|
||||
|
||||
recursiveCreateTextFile(filePath, fileContent, callBackFunction)
|
||||
recursiveCreateTextFile(filePath, fileContent, callBackFunction, keepPrevious)
|
||||
|
||||
function recursiveCreateTextFile(filePath, fileContent, callBackFunction) {
|
||||
function recursiveCreateTextFile(filePath, fileContent, callBackFunction, keepPrevious) {
|
||||
|
||||
logger.write(MODULE_NAME, '[INFO] FileStorage -> createTextFile -> Entering Function.')
|
||||
|
||||
@@ -165,32 +216,85 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
retryTimeToUse = SLOW_RETRY_TIME_IN_MILISECONDS
|
||||
}
|
||||
if (err) {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> file = ' + fileLocation)
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> file = ' + fileLocation + '.tmp')
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> Error writing file -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
} else {
|
||||
|
||||
const fs = require('fs')
|
||||
fs.unlink(fileLocation, onUnlinked)
|
||||
if (keepPrevious === true) {
|
||||
/*
|
||||
In some cases, we are going to keep a copy of the previous version of the file being written. This will be usefull to recover from crashes
|
||||
when the file written gets corrupted for any reason.
|
||||
*/
|
||||
fs.unlink(fileLocation + '.Previous.json', onUnlinked)
|
||||
|
||||
function onUnlinked(err) {
|
||||
let code = ''
|
||||
if (err) {
|
||||
code = err.code
|
||||
}
|
||||
if (code !== '' && code !== 'ENOENT') {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> file = ' + fileLocation)
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error renaming file -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
} else {
|
||||
function onUnlinked(err) {
|
||||
let code = ''
|
||||
if (err) {
|
||||
code = err.code
|
||||
}
|
||||
if (code !== '' && code !== 'ENOENT') {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error deleting file -> file = ' + fileLocation + '.Previous.json')
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error deleting file -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
return
|
||||
}
|
||||
|
||||
const fs = require('fs')
|
||||
fs.rename(fileLocation + '.tmp', fileLocation, onRenamed)
|
||||
/* Rename de Original into Previous */
|
||||
fs.rename(fileLocation, fileLocation + '.Previous.json', onOriginalRenamed)
|
||||
|
||||
function onRenamed(err) {
|
||||
function onOriginalRenamed(err) {
|
||||
let code = ''
|
||||
if (err) {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> file = ' + fileLocation + '.tmp')
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onRenamed -> Error renaming file -> err = ' + err.stack)
|
||||
code = err.code
|
||||
}
|
||||
if (code !== '' && code !== 'ENOENT') { // Unless the file does not exists we do this...
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onOriginalRenamed -> Error renaming original file -> file = ' + fileLocation)
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onOriginalRenamed -> Error renaming original file -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
return
|
||||
}
|
||||
|
||||
/* Rename de Temp into Original */
|
||||
fs.rename(fileLocation + '.tmp', fileLocation, onTempRenamed)
|
||||
|
||||
function onTempRenamed(err) {
|
||||
if (err) {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onOriginalRenamed -> onTempRenamed -> Error renaming temp file -> file = ' + fileLocation + '.tmp')
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onOriginalRenamed -> onTempRenamed -> Error renaming temp file -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
} else {
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
/* In this case, there is no need to keep a copy of the file being replaced, so we just delete and that's it. */
|
||||
fs.unlink(fileLocation , onUnlinked)
|
||||
|
||||
function onUnlinked(err) {
|
||||
let code = ''
|
||||
if (err) {
|
||||
code = err.code
|
||||
}
|
||||
if (code !== '' && code !== 'ENOENT') {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error deleting file -> file = ' + fileLocation )
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> Error deleting file -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
return
|
||||
}
|
||||
|
||||
/* Rename de Temp into Original */
|
||||
fs.rename(fileLocation + '.tmp', fileLocation, onTempRenamed)
|
||||
|
||||
function onTempRenamed(err) {
|
||||
if (err) {
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onOriginalRenamed -> onTempRenamed -> Error renaming temp file -> file = ' + fileLocation + '.tmp')
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> onFileWriten -> onUnlinked -> onOriginalRenamed -> onTempRenamed -> Error renaming temp file -> err = ' + err.stack)
|
||||
setTimeout(retry, retryTimeToUse)
|
||||
} else {
|
||||
|
||||
@@ -213,7 +317,7 @@ exports.newFileStorage = function newFileStorage(logger) {
|
||||
if (currentRetryWriteTextFile < MAX_RETRY) {
|
||||
currentRetryWriteTextFile++
|
||||
logger.write(MODULE_NAME, '[WARN] FileStorage -> createTextFile -> retry -> Will try to write the file again -> Retry #: ' + currentRetryWriteTextFile)
|
||||
recursiveCreateTextFile(filePath, fileContent, callBackFunction)
|
||||
recursiveCreateTextFile(filePath, fileContent, callBackFunction, keepPrevious)
|
||||
} else {
|
||||
currentRetryWriteTextFile = 0
|
||||
logger.write(MODULE_NAME, '[ERROR] FileStorage -> createTextFile -> retry -> Max retries reached writting a file. Giving up.')
|
||||
|
||||
+120
-61
@@ -10,10 +10,12 @@
|
||||
|
||||
return thisObject;
|
||||
|
||||
function initialize(callBackFunction) {
|
||||
function initialize(pProcessConfig, callBackFunction) {
|
||||
try {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
|
||||
|
||||
let telegramAPI
|
||||
|
||||
/* Initialize this info so that everything is logged propeerly */
|
||||
|
||||
bot.SESSION = {
|
||||
@@ -45,66 +47,79 @@
|
||||
return
|
||||
|
||||
function runSession(message) {
|
||||
if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { return } // This happens when the UI is reloaded, the session was running and tries to run it again.
|
||||
|
||||
/* We are going to run the Definition comming at the event. */
|
||||
bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem)
|
||||
bot.SESSION = JSON.parse(message.event.session)
|
||||
|
||||
/* Set the folderName for logging, reports, context and data output */
|
||||
let code
|
||||
if (bot.SESSION.code !== undefined) {
|
||||
code = bot.SESSION.code
|
||||
if (code.folderName === undefined) {
|
||||
bot.SESSION.folderName = bot.SESSION.id
|
||||
try {
|
||||
if (bot.SESSION_STATUS === 'Idle' || bot.SESSION_STATUS === 'Running') { return } // This happens when the UI is reloaded, the session was running and tries to run it again.
|
||||
|
||||
/* We are going to run the Definition comming at the event. */
|
||||
bot.TRADING_SYSTEM = JSON.parse(message.event.tradingSystem)
|
||||
bot.SESSION = JSON.parse(message.event.session)
|
||||
|
||||
/* Set the folderName for logging, reports, context and data output */
|
||||
let code
|
||||
if (bot.SESSION.code !== undefined) {
|
||||
code = bot.SESSION.code
|
||||
if (code.folderName === undefined) {
|
||||
bot.SESSION.folderName = bot.SESSION.id
|
||||
} else {
|
||||
bot.SESSION.folderName = code.folderName + "-" + bot.SESSION.id
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract values from different sources and consolidate them under one structure that is going to be used later on. */
|
||||
setValuesToUse(message)
|
||||
|
||||
/* Set up Social Bots */
|
||||
initializeSocialBots()
|
||||
|
||||
let allGood
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
allGood = startBackTesting(message)
|
||||
break
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
allGood = startLiveTrading(message)
|
||||
break
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
allGood = startFordwardTesting(message)
|
||||
break
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
allGood = startPaperTrading(message)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allGood === true) {
|
||||
bot.SESSION_STATUS = 'Idle'
|
||||
bot.STOP_SESSION = false
|
||||
} else {
|
||||
bot.SESSION.folderName = code.folderName + "-" + bot.SESSION.id
|
||||
bot.STOP_SESSION = true
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up Social Bots */
|
||||
setUpSocialBots()
|
||||
|
||||
/* Extract values from different sources and consolidate them under one structure that is going to be used later on. */
|
||||
setValuesToUse(message)
|
||||
let allGood
|
||||
switch (bot.SESSION.type) {
|
||||
case 'Backtesting Session': {
|
||||
allGood = startBackTesting(message)
|
||||
break
|
||||
}
|
||||
case 'Live Trading Session': {
|
||||
allGood = startLiveTrading(message)
|
||||
break
|
||||
}
|
||||
case 'Fordward Testing Session': {
|
||||
allGood = startFordwardTesting(message)
|
||||
break
|
||||
}
|
||||
case 'Paper Trading Session': {
|
||||
allGood = startPaperTrading(message)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allGood === true) {
|
||||
bot.SESSION_STATUS = 'Idle'
|
||||
bot.STOP_SESSION = false
|
||||
} else {
|
||||
bot.STOP_SESSION = true
|
||||
} catch (err) {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> runSession -> err = " + err.stack);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function stopSession(message) {
|
||||
bot.STOP_SESSION = true
|
||||
try {
|
||||
bot.STOP_SESSION = true
|
||||
finalizeSocialBots()
|
||||
} catch (err) {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> stopSession -> err = " + err.stack);
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
function checkDatetimes() {
|
||||
if (bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() < (new Date()).valueOf()) {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Overriding initialDatetime with present datetime because " + bot.VALUES_TO_USE.timeRange.initialDatetime + " is in the past."); }
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> checkDatetimes -> Overriding initialDatetime with present datetime because " + bot.VALUES_TO_USE.timeRange.initialDatetime + " is in the past."); }
|
||||
bot.VALUES_TO_USE.timeRange.initialDatetime = new Date()
|
||||
}
|
||||
if (bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf() < (new Date()).valueOf()) {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Overriding finalDatetime with present datetime plus one year because " + bot.VALUES_TO_USE.timeRange.finalDatetime + " is in the past."); }
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> checkDatetimes -> Overriding finalDatetime with present datetime plus one year because " + bot.VALUES_TO_USE.timeRange.finalDatetime + " is in the past."); }
|
||||
bot.VALUES_TO_USE.timeRange.finalDatetime = new Date() + ONE_YEAR_IN_MILISECONDS
|
||||
}
|
||||
}
|
||||
@@ -133,13 +148,14 @@
|
||||
bot.resumeExecution = false;
|
||||
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
|
||||
bot.hasTheBotJustStarted = true
|
||||
pProcessConfig.liveWaitTime = getTimeFrameFromLabel(bot.VALUES_TO_USE.timeFrame)
|
||||
return true
|
||||
}
|
||||
|
||||
function startFordwardTesting(message) {
|
||||
|
||||
if (process.env.KEY === undefined || process.env.SECRET === undefined) {
|
||||
if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Key name or Secret not provided, not possible to run the process in Forward Testing mode."); }
|
||||
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
|
||||
}
|
||||
@@ -166,6 +182,9 @@
|
||||
|
||||
bot.VALUES_TO_USE.maximumBalanceA = bot.VALUES_TO_USE.maximumBalanceA * balancePercentage / 100
|
||||
bot.VALUES_TO_USE.maximumBalanceB = bot.VALUES_TO_USE.maximumBalanceB * balancePercentage / 100
|
||||
|
||||
pProcessConfig.normalWaitTime = getTimeFrameFromLabel(bot.VALUES_TO_USE.timeFrame)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -176,9 +195,31 @@
|
||||
bot.resumeExecution = false;
|
||||
bot.hasTheBotJustStarted = true
|
||||
bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf())
|
||||
pProcessConfig.normalWaitTime = getTimeFrameFromLabel(bot.VALUES_TO_USE.timeFrame)
|
||||
return true
|
||||
}
|
||||
|
||||
function getTimeFrameFromLabel(timeFrameLabel) {
|
||||
|
||||
for (let i = 0; i < global.marketFilesPeriods.length; i++) {
|
||||
let value = global.marketFilesPeriods[i][0]
|
||||
let label = global.marketFilesPeriods[i][1]
|
||||
|
||||
if (timeFrameLabel === label) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < global.dailyFilePeriods.length; i++) {
|
||||
let value = global.dailyFilePeriods[i][0]
|
||||
let label = global.dailyFilePeriods[i][1]
|
||||
|
||||
if (timeFrameLabel === label) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setValuesToUse(message) {
|
||||
/*
|
||||
Base on the information received we will determined which values ultimatelly are going to be used,
|
||||
@@ -378,7 +419,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function setUpSocialBots() {
|
||||
function initializeSocialBots() {
|
||||
|
||||
if (bot.SESSION.socialBots !== undefined) {
|
||||
if (bot.SESSION.socialBots.bots !== undefined) {
|
||||
@@ -386,7 +427,7 @@
|
||||
let socialBot = bot.SESSION.socialBots.bots[i]
|
||||
if (socialBot.type === "Telegram Bot") {
|
||||
let code = socialBot.code
|
||||
socialBot.botInstance = setUpTelegramBot(code.botToken, code.chatId)
|
||||
socialBot.botInstance = initializeTelegramBot(code.botToken, code.chatId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,14 +441,14 @@
|
||||
|
||||
if (socialBot.type === "Telegram Bot") {
|
||||
if (announcement.formulaValue !== undefined) {
|
||||
socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err))
|
||||
socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, announcement.formulaValue).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> Telegram API error -> err = " + err))
|
||||
} else {
|
||||
socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> Telegram API error -> err = " + err))
|
||||
socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, code.text).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> Telegram API error -> err = " + err))
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpSocialBots -> announce -> err = " + err.stack);
|
||||
parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> err = " + err.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -417,22 +458,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
function setUpTelegramBot(botToken, chatId) {
|
||||
function finalizeSocialBots() {
|
||||
if (bot.SESSION.socialBots.bots !== undefined) {
|
||||
for (let i = 0; i < bot.SESSION.socialBots.bots.length; i++) {
|
||||
let socialBot = bot.SESSION.socialBots.bots[i]
|
||||
if (socialBot.type === "Telegram Bot") {
|
||||
let code = socialBot.code
|
||||
socialBot.botInstance = finalizeTelegramBot(code.chatId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTelegramBot(botToken, chatId) {
|
||||
/* Telegram Bot Initialization */
|
||||
try {
|
||||
const Telegraf = require('telegraf')
|
||||
let telegramBot
|
||||
telegramBot = new Telegraf(botToken)
|
||||
telegramBot.start((ctx) => ctx.reply('Hi! I am a Telegram Bot powered by Superalgos'))
|
||||
telegramBot.help((ctx) => ctx.reply('Send me a sticker'))
|
||||
telegramBot.on('sticker', (ctx) => ctx.reply('??'))
|
||||
telegramBot.hears('hi', (ctx) => ctx.reply('Hey there'))
|
||||
telegramBot.start((ctx) => ctx.reply('Hi! I am the Telegram Bot that will tell you what happens with your trading session.'))
|
||||
telegramBot.help((ctx) => ctx.reply('I can stop the session if you say STOP.'))
|
||||
telegramBot.hears('STOP', (ctx) => stopSession())
|
||||
telegramBot.launch()
|
||||
|
||||
const Telegram = require('telegraf/telegram')
|
||||
let telegramAPI
|
||||
|
||||
telegramAPI = new Telegram(botToken)
|
||||
telegramAPI.sendMessage(chatId, bot.SESSION.type + " '" + bot.SESSION.name + "' was Started.").catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpTelegramBot -> Telegram API error -> err = " + err))
|
||||
|
||||
const messge = bot.SESSION.type + " '" + bot.SESSION.name + "' was started with an initial balance of " + " " + bot.VALUES_TO_USE.initialBalanceA + " " + bot.VALUES_TO_USE.baseAsset + "."
|
||||
telegramAPI.sendMessage(chatId, messge ).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeTelegramBot -> Telegram API error -> err = " + err))
|
||||
|
||||
let botInstance = {
|
||||
telegramBot: telegramBot,
|
||||
@@ -441,10 +495,15 @@
|
||||
}
|
||||
return botInstance
|
||||
} catch (err) {
|
||||
parentLogger.write(MODULE_NAME, "[WARN] initialize -> setUpTelegramBot -> err = " + err.stack);
|
||||
parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeTelegramBot -> err = " + err.stack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function finalizeTelegramBot(chatId) {
|
||||
const messge = bot.SESSION.type + " '" + bot.SESSION.name + "' was stopped."
|
||||
telegramAPI.sendMessage(chatId, messge).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initialize -> initializeTelegramBot -> Telegram API error -> err = " + err))
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
parentLogger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
|
||||
|
||||
@@ -153,7 +153,21 @@
|
||||
|
||||
filePath += '/' + fileName
|
||||
|
||||
fileStorage.getTextFile(filePath, onFileReceived);
|
||||
let canUserPrevious
|
||||
/*
|
||||
If we are funning Trading Engines, we can not allow ourselves to use a Status Report that is not the latest one, because it might contain
|
||||
transactioinal information related to the context of the operations the trading engine is doing.
|
||||
|
||||
On the contraty, if we are running a Sensor bot or an Indicator bot, we might, if necesary, use a previous version of a Status Report since
|
||||
there will be no big impact, just some reprocessing.
|
||||
*/
|
||||
if (bot.SESSION !== undefined) {
|
||||
canUserPrevious = false
|
||||
} else {
|
||||
canUserPrevious = true
|
||||
}
|
||||
|
||||
fileStorage.getTextFile(filePath, onFileReceived, undefined, canUserPrevious);
|
||||
|
||||
function onFileReceived(err, text) {
|
||||
|
||||
@@ -241,7 +255,7 @@
|
||||
filePath += '/' + fileName
|
||||
let fileContent = JSON.stringify(thisObject.file);
|
||||
|
||||
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
|
||||
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated, true);
|
||||
|
||||
function onFileCreated(err) {
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
if (bot.definedByUI === true) {
|
||||
/* The code of the bot is defined at the UI. No need to load a file with the code. */
|
||||
session.initialize(onSessionInitialized)
|
||||
session.initialize(processConfig, onSessionInitialized)
|
||||
|
||||
function onSessionInitialized(err) {
|
||||
callBackFunction(err);
|
||||
@@ -902,12 +902,18 @@
|
||||
waitTime = processConfig.normalWaitTime
|
||||
}
|
||||
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (waitTime / 1000) + " seconds."); }
|
||||
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> loopControl -> Restarting Loop in " + (waitTime / 1000 / 60) + " minute/s."); }
|
||||
if (processConfig.deadWaitTime > 0) {
|
||||
checkLoopHealthHandle = setTimeout(checkLoopHealth, processConfig.deadWaitTime, bot.loopCounter);
|
||||
}
|
||||
nextLoopTimeoutHandle = setTimeout(loop, waitTime);
|
||||
processHeartBeat(undefined, undefined, "Waiting " + waitTime / 1000 + " seconds for next execution.")
|
||||
let waitingTime = waitTime / 1000 / 60
|
||||
let label = 'minute/s'
|
||||
if (waitingTime < 1) {
|
||||
waitingTime = waitTime / 1000
|
||||
label = 'seconds'
|
||||
}
|
||||
processHeartBeat(undefined, undefined, "Waiting " + waitingTime + " " + label + " for next execution.")
|
||||
if (global.WRITE_LOGS_TO_FILES === 'true') {
|
||||
logger.persist();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user