Merge pull request #307 from Superalgos/tutorials

Trading System Tutorials
This commit is contained in:
Julian Molina authored and GitHub committed 2020-12-14 14:24:20 +01:00
commit c7055ecf5b
930 files changed
+405425 -48175

No files matched your search

-33
View File
@@ -1,33 +0,0 @@
#PORT NUMBERS: If any of these ports are already in use by a different app, please pick a new number between 1 and 50000.
HTTP_INTERFACE_PORT=34248
WEB_SOCKETS_INTERFACE_PORT=18041
# TASK MANAGER VARIABLES:
TASK_SERVER_PATH=./TaskServer
# TASK SERVER VARIABLES:
STORAGE_PATH=./Data-Storage
WRITE_LOGS_TO_FILES=true
LOG_PATH=./Log-Files
WEB_SERVER_URL=localhost
# WEB SERVER VARIABLES:
#Log options
CONSOLE_LOG=false
CONSOLE_ERROR_LOG=true
LOG_FILE_CONTENT=false
PROJECTS_PATH=./Projects
PATH_TO_UI=./UI
PATH_TO_DATA_FILES=./UI/Data-Files/src
PATH_TO_CLIENT=./Client/
DEFAULT_WORKSPACE_PATH=./Projects/Superalgos/Plugins/Workspaces
MY_WORKSPACES_PATH=./My-Workspaces
PATH_TO_FONTS=./Client/WebServer/Fonts
-11
View File
@@ -1,11 +0,0 @@
INTER_PROCESS_FILES_PATH=../Inter-Process-Files
STORAGE_PATH=../Data-Storage
USE_KEYVAULT=false
WRITE_LOGS_TO_FILES=true
LOG_PATH=../Log-Files
WEB_SOCKETS_INTERFACE_PORT=18041
WEB_SERVER_PORT=34248
WEB_SERVER_URL=localhost
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

+1
View File
@@ -55,6 +55,7 @@
Javier A., Gustavo J., Romina GS, Pedro P., Thais M., Andrey M., Loui M., Natalia M., Bashar A., Carlos V., Diego M.,
Sebastian E., Bogdan P., Marko V., Igor S., Niksa K., Rodrigo M., Nicanor M., Alejandro P., Mateo H., Lan T., Leon A., Uroš R., Filip M., Vladimir J. and Pavle B.</p>
<p><span class="red">You too can help grow and improve Superalgos. Become a contributor!</span></p>
<p>If you like the project, please give it a <span class="red">star</span> and <span class="red">fork</span> it on Github. This helps developers find it on the search! </p>
</div>
<div id="masterApp"></div>
<div id="canvasApp"></div>
+29
View File
@@ -344,3 +344,32 @@ kbd {
border-width: 1px;
}
code {
color: #d9534f;
background-color:rgb(200,200,200,0.1);
padding: 2 0;
font-size:110%;
}
/* Tutorial Links */
a:link.tutorial-link {
text-decoration: none;
color: #31708f;
}
a:visited.tutorial-link {
text-decoration: none;
color: #31708f;
}
a:hover.tutorial-link {
text-decoration: underline;
color: #31708f;
}
a:active.tutorial-link {
text-decoration: none;
color: #31708f;
}
+8 -3
View File
@@ -1,4 +1,9 @@
/* Load Environment Variables */
let ENVIRONMENT = require('../Environment.js');
let ENVIRONMENT_MODULE = ENVIRONMENT.newEnvironment()
global.env = ENVIRONMENT_MODULE
process.on('uncaughtException', function (err) {
if (err.message.indexOf("EADDRINUSE") > 0) {
console.log("A Superalgos Client cannot be started. Reason: the port " + port + " is already in use by another application.")
@@ -113,15 +118,15 @@ try {
WEB_SOCKETS_INTERFACE = WEB_SOCKETS_INTERFACE.newWebSocketsInterface(EVENT_SERVER)
WEB_SOCKETS_INTERFACE.initialize()
WEB_SOCKETS_INTERFACE.run()
console.log('Web Sockets Interface .............. Listening at port ' + process.env.WEB_SOCKETS_INTERFACE_PORT)
console.log('Web Sockets Interface .............. Listening at port ' + global.env.WEB_SOCKETS_INTERFACE_PORT)
HTTP_INTERFACE = HTTP_INTERFACE.newHttpInterface(WEB_SERVER, DATA_FILE_SERVER, PROJECT_FILE_SERVER, UI_FILE_SERVER, PLUGIN_SERVER, CCXT_SERVER, WEB3_SERVER)
HTTP_INTERFACE.initialize()
HTTP_INTERFACE.run()
console.log('Http Interface ..................... Listening at port ' + process.env.HTTP_INTERFACE_PORT)
console.log('Http Interface ..................... Listening at port ' + global.env.HTTP_INTERFACE_PORT)
console.log('')
console.log("You are running Superalgos Beta 7")
console.log("You are running Superalgos Beta 7 SP 1")
console.log('')
console.log("What's new? The following was implemented here:")
console.log('')
+59 -79
View File
@@ -6,14 +6,9 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
run: run
}
require('dotenv').config()
const open = require('open')
CONSOLE_LOG = process.env.CONSOLE_LOG === 'true'
CONSOLE_ERROR_LOG = process.env.CONSOLE_ERROR_LOG === 'true'
LOG_FILE_CONTENT = process.env.LOG_FILE_CONTENT === 'true'
let port = process.env.HTTP_INTERFACE_PORT
let port = global.env.HTTP_INTERFACE_PORT
let http = require('http')
let isHttpServerStarted = false
@@ -35,7 +30,6 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
function startHtttpServer() {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> startHtttpServer -> Entering function.') }
try {
if (isHttpServerStarted === false) {
@@ -55,7 +49,6 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
function onBrowserRequest(httpRequest, httpResponse) {
if (CONSOLE_LOG === true && httpRequest.url.indexOf('NO-LOG') === -1) { console.log('[INFO] httpInterface -> onBrowserRequest -> httpRequest.url = ' + httpRequest.url) }
function getBody(callback) { // Gets the de body from a POST httpRequest to the web server
try {
@@ -75,11 +68,11 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
})
httpRequest.on('error', function (err) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> onBrowserRequest -> getBody -> err = ' + err.stack) }
console.log('[INFO] httpInterface -> onBrowserRequest -> getBody -> err = ' + err.stack)
respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), httpResponse)
})
} catch (err) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> onBrowserRequest -> getBody -> err = ' + err.stack) }
console.log('[INFO] httpInterface -> onBrowserRequest -> getBody -> err = ' + err.stack)
respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), httpResponse)
}
}
@@ -137,9 +130,9 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
}
} catch (err) {
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] httpInterface -> WEB3s -> Method call produced an error.') }
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] httpInterface -> WEB3s -> err.stack = ' + err.stack) }
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] httpInterface -> WEB3s -> Params Received = ' + body) }
console.log('[ERROR] httpInterface -> WEB3s -> Method call produced an error.')
console.log('[ERROR] httpInterface -> WEB3s -> err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> WEB3s -> Params Received = ' + body)
let error = {
result: 'Fail Because',
@@ -219,7 +212,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
err: global.DEFAULT_FAIL_RESPONSE // method not supported
}
} catch (err) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> CCXT FetchMarkets -> Could not fetch markets.') }
console.log('[INFO] httpInterface -> CCXT FetchMarkets -> Could not fetch markets.')
let error = {
result: 'Fail Because',
message: err.message
@@ -310,19 +303,19 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'LegacyPlotter.js':
{
respondWithFile(process.env.PATH_TO_CLIENT + 'WebServer/LegacyPlotter.js', httpResponse)
respondWithFile(global.env.PATH_TO_CLIENT + 'WebServer/LegacyPlotter.js', httpResponse)
}
break
case 'PlotterPanel.js':
{
respondWithFile(process.env.PATH_TO_CLIENT + 'WebServer/PlotterPanel.js', httpResponse)
respondWithFile(global.env.PATH_TO_CLIENT + 'WebServer/PlotterPanel.js', httpResponse)
}
break
case 'Images': // This means the Images folder.
{
let path = process.env.PATH_TO_CLIENT + 'WebServer/Images/' + requestParameters[2]
let path = global.env.PATH_TO_CLIENT + 'WebServer/Images/' + requestParameters[2]
if (requestParameters[3] !== undefined) {
path = path + '/' + requestParameters[3]
@@ -344,7 +337,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'Icons': // This means the Icons folder under Projects.
{
let path = process.env.PROJECTS_PATH + '/' + requestParameters[2] + '/Icons'
let path = global.env.PATH_TO_PROJECTS + '/' + requestParameters[2] + '/Icons'
if (requestParameters[3] !== undefined) {
path = path + '/' + requestParameters[3]
@@ -365,7 +358,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'Gifs': // This means the Gifs folder under Projects.
{
let path = process.env.PROJECTS_PATH + '/' + requestParameters[2] + '/Gifs'
let path = global.env.PATH_TO_PROJECTS + '/' + requestParameters[2] + '/Gifs'
if (requestParameters[3] !== undefined) {
path = path + '/' + requestParameters[3]
@@ -386,19 +379,19 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'favicon.ico': // This means the Scripts folder.
{
respondWithImage(process.env.PATH_TO_CLIENT + 'WebServer/Images/' + 'favicon.ico', httpResponse)
respondWithImage(global.env.PATH_TO_CLIENT + 'WebServer/Images/' + 'favicon.ico', httpResponse)
}
break
case 'WebServer': // This means the WebServer folder.
{
respondWithFile(process.env.PATH_TO_CLIENT + 'WebServer/' + requestParameters[2], httpResponse)
respondWithFile(global.env.PATH_TO_CLIENT + 'WebServer/' + requestParameters[2], httpResponse)
}
break
case 'externalScripts': // This means the WebServer folder.
{
respondWithFile(process.env.PATH_TO_CLIENT + 'WebServer/externalScripts/' + requestParameters[2], httpResponse)
respondWithFile(global.env.PATH_TO_CLIENT + 'WebServer/externalScripts/' + requestParameters[2], httpResponse)
}
break
@@ -408,45 +401,45 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
let dataMine = requestParameters[3]
let codeName = requestParameters[4]
let moduleName = requestParameters[5]
let filePath = process.env.PROJECTS_PATH + '/' + project + '/' + 'Bots-Plotters-Code' + '/' + dataMine + '/plotters/' + codeName + '/' + moduleName
let filePath = global.env.PATH_TO_PROJECTS + '/' + project + '/' + 'Bots-Plotters-Code' + '/' + dataMine + '/plotters/' + codeName + '/' + moduleName
respondWithFile(filePath, httpResponse)
}
break
case 'ChartLayers':
{
respondWithFile(process.env.PATH_TO_UI + '/' + requestParameters[1] + '/' + requestParameters[2], httpResponse)
respondWithFile(global.env.PATH_TO_UI + '/' + requestParameters[1] + '/' + requestParameters[2], httpResponse)
}
break
case 'Files':
{
respondWithFile(process.env.PATH_TO_DATA_FILES + '/' + requestParameters[2], httpResponse)
respondWithFile(global.env.PATH_TO_DATA_FILES + '/' + requestParameters[2], httpResponse)
}
break
case 'Fonts':
{
respondWithFont(process.env.PATH_TO_FONTS + '/' + requestParameters[2], httpResponse)
respondWithFont(global.env.PATH_TO_FONTS + '/' + requestParameters[2], httpResponse)
}
break
case 'ProjectNames':
{
let projects = getDirectories(process.env.PROJECTS_PATH)
let projects = getDirectories(global.env.PATH_TO_PROJECTS)
respondWithContent(JSON.stringify(projects), httpResponse)
}
break
case 'Schema':
{
sendSchema(process.env.PROJECTS_PATH + '/' + requestParameters[2] + '/Schemas/', requestParameters[3] + '.json')
sendSchema(global.env.PATH_TO_PROJECTS + '/' + requestParameters[2] + '/Schemas/', requestParameters[3] + '.json')
}
break
case 'IconNames':
{
let projects = getDirectories(process.env.PROJECTS_PATH)
let projects = getDirectories(global.env.PATH_TO_PROJECTS)
const fs = require('fs')
let icons = []
@@ -456,7 +449,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
for (let i = 0; i < projects.length; i++) {
let project = projects[i]
const folder = process.env.PROJECTS_PATH + '/' + project + '/Icons/'
const folder = global.env.PATH_TO_PROJECTS + '/' + project + '/Icons/'
fs.readdir(folder, (err, files) => {
for (let j = 0; j < files.length; j++) {
@@ -479,7 +472,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
let pluginType = unescape(requestParameters[3])
const fs = require('fs')
let folder = process.env.PROJECTS_PATH + '/' + project + '/Plugins/' + pluginType
let folder = global.env.PATH_TO_PROJECTS + '/' + project + '/Plugins/' + pluginType
fs.readdir(folder, (err, files) => {
respondWithContent(JSON.stringify(files), httpResponse)
@@ -492,7 +485,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
let project = unescape(requestParameters[2])
let pluginType = unescape(requestParameters[3])
let fileName = unescape(requestParameters[4])
let filePath = process.env.PROJECTS_PATH + '/' + project + '/Plugins/' + pluginType + '/' + fileName
let filePath = global.env.PATH_TO_PROJECTS + '/' + project + '/Plugins/' + pluginType + '/' + fileName
respondWithFile(filePath, httpResponse)
}
break
@@ -502,7 +495,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
let fs = require('fs')
try {
let filePath = process.env.DEFAULT_WORKSPACE_PATH + '/Getting-Started.json'
let filePath = global.env.PATH_TO_DEFAULT_WORKSPACE + '/Getting-Started.json'
fs.readFile(filePath, onFileRead)
} catch (e) {
console.log('[ERROR] Error reading the Workspace.', e)
@@ -522,21 +515,21 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'ListWorkspaces':
{
let allWorkspaces = []
let projects = getDirectories(process.env.PROJECTS_PATH)
let projects = getDirectories(global.env.PATH_TO_PROJECTS)
let projectsCount = 0
for (let i = 0; i < projects.length; i++) {
let project = projects[i]
readPluginWorkspaces()
function readPluginWorkspaces() {
let dirPath = process.env.PROJECTS_PATH + '/' + project + '/Plugins/Workspaces'
let dirPath = global.env.PATH_TO_PROJECTS + '/' + project + '/Plugins/Workspaces'
try {
let fs = require('fs')
fs.readdir(dirPath, onDirRead)
function onDirRead(err, fileList) {
if (err) {
if (CONSOLE_ERROR_LOG === true) { console.log('[WARN] Error reading a directory content. filePath = ' + dirPath) }
console.log('[WARN] Error reading a directory content. filePath = ' + dirPath)
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), httpResponse)
return
} else {
@@ -554,8 +547,8 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
}
} catch (err) {
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath) }
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] err.stack = ' + err.stack) }
console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath)
console.log('[ERROR] err.stack = ' + err.stack)
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), httpResponse)
return
}
@@ -563,14 +556,14 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
function readMyWorkspaces() {
let dirPath = process.env.MY_WORKSPACES_PATH
let dirPath = global.env.PATH_TO_MY_WORKSPACES
try {
let fs = require('fs')
fs.readdir(dirPath, onDirRead)
function onDirRead(err, fileList) {
if (err) {
if (CONSOLE_ERROR_LOG === true) { console.log('[WARN] Error reading a directory content. filePath = ' + dirPath) }
console.log('[WARN] Could not read this directory content. filePath = ' + dirPath)
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), httpResponse)
return
} else {
@@ -585,8 +578,8 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
}
} catch (err) {
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath) }
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] err.stack = ' + err.stack) }
console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath)
console.log('[ERROR] err.stack = ' + err.stack)
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), httpResponse)
return
}
@@ -597,7 +590,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'LoadMyWorkspace':
{
let fileName = unescape(requestParameters[2])
let filePath = process.env.MY_WORKSPACES_PATH + '/' + fileName + '.json'
let filePath = global.env.PATH_TO_MY_WORKSPACES + '/' + fileName + '.json'
respondWithFile(filePath, httpResponse)
}
break
@@ -610,11 +603,11 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
let fileContent = body
let fileName = unescape(requestParameters[2])
let filePath = process.env.MY_WORKSPACES_PATH + '/' + fileName + '.json'
let filePath = global.env.PATH_TO_MY_WORKSPACES + '/' + fileName + '.json'
try {
let fs = require('fs')
let dir = process.env.MY_WORKSPACES_PATH;
let dir = global.env.PATH_TO_MY_WORKSPACES;
/* Create Dir if it does not exist */
if (!fs.existsSync(dir)) {
@@ -660,7 +653,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'ProjectsSchema':
{
let path = process.env.PROJECTS_PATH + '/' + 'ProjectsSchema.json'
let path = global.env.PATH_TO_PROJECTS + '/' + 'ProjectsSchema.json'
respondWithFile(path, httpResponse)
}
break
@@ -669,15 +662,15 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
{
let fs = require('fs')
let allFiles = []
let projects = getDirectories(process.env.PROJECTS_PATH)
let projects = getDirectories(global.env.PATH_TO_PROJECTS)
let dirCount = 0
let totalDirs = 0
for (let i = 0; i < projects.length; i++) {
let project = projects[i]
let dirPath = project + '/Spaces'
let spaces = getDirectories(process.env.PROJECTS_PATH + '/' + dirPath)
let dirPath = project + '/UI/Spaces'
let spaces = getDirectories(global.env.PATH_TO_PROJECTS + '/' + dirPath)
for (let j = 0; j < spaces.length; j++) {
let space = spaces[j]
@@ -688,9 +681,9 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
try {
totalDirs++
fs.readdir(process.env.PROJECTS_PATH + '/' + path, onDirRead)
fs.readdir(global.env.PATH_TO_PROJECTS + '/' + path, onDirRead)
let otherDirs = getDirectories(process.env.PROJECTS_PATH + '/' + path)
let otherDirs = getDirectories(global.env.PATH_TO_PROJECTS + '/' + path)
for (let m = 0; m < otherDirs.length; m++) {
let otherDir = otherDirs[m]
readDirectory(path + '/' + otherDir)
@@ -715,9 +708,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
} catch (err) {
console.log('[ERROR] Error reading a directory content. filePath = ' + path)
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] err.stack = ' + err.stack) }
console.log(err.stack)
console.log('[ERROR] err.stack = ' + err.stack)
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), httpResponse)
return
}
@@ -748,14 +739,14 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
}
let filePath = process.env.PROJECTS_PATH + path
let filePath = global.env.PATH_TO_PROJECTS + path
respondWithFile(filePath, httpResponse)
}
break
case 'Storage':
{
respondWithFile(process.env.STORAGE_PATH + '/' + httpRequest.url.substring(9), httpResponse)
respondWithFile(global.env.PATH_TO_DATA_STORAGE + '/' + httpRequest.url.substring(9), httpResponse)
}
break
@@ -785,21 +776,20 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
function returnProjectFolderFileList(projectFolderName) {
{
let allLibraries = []
let projects = getDirectories(process.env.PROJECTS_PATH)
let projects = getDirectories(global.env.PATH_TO_PROJECTS)
let projectsCount = 0
for (let i = 0; i < projects.length; i++) {
let project = projects[i]
let dirPath = process.env.PROJECTS_PATH + '/' + project + '/' + projectFolderName
let dirPath = global.env.PATH_TO_PROJECTS + '/' + project + '/' + 'UI' + '/' + projectFolderName
try {
let fs = require('fs')
fs.readdir(dirPath, onDirRead)
function onDirRead(err, fileList) {
if (err) {
if (CONSOLE_ERROR_LOG === true) { console.log('[WARN] Error reading a directory content. filePath = ' + dirPath) }
if (CONSOLE_ERROR_LOG === true) { console.log(err.stack) }
console.log('[WARN] Error reading a directory content. filePath = ' + dirPath)
return
} else {
let updatedFileList = []
@@ -816,8 +806,8 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
}
} catch (err) {
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath) }
if (CONSOLE_ERROR_LOG === true) { console.log('[ERROR] err.stack = ' + err.stack) }
console.log('[ERROR] Error reading a directory content. filePath = ' + dirPath)
console.log('[ERROR] err.stack = ' + err.stack)
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), httpResponse)
return
@@ -863,16 +853,16 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
function sendStyleSheet(fileName) {
let fs = require('fs')
try {
let filePath = process.env.PATH_TO_CLIENT + 'WebServer/' + fileName
let filePath = global.env.PATH_TO_CLIENT + 'WebServer/' + fileName
fs.readFile(filePath, onFileRead)
function onFileRead(err, file) {
try {
let fileContent = file.toString()
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', process.env.HTTP_INTERFACE_PORT)
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', process.env.HTTP_INTERFACE_PORT)
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', process.env.HTTP_INTERFACE_PORT)
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', global.env.HTTP_INTERFACE_PORT)
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', global.env.HTTP_INTERFACE_PORT)
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', global.env.HTTP_INTERFACE_PORT)
respondWithContent(fileContent, httpResponse, 'text/css')
} catch (err) {
console.log('[ERROR] httpInterface -> mainCSS -> File Not Found: ' + fileName + ' or Error = ' + err.stack)
@@ -887,14 +877,14 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
if (requestParameters[1] === '') {
let fs = require('fs')
try {
let fileName = process.env.PATH_TO_CLIENT + 'WebServer/index.html'
let fileName = global.env.PATH_TO_CLIENT + 'WebServer/index.html'
fs.readFile(fileName, onFileRead)
function onFileRead(err, file) {
try {
let fileContent = file.toString()
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', process.env.HTTP_INTERFACE_PORT)
fileContent = fileContent.replace('HTTP_INTERFACE_PORT', global.env.HTTP_INTERFACE_PORT)
respondWithContent(fileContent, httpResponse)
} catch (err) {
console.log('[ERROR] httpInterface -> homePage -> File Not Found: ' + fileName + ' or Error = ' + err.stack)
@@ -904,7 +894,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
console.log(err)
}
} else {
respondWithFile(process.env.PATH_TO_UI + '/' + requestParameters[1], httpResponse)
respondWithFile(global.env.PATH_TO_UI + '/' + requestParameters[1], httpResponse)
}
}
}
@@ -919,7 +909,6 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
fs.readFile(fileName, onFileRead)
function onFileRead(err, file) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> respondWithFile -> onFileRead -> Entering function.') }
if (!err) {
respondWithContent(file.toString(), httpResponse)
} else {
@@ -934,8 +923,6 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
function respondWithContent(content, httpResponse, contentType) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> respondWithContent -> Entering function.') }
try {
httpResponse.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate') // HTTP 1.1.
httpResponse.setHeader('Pragma', 'no-cache') // HTTP 1.0.
@@ -960,8 +947,6 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
function respondWithImage(fileName, httpResponse) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> respondWithImage -> Entering function.') }
let fs = require('fs')
try {
fs.readFile(fileName, onFileRead)
@@ -989,15 +974,11 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
function respondWithFont(fileName, httpResponse) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> respondWithBinary -> Entering function.') }
let fs = require('fs')
try {
fs.readFile(fileName, onFileRead)
function onFileRead(err, file) {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> respondWithBinary -> onFileRead -> Entering function.') }
try {
if (err) {
console.log('[ERROR] httpInterface -> respondWithBinary -> onFileRead -> File Not Found: ' + fileName + ' or Error = ' + err.stack)
@@ -1025,7 +1006,6 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
function returnEmptyArray(httpResponse) {
try {
if (CONSOLE_LOG === true) { console.log('[INFO] httpInterface -> returnEmptyArray -> Entering function.') }
httpResponse.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate') // HTTP 1.1.
httpResponse.setHeader('Pragma', 'no-cache') // HTTP 1.0.
httpResponse.setHeader('Expires', '0') // Proxies.
-11
View File
@@ -1,11 +0,0 @@
/* @flow */
(function () {
require('./lib/main').config(
Object.assign(
{},
require('./lib/env-options'),
require('./lib/cli-options')(process.argv)
)
)
})()
-13
View File
@@ -1,13 +0,0 @@
/* @flow */
const re = /^dotenv_config_(encoding|path|debug)=(.+)$/
module.exports = function optionMatcher (args /*: Array<string> */) {
return args.reduce(function (acc, cur) {
const matches = cur.match(re)
if (matches) {
acc[matches[1]] = matches[2]
}
return acc
}, {})
}
-18
View File
@@ -1,18 +0,0 @@
/* @flow */
// ../config.js accepts options via environment variables
const options = {}
if (process.env.DOTENV_CONFIG_ENCODING != null) {
options.encoding = process.env.DOTENV_CONFIG_ENCODING
}
if (process.env.DOTENV_CONFIG_PATH != null) {
options.path = process.env.DOTENV_CONFIG_PATH
}
if (process.env.DOTENV_CONFIG_DEBUG != null) {
options.debug = process.env.DOTENV_CONFIG_DEBUG
}
module.exports = options
-113
View File
@@ -1,113 +0,0 @@
/* @flow */
/*::
type DotenvParseOptions = {
debug?: boolean
}
// keys and values from src
type DotenvParseOutput = { [string]: string }
type DotenvConfigOptions = {
path?: string, // path to .env file
encoding?: string, // encoding of .env file
debug?: string // turn on logging for debugging purposes
}
type DotenvConfigOutput = {
parsed?: DotenvParseOutput,
error?: Error
}
*/
const fs = require('fs')
const path = require('path')
function log (message /*: string */) {
console.log(`[dotenv][DEBUG] ${message}`)
}
const NEWLINE = '\n'
const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/
const RE_NEWLINES = /\\n/g
const NEWLINES_MATCH = /\n|\r|\r\n/
// Parses src into an Object
function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ {
const debug = Boolean(options && options.debug)
const obj = {}
// convert Buffers before splitting into lines and processing
src.toString().split(NEWLINES_MATCH).forEach(function (line, idx) {
// matching "KEY' and 'VAL' in 'KEY=VAL'
const keyValueArr = line.match(RE_INI_KEY_VAL)
// matched?
if (keyValueArr != null) {
const key = keyValueArr[1]
// default undefined or missing values to empty string
let val = (keyValueArr[2] || '')
const end = val.length - 1
const isDoubleQuoted = val[0] === '"' && val[end] === '"'
const isSingleQuoted = val[0] === "'" && val[end] === "'"
// if single or double quoted, remove quotes
if (isSingleQuoted || isDoubleQuoted) {
val = val.substring(1, end)
// if double quoted, expand newlines
if (isDoubleQuoted) {
val = val.replace(RE_NEWLINES, NEWLINE)
}
} else {
// remove surrounding whitespace
val = val.trim()
}
obj[key] = val
} else if (debug) {
log(`did not match key and value when parsing line ${idx + 1}: ${line}`)
}
})
return obj
}
// Populates process.env from .env file
function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ {
let dotenvPath = path.resolve(process.cwd(), '.env')
let encoding /*: string */ = 'utf8'
let debug = false
if (options) {
if (options.path != null) {
dotenvPath = options.path
}
if (options.encoding != null) {
encoding = options.encoding
}
if (options.debug != null) {
debug = true
}
}
try {
// specifying an encoding returns a string instead of a buffer
const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug })
Object.keys(parsed).forEach(function (key) {
if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
process.env[key] = parsed[key]
} else if (debug) {
log(`"${key}" is already defined in \`process.env\` and will not be overwritten`)
}
})
return { parsed }
} catch (e) {
return { error: e }
}
}
module.exports.config = config
module.exports.parse = parse
-79
View File
@@ -1,79 +0,0 @@
{
"_from": "dotenv@^8.2.0",
"_id": "dotenv@8.2.0",
"_inBundle": false,
"_integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
"_location": "/dotenv",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "dotenv@^8.2.0",
"name": "dotenv",
"escapedName": "dotenv",
"rawSpec": "^8.2.0",
"saveSpec": null,
"fetchSpec": "^8.2.0"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"_shasum": "97e619259ada750eea3e4ea3e26bceea5424b16a",
"_spec": "dotenv@^8.2.0",
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\BackendServers",
"bugs": {
"url": "https://github.com/motdotla/dotenv/issues"
},
"bundleDependencies": false,
"dependencies": {},
"deprecated": false,
"description": "Loads environment variables from .env file",
"devDependencies": {
"decache": "^4.5.1",
"dtslint": "^0.9.8",
"flow-bin": "^0.109.0",
"sinon": "^7.5.0",
"standard": "^13.1.0",
"standard-markdown": "^5.1.0",
"standard-version": "^7.0.0",
"tap": "^14.7.0"
},
"engines": {
"node": ">=8"
},
"homepage": "https://github.com/motdotla/dotenv#readme",
"keywords": [
"dotenv",
"env",
".env",
"environment",
"variables",
"config",
"settings"
],
"license": "BSD-2-Clause",
"main": "lib/main.js",
"name": "dotenv",
"repository": {
"type": "git",
"url": "git://github.com/motdotla/dotenv.git"
},
"scripts": {
"dtslint": "dtslint types",
"flow": "flow",
"lint": "standard",
"postlint": "standard-markdown",
"prerelease": "npm test",
"pretest": "npm run lint && npm run dtslint",
"release": "standard-version",
"test": "tap tests/*.js --100"
},
"standard": {
"ignore": [
"flow-typed/"
]
},
"types": "types",
"version": "8.2.0"
}
-15
View File
@@ -1,15 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["es6"],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"dotenv": ["."]
}
}
}
-6
View File
@@ -1,6 +0,0 @@
{
"extends": "dtslint/dtslint.json",
"rules": {
"strict-export-declare-modifiers": false
}
}
+1 -6
View File
@@ -1,5 +1,5 @@
{
"name": "client",
"name": "Client",
"version": "0.0.6",
"lockfileVersion": 1,
"requires": true,
@@ -812,11 +812,6 @@
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
},
"dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+2 -3
View File
@@ -1,15 +1,14 @@
{
"name": "client",
"name": "Client",
"version": "0.0.6",
"description": "BackendServer",
"main": "server.js",
"main": "client.js",
"author": {
"name": "Luis-Fernando-Molina"
},
"dependencies": {
"ccxt": "^1.23.67",
"child_process": "^1.0.2",
"dotenv": "^8.2.0",
"open": "^6.4.0",
"rimraf": "^3.0.2",
"web3": "^1.3.0",
+1 -2
View File
@@ -13,7 +13,6 @@
*/
const { fork } = require('child_process')
require('dotenv').config()
tasksMap = new Map()
return thisObject
@@ -64,7 +63,7 @@
//console.log('[INFO] Client -> Task Manager Server -> runTask -> Task Name = ' + message.event.taskName)
//console.log('[INFO] Client -> Task Manager Server -> runTask -> Task Id = ' + message.event.taskId)
let path = process.env.TASK_SERVER_PATH + '/server.js'
let path = global.env.PATH_TO_TASK_SERVER + '/Task.js'
/* Workarround to avoid having the same debug port at the forked process which makes it crash. */
for (let i = 0; i < process.execArgv.length; i++) {
+1 -2
View File
@@ -9,10 +9,9 @@
run: run
}
require('dotenv').config()
const WEB_SOCKET = require('ws')
let socketServer
let port = process.env.WEB_SOCKETS_INTERFACE_PORT
let port = global.env.WEB_SOCKETS_INTERFACE_PORT
return thisObject
+21
View File
@@ -0,0 +1,21 @@
exports.newEnvironment = function () {
let thisObject = {
WEB_SERVER_URL: 'localhost',
WEB_SOCKETS_INTERFACE_PORT: 18041,
HTTP_INTERFACE_PORT: 34248,
PATH_TO_TASK_SERVER: './TaskServer',
PATH_TO_DATA_STORAGE: './Data-Storage',
PATH_TO_PROJECTS: './Projects',
PATH_TO_LOG_FILES: './Log-Files',
PATH_TO_PROJECTS_REQUIRED: '../Projects',
PATH_TO_UI: './UI',
PATH_TO_DATA_FILES: './UI/Data-Files/src',
PATH_TO_CLIENT: './Client/',
PATH_TO_DEFAULT_WORKSPACE: './Projects/Superalgos/Plugins/Workspaces',
PATH_TO_MY_WORKSPACES: './My-Workspaces',
PATH_TO_FONTS: './Client/WebServer/Fonts'
}
return thisObject
}
@@ -1 +1 @@
{"type":"Workspace","name":"Ethereum Integration Demo","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataMining":{"type":"Data Mining","name":"New Data Mining","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2017-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"da7e6d2a-76d5-431c-ba48-aa98ebd2a907","savedPayload":{"position":{"x":23623.030015561097,"y":29888.16403283887},"targetPosition":{"x":23918.47234146476,"y":29836.06957953879},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"f9daa393-935e-4a9d-bdc5-706d97ac5530","savedPayload":{"position":{"x":23918.47234146476,"y":29836.06957953879},"targetPosition":{"x":24213.914667368423,"y":29783.97512623871},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"5827edbc-89f8-495a-953a-a568f2fca44e","savedPayload":{"position":{"x":24213.914667368423,"y":29783.97512623871},"targetPosition":{"x":24509.356993272086,"y":29731.88067293863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"243ecbf4-cb69-42e2-9acb-b03c7c481b79","savedPayload":{"position":{"x":23619.613932037235,"y":29653.83848916277},"targetPosition":{"x":23918.47234146476,"y":29627.69176633847},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"c90cb0e5-3e49-40ba-b919-38b0e5f7fcee","savedPayload":{"position":{"x":23646.580005353764,"y":29500.906287816262},"targetPosition":{"x":23918.47234146476,"y":29627.69176633847},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"980ceb1d-7be6-454f-90d9-01ba1fd4a65d"}}}],"id":"ac31f04b-4d79-4e18-9262-1a9a933b6156","savedPayload":{"position":{"x":23918.47234146476,"y":29627.69176633847},"targetPosition":{"x":24213.914667368423,"y":29679.78621963855},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"46c3f665-a9e2-46dc-b569-2a928d2067af","savedPayload":{"position":{"x":24213.914667368423,"y":29679.78621963855},"targetPosition":{"x":24509.356993272086,"y":29731.88067293863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Bollinger Bands","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Bollinger Bands","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Bollinger Bands Multi-Period-Market","project":"Superalgos","id":"12498614-646b-411c-9c82-747bacb8d7a6","savedPayload":{"position":{"x":23699.9640031147,"y":29354.234959407873},"targetPosition":{"x":23989.74175100142,"y":29431.88067293863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}},{"typeLine truncated
{"type":"Workspace","name":"Ethereum Integration Demo","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[{"type":"Project Data Tasks","name":"New Project Definition","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"790c56a4-deba-499f-9756-115fdb8cab44","savedPayload":{"position":{"x":25276.356993272086,"y":30604.11115747996},"targetPosition":{"x":25218.356993272086,"y":30630.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"8a5bbe7c-4a5c-4c4a-ad2b-38cf489c859a","savedPayload":{"position":{"x":25218.356993272086,"y":30630.11115747996},"targetPosition":{"x":25145.356993272086,"y":30724.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"56ceaef5-290c-4512-94c9-2f5fd13486d1","savedPayload":{"position":{"x":25145.356993272086,"y":30724.11115747996},"targetPosition":{"x":25092.356993272086,"y":30693.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Webhooks","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Webhooks","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Webhooks Check-Webhook","project":"Superalgos","id":"0a8d8265-e0f8-4f3a-96d0-bfe4bbbd3224","savedPayload":{"position":{"x":25136.356993272086,"y":30684.11115747996},"targetPosition":{"x":25152.356993272086,"y":30784.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Check-Webhook","id":"69f37820-ab16-4e19-bfd8-386dd6e249a3"}}}],"id":"718a49a9-deae-4849-a796-714feb1e08bc","savedPayload":{"position":{"x":25152.356993272086,"y":30784.11115747996},"targetPosition":{"x":25081.356993272086,"y":30703.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"31747cdc-3783-40be-860f-92e381003ffe","savedPayload":{"position":{"x":25081.356993272086,"y":30703.11115747996},"targetPosition":{"x":25092.356993272086,"y":30693.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"1daa3ec2-c891-44ab-997a-f427a8586716","savedPayload":{"position":{"x":25041.356993272086,"y":30670.11115747996},"targetPosition":{"x":25039.356993272086,"y":30732.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"8225345d-7cd7-4889-99b0-af692402d269","savedPayload":{"position":{"x":24989.356993272086,"y":30689.11115747996},"targetPosition":{"x":25039.356993272086,"y":30732.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":Line truncated
+330 -2
View File
@@ -26,6 +26,12 @@
"relatedUiObject": "Ethereum Wallet",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"label": "Add Data Mine",
"relatedUiObject": "Data Mine",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
@@ -41,7 +47,16 @@
"addLeftIcons": true,
"isPinned": true,
"positionLocked": true,
"icon": "ethereum"
"icon": "ethereum",
"properties": [
{
"name": "projectDefinition",
"type": "node",
"childType": "Project Definition",
"project": "Superalgos",
"autoAdd": true
}
]
},
{
"type": "Ethereum Wallet",
@@ -200,7 +215,7 @@
"propertyNameAtParent": "ethBalance",
"icon": "ethereum"
},
{
{
"type": "Token Balance",
"menuItems": [
{
@@ -686,5 +701,318 @@
"addLeftIcons": true,
"level": 2,
"icon": "ethereum"
},
{
"type": "Project Data Tasks",
"menuItems": [
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Data Tasks->"
},
"propertyNameAtParent": "projectDataTasks",
"properties": [],
"icon": "bitcoin"
},
{
"type": "Project Learning Tasks",
"menuItems": [
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Learning Tasks->"
},
"propertyNameAtParent": "projectLearningTasks",
"properties": [],
"icon": "bitcoin"
},
{
"type": "Project Trading Tasks",
"menuItems": [
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Trading Tasks->"
},
"propertyNameAtParent": "projectTradingTasks",
"properties": [],
"icon": "bitcoin"
},
{
"type": "Project Data Products",
"menuItems": [
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Data Mines Data->"
},
"propertyNameAtParent": "projectDataProducts",
"properties": [],
"icon": "bitcoin"
},
{
"type": "Project Learning Products",
"menuItems": [
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Learning Mine Data->"
},
"propertyNameAtParent": "projectLearningProducts",
"properties": [],
"icon": "bitcoin"
},
{
"type": "Project Trading Products",
"menuItems": [
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Trading Mine Data->"
},
"propertyNameAtParent": "projectTradingProducts",
"properties": [],
"icon": "bitcoin"
},
{
"type": "Project Dashboards",
"menuItems": [
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Charting Space->"
},
"propertyNameAtParent": "projectDashboards",
"properties": [],
"icon": "bitcoin"
},
{
"type": "Data Mine",
"menuItems": [
{
"action": "Configure",
"label": "Configure",
"iconPathOn": "configuration",
"iconPathOff": "configuration",
"dontShowAtFullscreen": true,
"actionFunction": "uiObject.configEditor.activate"
},
{
"action": "Add UI Object",
"label": "Add Blockchain Scanner Bot",
"relatedUiObject": "Blockchain Scanner Bot",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"isTitleAllwaysVisible": true,
"isHierarchyHead": true,
"addLeftIcons": true,
"editors": {
"config": true
},
"level": 0,
"initialValues": {
"config": "{ \n\"codeName\": \"Type a name with no spaces or dashes\"\n}"
},
"properties": [
{
"name": "blockchainScannerBots",
"type": "array",
"childType": "Blockchain Scanner Bot"
}
],
"icon": "ethereum"
},
{
"type": "Blockchain Scanner Bot",
"menuItems": [
{
"action": "Configure",
"label": "Configure",
"iconPathOn": "configuration",
"iconPathOff": "configuration",
"dontShowAtFullscreen": true,
"actionFunction": "uiObject.configEditor.activate"
},
{
"action": "Add UI Object",
"label": "Add Process Definition",
"relatedUiObject": "Process Definition",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"label": "Add Product Definition",
"relatedUiObject": "Product Definition",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"isTitleAllwaysVisible": true,
"addLeftIcons": true,
"editors": {
"config": true
},
"initialValues": {
"config": "{ \n \"codeName\": \"\"\n}"
},
"level": 1,
"chainAttachesTo": {
"compatibleTypes": "->Data Mine->"
},
"properties": [
{
"name": "processes",
"type": "array",
"childType": "Process Definition",
"project": "Superalgos",
"autoAdd": true
},
{
"name": "products",
"type": "array",
"childType": "Product Definition",
"project": "Superalgos",
"autoAdd": true
}
],
"propertyNameAtParent": "blockchainScannerBots",
"icon": "ethereum"
}
]
File diff suppressed because it is too large. Load diff
@@ -1 +0,0 @@
// Nothing here to run
@@ -1,891 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const INTENSIVE_LOG = false;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands";
const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles from Candles Volumes and produce daily files with bollinger bands.
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
/* Context Variables */
let contextVariables = {
lastBandFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxBandFile: undefined // Datetime of the last file available to be used as an input of this process.
};
let previousDay; // Holds the date of the previous day relative to the processing date.
let processDate; // Holds the processing date.
getContextVariables();
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); }
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS);
/* Second, we get the report from Candles Volumes, to know when the marted ends. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxBandFile = new Date(thisReport.lastFile.valueOf());
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Daily"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastBandFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastBandFile = new Date(contextVariables.lastBandFile.valueOf() + ONE_DAY_IN_MILISECONDS);
buildBands();
return;
}
contextVariables.lastBandFile = new Date(thisReport.lastFile);
/*
The bands objects needs to be calculated over more than one day in some situations. In order to simplify the calculations we will
always load the last 2 days of candles, and run the calculations on that array.
1. So first we will load the candles file of processDay -1.
2. Secondly we will load the candles file of processDay.
After reading these two files we will add all candles to a unique array.
*/
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile !== undefined"); }
buildBands();
return;
} else {
/*
In the case when there is no status report, we take the date of the file with the first trades as the begining of the market. Then we will
go one day further in time, so that the previous day does fine a file at the begining of the market.
*/
contextVariables.lastBandFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastBandFile = new Date(contextVariables.lastBandFile.valueOf() + ONE_DAY_IN_MILISECONDS);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> thisReport.lastFile === undefined"); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> contextVariables.lastBandFile = " + contextVariables.lastBandFile); }
buildBands();
return;
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> Entering function."); }
let n;
processDate = new Date(contextVariables.lastBandFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop.
let fromDate = new Date(processDate.valueOf())
let lastDate = new Date()
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> processDate = " + processDate); }
advanceTime();
function advanceTime() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> Entering function."); }
processDate = new Date(processDate.valueOf() + ONE_DAY_IN_MILISECONDS);
previousDay = new Date(processDate.valueOf() - ONE_DAY_IN_MILISECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> processDate = " + processDate); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> previousDay = " + previousDay); }
/* Validation that we are not going past the head of the market. */
if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) {
const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + ".";
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> advanceTime -> " + logText); }
callBackFunction(global.DEFAULT_OK_RESPONSE);
return;
}
/* Telling the world we are alive and doing well */
let currentDateString = processDate.getUTCFullYear() + '-' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processDate.getUTCDate(), 2);
let currentDate = new Date(processDate)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(currentDateString, percentage)
if (global.areEqualDates(currentDate, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
}
periodsLoop();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> advanceTime -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function periodsLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> periodsLoop -> Entering function."); }
/*
We will iterate through all posible timeFrames.
*/
n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function loopBody() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> Entering function."); }
const timeFrame = global.dailyFilePeriods[n][1];
let candles = []; // Here we will put all the candles of the 2 files read.
let previousDayFile;
let processDayFile;
getPreviousDayFile();
function getPreviousDayFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> Entering function."); }
let dateForPath = previousDay.getUTCFullYear() + '/' + utilities.pad(previousDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(previousDay.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Candles-Volumes" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onCurrentDayFileReceived);
function onCurrentDayFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> text = " + text); }
previousDayFile = JSON.parse(text);
getProcessDayFile()
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function getProcessDayFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> Entering function."); }
let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Candles-Volumes" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onCurrentDayFileReceived);
function onCurrentDayFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> text = " + text); }
processDayFile = JSON.parse(text);
buildBands();
} catch (err) {
if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) {
processDayFile = []; // we are past the head of the market, then no worries if this file is non existent.
buildBands();
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> market = " + market.baseAsset + '_' + market.quotedAsset);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> buildBands -> Entering function."); }
addCandlesToSingleArray(previousDayFile);
addCandlesToSingleArray(processDayFile);
calculateBands();
function addCandlesToSingleArray(candlesFile) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> buildBands -> addCandlesToSingleArray -> Entering function."); }
for (let i = 0; i < candlesFile.length; i++) {
let candle = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined,
direction: undefined
};
candle.min = candlesFile[i][0];
candle.max = candlesFile[i][1];
candle.open = candlesFile[i][2];
candle.close = candlesFile[i][3];
candle.begin = candlesFile[i][4];
candle.end = candlesFile[i][5];
if (candle.open > candle.close) { candle.direction = 'down'; }
if (candle.open < candle.close) { candle.direction = 'up'; }
if (candle.open === candle.close) { candle.direction = 'side'; }
candles.push(candle);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> addCandlesToSingleArray -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function calculateBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> calculateBands -> Entering function."); }
let bandsArray = [];
let pBArray = [];
let numberOfPeriodsBB = 20;
let numberOfStandardDeviations = 2;
let numberOfPeriodsPB = 10;
/* Building bands */
let band;
for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them.
/* Calculating the bollinger bands. */
let movingAverage = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the moving average.
movingAverage = movingAverage + candles[j].close;
}
movingAverage = movingAverage / numberOfPeriodsBB;
let standardDeviation = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the standard deviation.
standardDeviation = standardDeviation + Math.pow(candles[j].close - movingAverage, 2);
}
standardDeviation = standardDeviation / numberOfPeriodsBB;
standardDeviation = Math.sqrt(standardDeviation);
if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later.
band = {
begin: candles[i].begin,
end: candles[i].end,
movingAverage: movingAverage,
standardDeviation: standardDeviation,
deviation: standardDeviation * numberOfStandardDeviations
};
/* Will only add to the array the bands of the current day */
if (band.begin >= processDate.valueOf()) { bandsArray.push(band); }
/* Calculating %B */
let lowerBB;
let upperBB;
lowerBB = band.movingAverage - band.deviation;
upperBB = band.movingAverage + band.deviation;
let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100;
/* Moving Average Calculation */
let numberOfPreviousPeriods;
let currentPosition = pBArray.length;
if (currentPosition < numberOfPeriodsPB) { // Avoinding to get into negative array indexes
numberOfPreviousPeriods = currentPosition;
} else {
numberOfPreviousPeriods = numberOfPeriodsPB;
}
movingAverage = 0;
for (let j = currentPosition - numberOfPreviousPeriods; j < currentPosition; j++) { // go through the last numberOfPeriodsPBs to calculate the moving average.
movingAverage = movingAverage + pBArray[j].value;
}
movingAverage = movingAverage + value;
movingAverage = movingAverage / (numberOfPreviousPeriods + 1);
let bandwidth = (upperBB - lowerBB) / band.movingAverage;
let percentageBandwidth = {
begin: candles[i].begin,
end: candles[i].end,
value: value,
movingAverage: movingAverage,
bandwidth: bandwidth
};
/* Will only add to the array the pBs of the current day */
if (percentageBandwidth.begin >= processDate.valueOf()) { pBArray.push(percentageBandwidth); }
}
writeBandsFile(bandsArray, pBArray);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> calculateBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
}
function writeBandsFile(pBands, pPB) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < pBands.length; i++) {
let band = pBands[i];
fileContent = fileContent + separator + '[' +
band.begin + "," +
band.end + "," +
band.movingAverage + "," +
band.standardDeviation + "," +
band.deviation + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2);
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + BOLLINGER_BANDS_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> Content written = " + fileContent);
}
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + "";
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> " + logText); }
writePBFile(pPB);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writePBFile(pPercentageBandwidths) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < pPercentageBandwidths.length; i++) {
let pB = pPercentageBandwidths[i];
fileContent = fileContent + separator + '[' +
pB.begin + "," +
pB.end + "," +
pB.value + "," +
pB.movingAverage + "," +
pB.bandwidth + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let dateForPath = processDate.getUTCFullYear() + '/' + utilities.pad(processDate.getUTCMonth() + 1, 2) + '/' + utilities.pad(processDate.getUTCDate(), 2);
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> Content written = " + fileContent);
}
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
const logText = "[WARN] Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + "";
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> " + logText); }
controlLoop();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> loopBody -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function controlLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> Entering function."); }
n++;
if (n < global.dailyFilePeriods.length) {
loopBody();
} else {
n = 0;
writeDataRanges(onWritten);
function onWritten(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> onWritten -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> controlLoop -> onWritten -> err = " + err.stack);
callBackFunction(err);
return;
}
writeStatusReport(processDate, advanceTime);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> onWritten -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> err.message = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRanges(callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
writeDataRange(contextVariables.firstTradeFile, processDate, BOLLINGER_BANDS_FOLDER_NAME, onBandsBandsDataRangeWritten);
function onBandsBandsDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
writeDataRange(contextVariables.firstTradeFile, processDate, PERCENTAGE_BANDWIDTH_FOLDER_NAME, onPercentageBandwidthDataRangeWritten);
function onPercentageBandwidthDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> onPercentageBandwidthDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
callBack(global.DEFAULT_OK_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); }
let dataRange = {
begin: pBegin.valueOf(),
end: pEnd.valueOf() + ONE_DAY_IN_MILISECONDS
};
let fileContent = JSON.stringify(dataRange);
let fileName = 'Data.Range.json';
let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack);
callBack(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent);
}
callBack(global.DEFAULT_OK_RESPONSE);
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(lastFileDate, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); }
try {
let reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Daily"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err.message = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,13 +0,0 @@
{
"name": "AAToma",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Luis-Fernando-Molina",
"license": "ISC",
"dependencies": {
}
}
@@ -1,206 +0,0 @@
{
"displayName": "Bollinger Bands",
"codeName": "Bollinger-Bands",
"type": "Indicator Bot",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"dataMine": "Masters",
"profilePicture": "Bollinger Bands.png",
"processes": [
{
"name": "Multi-Period-Market",
"description": "Produces a single file for each Time Frame covering all the market.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Bollinger-Bands",
"dataSet": "Multi-Period-Market"
},
{
"product": "Percentage-Bandwidth",
"dataSet": "Multi-Period-Market"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Bollinger-Bands",
"process": "Multi-Period-Market"
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Market",
"waitUntilNextUpdate": true
}
]
},
{
"name": "Multi-Period-Daily",
"description": "Produces a Daily File at each Time Frame.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Bollinger-Bands",
"dataSet": "Multi-Period-Daily"
},
{
"product": "Percentage-Bandwidth",
"dataSet": "Multi-Period-Daily"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-Trades"
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Daily",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Bollinger-Bands",
"process": "Multi-Period-Daily"
}
]
}
],
"products": [
{
"codeName": "Bollinger-Bands",
"displayName": "Bollinger Bands",
"description": "Bollinger Bands",
"storageAccount": "Bollinger-Bands",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Bollinger-Bands/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersBollingerBands",
"moduleName": "BollingerBands"
}
},
{
"codeName": "Percentage-Bandwidth",
"displayName": "Percentage Bandwidth",
"description": "Percentage Bandwidth",
"storageAccount": "Bollinger-Bands",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Bollinger-Bands.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Percentage-Bandwidth/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersBollingerBands",
"moduleName": "PercentageBandwidth"
}
}
]
}
@@ -1 +0,0 @@
// Nothing here to run
@@ -1,812 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const GMT_MILI_SECONDS = '.000 GMT+0000';
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const CANDLES_ONE_MIN = "One-Min";
const VOLUMES_FOLDER_NAME = "Volumes";
const VOLUMES_ONE_MIN = "One-Min";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles and volumes from Exchange Raw Data and produce a file for each day and for each period with candles and volumes.
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
/* Context Variables */
let contextVariables = {
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
getContextVariables();
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); }
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS);
/* Second, we get the report from Exchange Raw Data, to know when the marted ends. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + "00:00" + GMT_SECONDS);
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS);
buildCandles();
return;
} else {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildCandles() {
try {
let fromDate = new Date(contextVariables.lastCandleFile.valueOf())
let lastDate = new Date()
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> Entering function."); }
let outputCandles;
let outputVolumes;
advanceTime();
function advanceTime() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Entering function."); }
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
/* Validation that we are not going past the head of the market. */
if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
callBackFunction(global.DEFAULT_OK_RESPONSE); // Here is where we finish processing and wait for the platform to run this module again.
return;
}
/* Telling the world we are alive and doing well */
let currentDateString = contextVariables.lastCandleFile.getUTCFullYear() + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let currentDate = new Date(contextVariables.lastCandleFile)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(currentDateString, percentage)
if (global.areEqualDates(currentDate, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
}
/*
We prepere the arrays that will accumulate all the information for each output file.
*/
outputCandles = [];
outputVolumes = [];
for (let n = 0; n < global.dailyFilePeriods.length; n++) {
const emptyArray1 = [];
const emptyArray2 = [];
outputCandles.push(emptyArray1);
outputVolumes.push(emptyArray2);
}
periodsLoop();
}
function periodsLoop() {
/*
We will iterate through all posible periods.
*/
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
function loopBody() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Entering function."); }
const outputPeriod = global.dailyFilePeriods[n][0];
const timeFrame = global.dailyFilePeriods[n][1];
nextCandleFile();
function nextCandleFile() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); }
let candlesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') {
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + err.stack);
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
}
const inputCandlesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputCandles = inputFilePeriod / outputPeriod;
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputCandles; i++) {
let outputCandle = {
open: 0,
close: 0,
min: 0,
max: 0,
begin: 0,
end: 0
};
let saveCandle = false;
outputCandle.begin = beginingOutputTime + i * outputPeriod;
outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < candlesFile.length; j++) {
let candle = {
open: candlesFile[j][2],
close: candlesFile[j][3],
min: candlesFile[j][0],
max: candlesFile[j][1],
begin: candlesFile[j][4],
end: candlesFile[j][5]
};
/* Here we discard all the candles out of range. */
if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) {
if (saveCandle === false) { // this will set the value only once.
outputCandle.open = candle.open;
outputCandle.min = candle.min;
outputCandle.max = candle.max;
}
saveCandle = true;
outputCandle.close = candle.close; // only the last one will be saved
if (candle.min < outputCandle.min) {
outputCandle.min = candle.min;
}
if (candle.max > outputCandle.max) {
outputCandle.max = candle.max;
}
}
}
if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range.
outputCandles[n].push(outputCandle);
}
}
nextVolumeFile();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function nextVolumeFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> text = " + text); }
let volumesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
const inputVolumesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputVolumes; i++) {
let outputVolume = {
buy: 0,
sell: 0,
begin: 0,
end: 0
};
let saveVolume = false;
outputVolume.begin = beginingOutputTime + i * outputPeriod;
outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < volumesFile.length; j++) {
let volume = {
buy: volumesFile[j][0],
sell: volumesFile[j][1],
begin: volumesFile[j][2],
end: volumesFile[j][3]
};
/* Here we discard all the Volumes out of range. */
if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) {
saveVolume = true;
outputVolume.buy = outputVolume.buy + volume.buy;
outputVolume.sell = outputVolume.sell + volume.sell;
}
}
if (saveVolume === true) {
outputVolumes[n].push(outputVolume);
}
}
writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> Entering function."); }
n++;
if (n < global.dailyFilePeriods.length) {
loopBody();
} else {
writeDataRanges(onWritten);
function onWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> onWritten -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
writeStatusReport(contextVariables.lastCandleFile, advanceTime);
}
}
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeFiles(candles, volumes, timeFrame, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> Entering function."); }
/*
Here we will write the contents of the Candles and Volumens files.
*/
try {
writeCandles();
function writeCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < candles.length; i++) {
let candle = candles[i];
fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
writeVolumes();
}
}
function writeVolumes() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < volumes.length; i++) {
let candle = volumes[i];
fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
callBack();
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRanges(callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, CANDLES_FOLDER_NAME, onCandlesDataRangeWritten);
function onCandlesDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRanges -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, VOLUMES_FOLDER_NAME, onVolumeDataRangeWritten);
function onVolumeDataRangeWritten(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] writeDataRanges -> writeDataRanges -> onVolumeDataRangeWritten -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] writeDataRanges -> writeDataRanges -> onVolumeDataRangeWritten -> err = " + err.stack);
callBack(err);
return;
}
callBack(global.DEFAULT_OK_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRanges -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> Entering function."); }
let dataRange = {
begin: pBegin.valueOf(),
end:pEnd.valueOf() + ONE_DAY_IN_MILISECONDS
};
let fileContent = JSON.stringify(dataRange);
let fileName = 'Data.Range.json';
let filePath = bot.filePathRoot + "/Output/" + pProductFolder + "/" + bot.process;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack);
callBack(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeDataRange -> onFileCreated -> Content written = " + fileContent);
}
let key = bot.dataMine + "-" + bot.codeName + "-" + pProductFolder + "-" + bot.exchange + "-" + bot.market.baseAsset + '/' + bot.market.quotedAsset
let event = {
dateRange: dataRange
}
global.EVENT_SERVER_CLIENT.raiseEvent(key, 'Data Range Updated', event)
callBack(global.DEFAULT_OK_RESPONSE);
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeDataRange -> err = " + err.stack);
callBack(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(lastFileDate, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); }
try {
let reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] 'Start' - ERROR : " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,929 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const GMT_MILI_SECONDS = '.000 GMT+0000';
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const CANDLES_ONE_MIN = "One-Min";
const VOLUMES_FOLDER_NAME = "Volumes";
const VOLUMES_ONE_MIN = "One-Min";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles and volumes from Exchange Raw Data and produce a single Index File for Market Period. But this is the situation:
Exchange Raw Data has a dataset organized with daily files with candles of 1 min. Candles Volumes is writting in this process a single file for each timeFrame for the whole market.
Everytime this process run, must be able to resume its job and process everything pending until reaching the head of the market. So the tactic to do this is the
following:
1. First we need to read the last file written by this process, and load all the information into in-memory arrays. We will then append to this arrays the new
information we will get from Exchange Raw Data.
2. We know from out status report which was the last DAY we processed from Exchange Raw Data, but we must be carefull, because that day mightn not have been complete, if the
last run found the head of the market. That means that we have to be carefull not to append candles that are already there. To simplify what we do is to discard
all candles of the last processed day, and then we can process that full day again adding all the candles.
*/
function start(callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
/* Context Variables */
let contextVariables = {
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
getContextVariables();
function getContextVariables() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Entering function."); }
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.firstTradeFile = new Date(thisReport.beginingOfMarket.year + "-" + thisReport.beginingOfMarket.month + "-" + thisReport.beginingOfMarket.days + " " + thisReport.beginingOfMarket.hours + ":" + thisReport.beginingOfMarket.minutes + GMT_SECONDS);
/* Second, we get the report from Exchange Raw Data, to know when the marted ends. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: global.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxCandleFile = new Date(thisReport.lastFile.year + "-" + thisReport.lastFile.month + "-" + thisReport.lastFile.days + " " + "00:00" + GMT_SECONDS);
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Market"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
logger.write(MODULE_NAME, "[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS);
findPreviousContent();
return;
} else {
logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + GMT_SECONDS);
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles();
return;
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> Double check that you are not running a process that only can be run at noTime mode at a certain month when it is not prepared to do so.");
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function findPreviousContent() {
/*
This is where we read the current files we have produced at previous runs of this same process. We just read all the content and organize it
in arrays and keep them in memory.
*/
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> Entering function."); }
let n = 0 // loop Variable representing each possible period as defined at the periods array.
let allPreviousCandles = [];
let allPreviousVolumes = [];
loopBody();
function loopBody() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> Entering function."); }
let timeFrame = global.marketFilesPeriods[n][1];
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> timeFrame = " + timeFrame); }
let previousCandles;
let previousVolumes;
getCandles();
function getCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> Entering function."); }
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> getting file.");
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> text = " + text); }
let candlesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
previousCandles = candlesFile;
getVolumes();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> fileName = " + fileName);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
}
}
}
function getVolumes() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> Entering function."); }
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> getting file.");
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> text = " + text); }
let volumesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
previousVolumes = volumesFile;
allPreviousCandles.push(previousCandles);
allPreviousVolumes.push(previousVolumes);
controlLoop();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> fileName = " + fileName);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
}
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> findPreviousContent -> controlLoop -> Entering function."); }
n++;
if (n < global.marketFilesPeriods.length) {
loopBody();
} else {
buildCandles(allPreviousCandles, allPreviousVolumes);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> findPreviousContent -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function buildCandles(allPreviousCandles, allPreviousVolumes) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> Entering function."); }
let fromDate = new Date(contextVariables.lastCandleFile.valueOf())
let lastDate = new Date()
/*
Firstly we prepere the arrays that will accumulate all the information for each output file.
*/
let outputCandles = [];
let outputVolumes = [];
for (let n = 0; n < global.marketFilesPeriods.length; n++) {
const emptyArray1 = [];
const emptyArray2 = [];
outputCandles.push(emptyArray1);
outputVolumes.push(emptyArray2);
}
advanceTime();
function advanceTime() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Entering function."); }
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + ONE_DAY_IN_MILISECONDS);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
/* Validation that we are not going past the head of the market. */
if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + "."); }
callBackFunction(global.DEFAULT_OK_RESPONSE); // Here is where we finish processing and wait for the platform to run this module again.
return;
}
/* Telling the world we are alive and doing well */
let currentDateString = contextVariables.lastCandleFile.getUTCFullYear() + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '-' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let currentDate = new Date(contextVariables.lastCandleFile)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(currentDateString, percentage)
if (global.areEqualDates(currentDate, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
}
periodsLoop();
}
function periodsLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> Entering function."); }
/*
We will iterate through all posible periods.
*/
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
function loopBody() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Entering function."); }
let previousCandles;
let previousVolumes;
if (allPreviousCandles !== undefined) {
previousCandles = allPreviousCandles[n];
previousVolumes = allPreviousVolumes[n];
}
const outputPeriod = global.marketFilesPeriods[n][0];
const timeFrame = global.marketFilesPeriods[n][1];
/*
Here we are inside a Loop that is going to advance 1 day at the time, at each pass, will ready one of Exchange Raw Data's daily files and
add all its candles to our in memory arrays. At the first iteration of this loop, we will add the candles that we are carrying
from our previous run, the ones we already have in-memory. You can see below how we discard from those candles the ones that
are belonging to the first day we are processing at this run, that it is exactly the same as the last day processed the privious
run. By discarding these candles, we are ready to run after that standard function that will just add ALL the candles found each
day at Exchange Raw Data.
*/
if (previousCandles !== undefined) {
for (let i = 0; i < previousCandles.length; i++) {
let candle = {
open: previousCandles[i][2],
close: previousCandles[i][3],
min: previousCandles[i][0],
max: previousCandles[i][1],
begin: previousCandles[i][4],
end: previousCandles[i][5]
};
if (candle.end < contextVariables.lastCandleFile.valueOf()) {
outputCandles[n].push(candle);
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Candle # " + i + " @ " + timeFrame + " discarded for closing past the current process time."); }
}
}
allPreviousCandles[n] = []; // erasing these so as not to duplicate them.
}
if (previousVolumes !== undefined) {
for (let i = 0; i < previousVolumes.length; i++) {
let volume = {
begin: previousVolumes[i][2],
end: previousVolumes[i][3],
buy: previousVolumes[i][0],
sell: previousVolumes[i][1]
};
if (volume.end < contextVariables.lastCandleFile.valueOf()) {
outputVolumes[n].push(volume);
} else {
if (FULL_LOG === true) {logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Volume # " + i + " @ " + timeFrame + " discarded for closing past the current process time."); }
}
}
allPreviousVolumes[n] = []; // erasing these so as not to duplicate them.
}
/*
From here on is where every iteration of the loop fully runs. Here is where we read Exchange Raw Data's files and add their content to whatever
we already have in our arrays in-memory. In this way the process will run as many days needed and it should only stop when it reaches
the head of the market.
*/
nextCandleFile();
function nextCandleFile() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); }
let candlesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') {
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + JSON.stringify(err));
logger.write(MODULE_NAME, "[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
}
const inputCandlesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputCandles = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
/*
The algorithm that follows is going to agregate candles of 1 min timeFrame read from Exchange Raw Data, into candles of each timeFrame
that Candles Volumes generates. For market files those timePediods goes from 1h to 24hs.
*/
for (let i = 0; i < totalOutputCandles; i++) {
let outputCandle = {
open: 0,
close: 0,
min: 0,
max: 0,
begin: 0,
end: 0
};
let saveCandle = false;
outputCandle.begin = beginingOutputTime + i * outputPeriod;
outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < candlesFile.length; j++) {
let candle = {
open: candlesFile[j][2],
close: candlesFile[j][3],
min: candlesFile[j][0],
max: candlesFile[j][1],
begin: candlesFile[j][4],
end: candlesFile[j][5]
};
/* Here we discard all the candles out of range. */
if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) {
if (saveCandle === false) { // this will set the value only once.
outputCandle.open = candle.open;
outputCandle.min = candle.min;
outputCandle.max = candle.max;
}
saveCandle = true;
outputCandle.close = candle.close; // only the last one will be saved
if (candle.min < outputCandle.min) {
outputCandle.min = candle.min;
}
if (candle.max > outputCandle.max) {
outputCandle.max = candle.max;
}
}
}
if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range.
outputCandles[n].push(outputCandle);
}
}
nextVolumeFile();
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function nextVolumeFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> Entering function."); }
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + utilities.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Exchange-Raw-Data" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> text = " + text); }
let volumesFile;
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
const inputVolumesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputVolumes; i++) {
let outputVolume = {
buy: 0,
sell: 0,
begin: 0,
end: 0
};
let saveVolume = false;
outputVolume.begin = beginingOutputTime + i * outputPeriod;
outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < volumesFile.length; j++) {
let volume = {
buy: volumesFile[j][0],
sell: volumesFile[j][1],
begin: volumesFile[j][2],
end: volumesFile[j][3]
};
/* Here we discard all the Volumes out of range. */
if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) {
saveVolume = true;
outputVolume.buy = outputVolume.buy + volume.buy;
outputVolume.sell = outputVolume.sell + volume.sell;
}
}
if (saveVolume === true) {
outputVolumes[n].push(outputVolume);
}
}
writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
function controlLoop() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> Entering function."); }
n++;
if (n < global.marketFilesPeriods.length) {
loopBody();
} else {
writeStatusReport(contextVariables.lastCandleFile, advanceTime);
}
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildCandles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeFiles(candles, volumes, timeFrame, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> Entering function."); }
/*
Here we will write the contents of the Candles and Volumens files.
*/
try {
writeCandles();
function writeCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < candles.length; i++) {
let candle = candles[i];
fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName );
writeVolumes();
}
}
function writeVolumes() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < volumes.length; i++) {
let candle = volumes[i];
fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + "/" + bot.process + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Entering function."); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
if (LOG_FILE_CONTENT === true) {
logger.write(MODULE_NAME, "[INFO] start -> writeFiles -> writeVolumes -> onFileCreated -> Content written = " + fileContent);
}
logger.write(MODULE_NAME, "[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + market.baseAsset + "_" + market.quotedAsset + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
callBack();
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeFiles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(lastFileDate, callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate); }
try {
let reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Market"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,12 +0,0 @@
{
"name": "AAOlivia",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Luis-Fernando-Molina",
"license": "ISC",
"dependencies": {}
}
@@ -1,204 +0,0 @@
{
"displayName": "Candles Volumes",
"codeName": "Candles-Volumes",
"type": "Indicator Bot",
"dataMine": "Masters",
"processes": [
{
"name": "Multi-Period-Market",
"description": "Produces different files with multi-period candles and volumes for entire markets.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Candles",
"dataSet": "Multi-Period-Market"
},
{
"product": "Volumes",
"dataSet": "Multi-Period-Market"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs",
"dataSetSection": "Month",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Market"
}
]
},
{
"name": "Multi-Period-Daily",
"description": "Produces different files with multi-period candles and volumes on a daily format.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "true"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"updatesDatasets": [
{
"product": "Candles",
"dataSet": "Multi-Period-Daily"
},
{
"product": "Volumes",
"dataSet": "Multi-Period-Daily"
}
],
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-OHLCVs",
"dataSetSection": "Month",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Candles-Volumes",
"process": "Multi-Period-Daily"
}
]
}
],
"products": [
{
"codeName": "Candles",
"displayName": "Candles",
"description": "Two sets of files per period that contains candles.",
"storageAccount": "Candles-Volumes",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Candles/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersCandlesVolumes",
"moduleName": "Candles"
}
},
{
"codeName": "Volumes",
"displayName": "Buy and Sell Volumes",
"description": "Two sets of files per period that contains buy and sell volumes.",
"storageAccount": "Candles-Volumes",
"shareWith": "Public",
"dataSets": [
{
"codeName": "Multi-Period-Market",
"type": "Market Files",
"validPeriods": [ "24-hs", "12-hs", "08-hs", "06-hs", "04-hs", "03-hs", "02-hs", "01-hs" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ],
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "Masters/Candles-Volumes.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Volumes/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"dataMine": "Masters",
"codeName": "PlottersCandlesVolumes",
"moduleName": "Volumes"
}
}
]
}
@@ -1,8 +0,0 @@
exports.newCommons = function newCommons(bot, logger, UTILITIES) {
let thisObject = {
};
return thisObject;
};
@@ -1,501 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE, STATUS_REPORT, EXCHANGE_API) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const GMT_SECONDS = ':00.000 GMT+0000';
const GMT_MILI_SECONDS = '.000 GMT+0000';
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger)
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies
const ONE_MINUTE = 60000
const MAX_TRADES_PER_EXECUTION = 100000
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
const ccxt = require('ccxt')
let allTrades = []
let thisReport;
let since
let initialProcessTimestamp
let beginingOfMarket
let lastFileSaved
let exchangeId
let options = {}
let fetchType = "by Time"
let lastId
let firstId
let rateLimit
let exchange
let uiStartDate = new Date(bot.uiStartDate)
const limit = 1000
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
statusDependencies = pStatusDependencies;
exchangeId = bot.exchange.toLowerCase()
/* Applying the parameters defined by the user at the Exchange node */
if (bot.exchangeNode.config.API !== undefined) {
for (let i = 0; i < bot.exchangeNode.config.API.length; i++) {
if (bot.exchangeNode.config.API[i].method === 'fetchTrades') {
if (bot.exchangeNode.config.API[i].class !== undefined) {
exchangeId = bot.exchangeNode.config.API[i].class
}
if (bot.exchangeNode.config.API[i].fetchType !== undefined) {
fetchType = bot.exchangeNode.config.API[i].fetchType
}
if (bot.exchangeNode.config.API[i].fetchTradesMethod !== undefined) {
options = {
'fetchTradesMethod': bot.exchangeNode.config.API[i].fetchTradesMethod
}
}
if (bot.exchangeNode.config.API[i].firstId !== undefined) {
firstId = bot.exchangeNode.config.API[i].firstId
}
if (bot.exchangeNode.config.API[i].rateLimit !== undefined) {
rateLimit = bot.exchangeNode.config.API[i].rateLimit
}
}
}
}
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
}
if (rateLimit !== undefined) {
exchangeConstructorParams.rateLimit = rateLimit
}
exchange = new exchangeClass(exchangeConstructorParams)
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function start(callBackFunction) {
try {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
let abort = false
begin()
async function begin() {
getContextVariables()
if (abort === true) { return }
await getFirstId()
await getTrades()
if (abort === true) { return}
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
await saveTrades()
}
function getContextVariables() {
try {
let reportKey;
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-Trades"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey)
if (thisReport.file.beginingOfMarket !== undefined) { // This means this is not the first time this process run.
beginingOfMarket = new Date(thisReport.file.beginingOfMarket.year + "-" + thisReport.file.beginingOfMarket.month + "-" + thisReport.file.beginingOfMarket.days + " " + thisReport.file.beginingOfMarket.hours + ":" + thisReport.file.beginingOfMarket.minutes + GMT_SECONDS);
lastFileSaved = new Date(thisReport.file.lastFileSaved.year + "-" + thisReport.file.lastFileSaved.month + "-" + thisReport.file.lastFileSaved.days + " " + thisReport.file.lastFileSaved.hours + ":" + thisReport.file.lastFileSaved.minutes + GMT_SECONDS);
lastId = thisReport.file.lastId
} else { // This means this is the first time this process run.
beginingOfMarket = new Date(uiStartDate.valueOf())
}
defineSince()
function defineSince() {
if (thisReport.file.uiStartDate === undefined) {
thisReport.file.uiStartDate = uiStartDate
} else {
thisReport.file.uiStartDate = new Date(thisReport.file.uiStartDate)
}
if (uiStartDate.valueOf() !== thisReport.file.uiStartDate.valueOf()) {
since = uiStartDate.valueOf()
initialProcessTimestamp = since
beginingOfMarket = new Date(uiStartDate.valueOf())
} else {
if (lastFileSaved !== undefined) {
since = lastFileSaved.valueOf()
initialProcessTimestamp = lastFileSaved.valueOf()
} else {
since = uiStartDate.valueOf()
initialProcessTimestamp = uiStartDate.valueOf()
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot Status Dependencies. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
async function getFirstId() {
try {
/* We need the first id only when we are going to fetch trades based on id and it is the first time the process runs*/
if (fetchType !== "by Id") { return }
if (lastId !== undefined) { return }
const limit = 1
const exchangeClass = ccxt[exchangeId]
const exchange = new exchangeClass({
'timeout': 30000,
'enableRateLimit': true,
verbose: false
})
const trades = await exchange.fetchTrades(symbol, since, limit, undefined)
let lastRecord = trades[trades.length - 1]
lastId = lastRecord.info[firstId]
} catch (err) {
/* If something fails trying to get an id close to since, we just will continue without an id.*/
}
}
async function getTrades() {
try {
let lastTradeKey = ''
let params = undefined
let previousSince
while (true) {
/* Reporting we are doing well */
let processingDate = new Date(since)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Fetching Trades @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit ) }
bot.processHeartBeat("Fetching " + allTrades.length.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
if (fetchType === "by Id") {
params = {
'fromId': lastId
}
since = undefined
}
/* Fetching the trades from the exchange.*/
const trades = await exchange.fetchTrades(symbol, since, limit, params)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched = " + trades.length) }
if (trades.length > 0) {
let beginDate = new Date(trades[0].timestamp)
let endDate = new Date(trades[trades.length - 1].timestamp)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched From " + beginDate) }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched to " + endDate) }
}
if (trades.length > 1) {
previousSince = since
since = trades[trades.length - 1]['timestamp']
if (since === previousSince) {
since++ // this prevents requesting in a loop trades with the same timestamp, that can happen when all the records fetched comes with exactly the same timestamp.
}
lastId = trades[trades.length - 1]['id']
for (let i = 0; i < trades.length; i++) {
let trade = trades[i]
let tradeKey = trade.id + '-' + trade.timestamp + '-' + trade.side + '-' + trade.price.toFixed(16) + '-' + trade.amount.toFixed(16)
if (tradeKey !== lastTradeKey) {
allTrades.push([trade.timestamp, trade.side, trade.price, trade.amount, trade.id])
}
lastTradeKey = tradeKey
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Fetching Trades @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit) }
bot.processHeartBeat("Fetching " + allTrades.length.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
}
if (
trades.length < limit - 1 ||
global.STOP_TASK_GRACEFULLY === true ||
allTrades.length >= MAX_TRADES_PER_EXECUTION
) {
break
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getTrades -> Retrying Later -> err = " + err.stack);
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
abort = true
}
}
async function saveTrades() {
try {
let fileContent = '['
let previousRecordMinute = Math.trunc((initialProcessTimestamp - ONE_MINUTE) / ONE_MINUTE)
let currentRecordMinute
let needSeparator = false
let error
let separator
let heartBeatCounter = 0
let i = -1
lastId = undefined
controlLoop()
function loop() {
let filesToCreate = 0
let filesCreated = 0
let record = allTrades[i]
let trade = {
timestamp: record[0],
side: record[1],
price: record[2],
amount: record[3]
}
if (lastId === undefined) {
lastId = record[4]
}
let processingDate = new Date(trade.timestamp)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
/* Reporting we are doing well */
heartBeatCounter--
if (heartBeatCounter <= 0) {
heartBeatCounter = 1000
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveTrades -> Saving Trades @ " + processingDate + " -> i = " + i + " -> total = " + allTrades.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
}
/* Saving the trades in Files*/
currentRecordMinute = Math.trunc(trade.timestamp / ONE_MINUTE)
if (
currentRecordMinute !== previousRecordMinute
) {
/* There are no more trades at this minute or it is the last trade, so we save the file.*/
saveFile(previousRecordMinute)
lastId = undefined
}
if (needSeparator === false) {
needSeparator = true;
separator = '';
} else {
separator = ',';
}
/* Add the trade to the file content.*/
fileContent = fileContent + separator + '[' + trade.timestamp + ',"' + trade.side + '",' + trade.price + ',' + trade.amount + ']';
if (i === allTrades.length - 1) {
/* This is the last trade, so we save the file.*/
saveFile(currentRecordMinute)
/* It might happen that there are several minutes after the last trade without trades. We need to record empty files for them.*/
if (allTrades.length < MAX_TRADES_PER_EXECUTION) {
let currentTimeMinute = Math.trunc((new Date()).valueOf() / ONE_MINUTE)
if (currentTimeMinute - currentRecordMinute > 1) {
createMissingEmptyFiles(currentRecordMinute, currentTimeMinute)
}
}
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveTrades -> Saving Trades @ " + processingDate + " -> i = " + i + " -> total = " + allTrades.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
return
}
previousRecordMinute = currentRecordMinute
if (error) {
callBackFunction(error);
return;
}
if (filesToCreate === 0) {
controlLoop()
}
function saveFile(minute) {
fileContent = fileContent + ']'
if (currentRecordMinute - previousRecordMinute > 1) {
createMissingEmptyFiles(previousRecordMinute, currentRecordMinute)
}
let fileName = 'Data.json'
if (previousRecordMinute >= initialProcessTimestamp / ONE_MINUTE) {
filesToCreate++
fileStorage.createTextFile(getFilePath(minute * ONE_MINUTE) + '/' + fileName, fileContent + '\n', onFileCreated);
}
fileContent = '['
needSeparator = false
}
function createMissingEmptyFiles(begin, end) {
/*
If this range is too wide, we will consider this means that the begin is before the begining of this market at this exchange.
In that case we will change the begin and the beginingOfMarket
*/
if ((end - begin) / 60 / 24 > 7) {
begin = end
beginingOfMarket = new Date(end * ONE_MINUTE)
}
for (let j = begin + 1; j < end; j++) {
let fileName = 'Data.json'
filesToCreate++
fileStorage.createTextFile(getFilePath(j * ONE_MINUTE) + '/' + fileName, "[]" + '\n', onFileCreated);
}
}
function onFileCreated(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> tradesReadyToBeSaved -> onFileBCreated -> err = " + JSON.stringify(err));
error = err // This allows the loop to be breaked.
return;
}
filesCreated++
lastFileSaved = new Date((currentRecordMinute * ONE_MINUTE))
if (filesCreated === filesToCreate) {
controlLoop()
}
}
function getFilePath(timestamp) {
let datetime = new Date(timestamp)
let dateForPath = datetime.getUTCFullYear() + '/' +
utilities.pad(datetime.getUTCMonth() + 1, 2) + '/' +
utilities.pad(datetime.getUTCDate(), 2) + '/' +
utilities.pad(datetime.getUTCHours(), 2) + '/' +
utilities.pad(datetime.getUTCMinutes(), 2)
let filePath = bot.filePathRoot + "/Output/" + TRADES_FOLDER_NAME + '/' + dateForPath;
return filePath
}
}
function controlLoop() {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
i++
if (i < allTrades.length) {
setImmediate(loop)
} else {
writeStatusReport()
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> saveTrades -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
function writeStatusReport() {
try {
if (lastFileSaved === undefined) {return}
thisReport.file = {
lastFileSaved: {
year: lastFileSaved.getUTCFullYear(),
month: (lastFileSaved.getUTCMonth() + 1),
days: lastFileSaved.getUTCDate(),
hours: lastFileSaved.getUTCHours(),
minutes: lastFileSaved.getUTCMinutes()
},
beginingOfMarket: {
year: beginingOfMarket.getUTCFullYear(),
month: (beginingOfMarket.getUTCMonth() + 1),
days: beginingOfMarket.getUTCDate(),
hours: beginingOfMarket.getUTCHours(),
minutes: beginingOfMarket.getUTCMinutes()
},
uiStartDate: uiStartDate.toUTCString()
};
if (fetchType === "by Id") {
thisReport.file.lastId = lastId
}
thisReport.save(onSaved);
function onSaved(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,5 +0,0 @@
{
"name": "AACharly",
"version": "1.0.0",
"lockfileVersion": 1
}
@@ -1,12 +0,0 @@
{
"name": "AACharly",
"version": "1.0.0",
"description": "Processes that get the trades from Poloniex.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Luis-Fernando-Molina",
"license": "ISC",
"dependencies": {}
}
@@ -1,139 +0,0 @@
{
"displayName": "Exchange Raw Data",
"codeName": "Exchange-Raw-Data",
"type": "Sensor Bot",
"dataMine": "Masters",
"profilePicture": "Exchange Raw Data.png",
"processes": [
{
"name": "Live-Trades",
"description": "Retrieves the trades done at the current and the previous minute and saves them at the storage account.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "true",
"interval": 60000
}
},
"deadWaitTime": 0,
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Live-Trades"
}
]
},
{
"name": "Historic-Trades",
"description": "Retrieves and saves the historical trades in batches going backwards from the current time until reaching the begining of the market.",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "2014",
"maxYear": "2020"
},
"oneMonth": {
"run": "true",
"year": "2018",
"month": "01"
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Live-Trades"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-Trades"
}
]
},
{
"name": "Hole-Fixing",
"description": "Scans the trades saved by the Live Trades and Historic Trades processes searching for missing records. Once a hole is found on the data set, it patches it by retrieving the missing records from the exchange.",
"startMode": {
"allMonths": {
"run": "true",
"minYear": "2019",
"maxYear": "2019"
},
"oneMonth": {
"run": "false",
"year": "2019",
"month": "10"
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "false",
"interval": 0
}
},
"deadWaitTime": 0,
"normalWaitTime": 0,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Live-Trades",
"waitUntilNextUpdate": true
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Historic-Trades"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Hole-Fixing",
"dataSetSection": "Month"
},
{
"dataMine": "Masters",
"bot": "Exchange-Raw-Data",
"process": "Hole-Fixing"
}
]
}
]
}
@@ -1,190 +0,0 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE, STATUS_REPORT, EXCHANGE_API) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const MODULE_NAME = "User Bot";
const FOLDER_NAME = "External-Signals";
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger)
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let statusDependencies
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function start(callBackFunction) {
try {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
return
}
let fileContent
getContextVariables(saveMessages)
function getContextVariables(callBack) {
try {
let reportKey;
reportKey = "Masters" + "-" + "Webhooks" + "-" + "Check-Webhook"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey)
if (thisReport.file.lasrRun !== undefined) {
let fileName = 'Data.json'
let filePath = bot.filePathRoot + "/Output/" + FOLDER_NAME + "/" + 'Single-File'
fileStorage.getTextFile(filePath + '/' + fileName, onFileReceived);
function onFileReceived(err, text) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> onFileReceived -> Could read file. -> filePath = " + filePath + "/" + fileName);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
} else {
fileContent = text
callBack()
}
}
} else { // If there is no status report, we assume there is no previous file or that if there is we will override it.
callBack()
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot Status Dependencies. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
function saveMessages() {
try {
let http = require('http');
let url = 'http://' + process.env.WEB_SERVER_URL +
':' + process.env.HTTP_INTERFACE_PORT +
'/Webhook/Fetch-Messages/' +
bot.exchange + "/" +
bot.market.baseAsset + "-" + bot.market.quotedAsset
http.get(url, onResponse);
function onResponse(response) {
const chunks = []
response.on('data', onMessegesArrived)
response.on('end', onEnd)
function onMessegesArrived(chunk) {
chunks.push(chunk)
}
function onEnd() {
let messages = Buffer.concat(chunks).toString('utf8')
if (fileContent !== undefined) {
// we are going to append the curernt messages to the existing file.
let fileContentArray = JSON.parse(fileContent)
let messagesArray = JSON.parse(messages)
for (let i = 0; i < messagesArray.length; i++) {
let message = messagesArray[i]
fileContentArray.push(message)
}
fileContent = JSON.stringify(fileContentArray)
} else {
// we are going to save the current messages.
fileContent = messages
}
let fileName = 'Data.json'
let filePath = bot.filePathRoot + "/Output/" + FOLDER_NAME + "/" + 'Single-File'
fileStorage.createTextFile(filePath + '/' + fileName, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> saveMessages -> onResponse -> onEnd -> onFileCreated -> Could not save file. -> filePath = " + filePath + "/" + fileName);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
} else {
writeStatusReport()
}
}
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> saveMessages -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
function writeStatusReport() {
try {
thisReport.file = {
lasrRun: (new Date()).toISOString()
};
thisReport.save(onSaved);
function onSaved(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,8 +0,0 @@
exports.newCommons = function newCommons(bot, logger, UTILITIES) {
let thisObject = {
};
return thisObject;
};
@@ -1,5 +0,0 @@
{
"name": "AACharly",
"version": "1.0.0",
"lockfileVersion": 1
}
@@ -1,9 +0,0 @@
{
"name": "webhooks-sensor-bot",
"version": "1.0.0",
"description": "",
"main": "",
"author": "Luis-Fernando-Molina",
"license": "",
"dependencies": {}
}
@@ -1,51 +0,0 @@
{
"displayName": "Webhooks",
"codeName": "Webhooks",
"type": "Sensor Bot",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"dataMine": "Masters",
"processes": [
{
"name": "Check-Webhook",
"description": "",
"startMode": {
"allMonths": {
"run": "false",
"minYear": "",
"maxYear": ""
},
"oneMonth": {
"run": "false",
"year": "",
"month": ""
},
"noTime": {
"run": "false"
},
"fixedInterval": {
"run": "true",
"interval": 60000
}
},
"deadWaitTime": 0,
"normalWaitTime": 30000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"statusDependencies": [
{
"dataMine": "Masters",
"bot": "Webhooks",
"process": "Check-Webhook"
}
]
}
]
}
@@ -4,7 +4,7 @@
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
@@ -4,7 +4,7 @@
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
@@ -4,7 +4,7 @@
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
initialize: initialize,
@@ -4,7 +4,7 @@
const ERROR_LOG = true;
const INTENSIVE_LOG = false;
const logger = newWebDebugLog();
logger.fileName = MODULE_NAME;
let thisObject = {
@@ -1,572 +0,0 @@
function newSuperalgosFunctionLibraryCryptoEcosystemFunctions() {
thisObject = {
addMissingExchanges: addMissingExchanges,
addMissingAssets: addMissingAssets,
addMissingMarkets: addMissingMarkets,
installMarket: installMarket,
uninstallMarket: uninstallMarket
}
return thisObject
function addMissingExchanges(node) {
currentExchanges = new Map()
let parent = node.payload.parentNode
if (parent !== undefined) {
for (let i = 0; i < parent.cryptoExchanges.length; i++) {
let cryptoExchanges = parent.cryptoExchanges[i]
for (let j = 0; j < cryptoExchanges.exchanges.length; j++) {
let exchange = cryptoExchanges.exchanges[j]
let codeName = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(exchange.payload, 'codeName')
currentExchanges.set(codeName, exchange)
}
}
}
let params = {
method: 'listExchanges',
has: {
fetchOHLCV: true,
fetchMarkets: true
}
}
httpRequest(JSON.stringify(params), 'CCXT', onResponse)
function onResponse(err, data) {
if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
return
}
let exchanges = JSON.parse(data)
for (let i = 0; i < exchanges.length; i++) {
let exchange = exchanges[i]
let existingExchange = currentExchanges.get(exchange.id)
if (existingExchange === undefined) {
let newExchange = UI.projects.superalgos.functionLibraries.uiObjectsFromNodes.addUIObject(node, 'Crypto Exchange')
newExchange.name = exchange.name
newExchange.config = '{ \n\"codeName\": \"' + exchange.id + '\"\n}'
newExchange.payload.floatingObject.collapseToggle()
newExchange.exchangeAssets.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
newExchange.exchangeMarkets.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
newExchange.exchangeAccounts.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
newExchange.exchangeAssets.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_050X
newExchange.exchangeMarkets.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_100X
newExchange.exchangeAccounts.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_025X
newExchange.exchangeAssets.payload.floatingObject.arrangementStyle = ARRANGEMENT_STYLE.CONCAVE
newExchange.exchangeMarkets.payload.floatingObject.arrangementStyle = ARRANGEMENT_STYLE.CONCAVE
newExchange.exchangeAccounts.payload.floatingObject.arrangementStyle = ARRANGEMENT_STYLE.CONCAVE
}
}
}
}
function addMissingAssets(node) {
if (node.payload.parentNode === undefined) { return }
let currentAssets = new Map()
for (let j = 0; j < node.assets.length; j++) {
let asset = node.assets[j]
let codeName = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(asset.payload, 'codeName')
currentAssets.set(codeName, asset)
}
let exchangeId = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(node.payload.parentNode.payload, 'codeName')
try {
let params = {
exchangeId: exchangeId,
method: 'fetchMarkets'
}
httpRequest(JSON.stringify(params), 'CCXT', onResponse)
function onResponse(err, data) {
if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
return
}
let queryParams = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(node.payload, 'addMissingAssetsFilter')
let markets = JSON.parse(data)
let totalAdded = 0
for (let i = 0; i < markets.length; i++) {
let market = markets[i]
if (queryParams !== undefined) {
if (queryParams.baseAsset !== undefined) {
if (market.base.indexOf(queryParams.baseAsset) < 0) {
continue
}
}
if (queryParams.quotedAsset !== undefined) {
if (market.quote.indexOf(queryParams.quotedAsset) < 0) {
continue
}
}
}
if (currentAssets.get(market.base) === undefined) {
addAsset(market.base)
totalAdded++
currentAssets.set(market.base, market.base)
}
if (currentAssets.get(market.quote) === undefined) {
addAsset(market.quote)
totalAdded++
currentAssets.set(market.quote, market.quote)
}
function addAsset(name) {
let newAsseet = UI.projects.superalgos.functionLibraries.uiObjectsFromNodes.addUIObject(node, 'Asset')
newAsseet.name = name
newAsseet.config = '{ \n\"codeName\": \"' + name + '\"\n}'
}
}
node.payload.uiObject.setInfoMessage(node.payload.parentNode.name + ' supports ' + markets.length + ' markets. Applying the filters at this node config ' + node.config + ' this process added ' + totalAdded + ' assets.', 4)
}
} catch (err) {
node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
console.log(err.stack)
}
}
function addMissingMarkets(node) {
if (node.payload.parentNode === undefined) { return }
if (node.payload.parentNode.exchangeAssets === undefined) { return }
if (node.payload.parentNode.payload.parentNode === undefined) { return }
if (node.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { return }
let currentAssets = new Map()
let exchangeAssets = node.payload.parentNode.exchangeAssets
for (let j = 0; j < exchangeAssets.assets.length; j++) {
let asset = exchangeAssets.assets[j]
let codeName = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(asset.payload, 'codeName')
currentAssets.set(codeName, asset)
}
let currentMarkets = new Map()
let exchangeMarkets = node
for (let j = 0; j < exchangeMarkets.markets.length; j++) {
let asset = exchangeMarkets.markets[j]
let codeName = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(asset.payload, 'codeName')
currentMarkets.set(codeName, asset)
}
let exchangeId = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(node.payload.parentNode.payload, 'codeName')
try {
let params = {
exchangeId: exchangeId,
method: 'fetchMarkets'
}
httpRequest(JSON.stringify(params), 'CCXT', onResponse)
function onResponse(err, data) {
if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
return
}
let markets = JSON.parse(data)
for (let i = 0; i < markets.length; i++) {
let market = markets[i]
let baseAsset = currentAssets.get(market.base)
let quotedAsset = currentAssets.get(market.quote)
if (baseAsset === undefined) {
continue
}
if (quotedAsset === undefined) {
continue
}
if (currentMarkets.get(market.symbol) === undefined) {
addMarket(market.symbol, baseAsset, quotedAsset)
}
function addMarket(name, baseAsset, quotedAsset) {
let newMarket = UI.projects.superalgos.functionLibraries.uiObjectsFromNodes.addUIObject(node, 'Market')
newMarket.name = name
newMarket.config = '{ \n\"codeName\": \"' + name + '\"\n}'
newMarket.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
newMarket.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_100X
newMarket.payload.floatingObject.arrangementStyle = ARRANGEMENT_STYLE.CONCAVE
newMarket.baseAsset.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_45
newMarket.quotedAsset.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_45
newMarket.baseAsset.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_100X
newMarket.quotedAsset.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_100X
newMarket.baseAsset.payload.floatingObject.arrangementStyle = ARRANGEMENT_STYLE.CONCAVE
newMarket.quotedAsset.payload.floatingObject.arrangementStyle = ARRANGEMENT_STYLE.CONCAVE
newMarket.baseAsset.payload.referenceParent = baseAsset
newMarket.quotedAsset.payload.referenceParent = quotedAsset
currentMarkets.set(name, newMarket)
}
}
}
} catch (err) {
node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
console.log(err.stack)
}
}
function installMarket(
node,
rootNodes
) {
let market = node
let cryptoExchange = UI.projects.superalgos.utilities.meshes.findNodeInNodeMesh(node, 'Crypto Exchange', undefined, true, false, true, false)
if (cryptoExchange === undefined) {
node.payload.uiObject.setErrorMessage('Market must be a descendant of a Crypto Exchange')
return
}
node.payload.uiObject.setInfoMessage('This market is being installed. This might take a minute or two. Please hold on while we connect all the dots for you. ')
setTimeout(installMarketProcedure, 500)
function installMarketProcedure() {
let dashboardsArray = []
for (let i = 0; i < rootNodes.length; i++) {
let rootNode = rootNodes[i]
if (rootNode.type === 'Network') {
installInNetwork(rootNode)
}
}
for (let i = 0; i < rootNodes.length; i++) {
let rootNode = rootNodes[i]
if (rootNode.type === 'Charting Space') {
installInChartingSpace(rootNode)
}
}
node.payload.uiObject.setInfoMessage('Market installation is complete.')
function installInNetwork(network) {
for (let j = 0; j < network.networkNodes.length; j++) {
let networkNode = network.networkNodes[j]
installInNetworkNode(networkNode)
}
function installInNetworkNode(networkNode) {
let tradingSessionsCreatedArray = []
let learningSessionsCreatedArray = []
installDataMiningTasks()
installLearningMiningTasks()
isntallTradingMiningTasks()
installDataMinesData()
installLearningMinesData()
installTradingMineData()
function installDataMiningTasks() {
/*
Here we complete the missing stuff at Data Mining
*/
let dataMining = UI.projects.superalgos.utilities.branches.findInBranch(networkNode, 'Data Mining', node, true)
if (dataMining === undefined) {
node.payload.uiObject.setErrorMessage('Data Mining node not found at Network Node ' + networkNode.name)
return
}
let exchangeDataTasks = UI.projects.superalgos.utilities.children.findOrCreateChildWithReference(dataMining, 'Exchange Data Tasks', cryptoExchange)
exchangeDataTasks.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
let marketDataTask = UI.projects.superalgos.utilities.children.findAndRecreateChildWithReference(exchangeDataTasks, 'Market Data Tasks', market, rootNodes)
UI.projects.superalgos.utilities.menu.menuClick(marketDataTask, 'Add Missing Data Mine Tasks', true)
UI.projects.superalgos.utilities.menu.menuClickOfNodeArray(marketDataTask.dataMineTasks, 'Add All Tasks', true)
}
function installLearningMiningTasks() {
/*
Here we complete the missing stuff at Learning Mining
*/
let learningMining = UI.projects.superalgos.utilities.branches.findInBranch(networkNode, 'Learning Mining', node, true)
if (learningMining === undefined) {
node.payload.uiObject.setErrorMessage('Learning Mining node not found at Network Node ' + networkNode.name)
return
}
let exchangeLearningTasks = UI.projects.superalgos.utilities.children.findOrCreateChildWithReference(learningMining, 'Exchange Learning Tasks', cryptoExchange)
exchangeLearningTasks.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
let marketLearningTask = UI.projects.superalgos.utilities.children.findAndRecreateChildWithReference(exchangeLearningTasks, 'Market Learning Tasks', market, rootNodes)
UI.projects.superalgos.utilities.menu.menuClick(marketLearningTask, 'Add Missing Learning Mine Tasks', true)
UI.projects.superalgos.utilities.menu.menuClickOfNodeArray(marketLearningTask.learningMineTasks, 'Add All Tasks', true)
/* This will be needed at the charting space, for creating Dashboards */
let learningSessionsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(marketLearningTask, 'Learning Session')
learningSessionsCreatedArray = learningSessionsCreatedArray.concat(learningSessionsArray)
}
function isntallTradingMiningTasks() {
/*
Next we complete the missing stuff at Testing Environment
*/
installEnvironment('Testing Environment')
/*
Next we complete the missing stuff at Production Environment
*/
installEnvironment('Production Environment')
function installEnvironment(environment) {
/*
Now we install the environmnet at the current Network Node
*/
let environmentFound = UI.projects.superalgos.utilities.branches.findInBranch(networkNode, environment, node, true)
if (environmentFound === undefined) {
node.payload.uiObject.setErrorMessage(environment + ' node not found at Network Node ' + networkNode.name)
return
}
let exchangeTradingTasks = UI.projects.superalgos.utilities.children.findOrCreateChildWithReference(environmentFound, 'Exchange Trading Tasks', cryptoExchange)
exchangeTradingTasks.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
let marketTradingTask = UI.projects.superalgos.utilities.children.findAndRecreateChildWithReference(exchangeTradingTasks, 'Market Trading Tasks', market, rootNodes)
UI.projects.superalgos.utilities.menu.menuClick(marketTradingTask, 'Add Missing Trading Mine Tasks', true)
UI.projects.superalgos.utilities.menu.menuClickOfNodeArray(marketTradingTask.tradingMineTasks, 'Add All Tasks', true)
/* This will be needed at the charting space, for creating Dashboards */
let backtestingSessionsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(marketTradingTask, 'Backtesting Session')
let liveTradingSessionsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(marketTradingTask, 'Live Trading Session')
let paperTradingSessionsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(marketTradingTask, 'Paper Trading Session')
let forwardSessionsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(marketTradingTask, 'Forward Testing Session')
let allSessionsArray = backtestingSessionsArray
.concat(liveTradingSessionsArray)
.concat(paperTradingSessionsArray)
.concat(forwardSessionsArray)
tradingSessionsCreatedArray = tradingSessionsCreatedArray.concat(allSessionsArray)
dashboardsArray.push({ environmentNode: environmentFound, networkNode: networkNode, sessionsArray: allSessionsArray })
}
}
function installDataMinesData() {
/*
Here we complete the missing stuff at Data Mines Data
*/
let dataMinesData = UI.projects.superalgos.utilities.branches.findInBranch(networkNode, 'Data Mines Data', node, true)
if (dataMinesData === undefined) {
node.payload.uiObject.setErrorMessage('Data Mines Data node not found at Network Node ' + networkNode.name)
return
}
let exchangeDataProducts = UI.projects.superalgos.utilities.children.findOrCreateChildWithReference(dataMinesData, 'Exchange Data Products', cryptoExchange)
exchangeDataProducts.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
let marketDataProducts = UI.projects.superalgos.utilities.children.findAndRecreateChildWithReference(exchangeDataProducts, 'Market Data Products', market, rootNodes)
marketDataProducts.payload.floatingObject.collapseToggle()
UI.projects.superalgos.utilities.menu.menuClick(marketDataProducts, 'Add All Data Mine Products', true)
UI.projects.superalgos.utilities.menu.menuClickOfNodeArray(marketDataProducts.dataMineProducts, 'Add All Data Products', true)
}
function installLearningMinesData() {
/*
Here we complete the missing stuff at Learning Mines Data
*/
let learningMinesData = UI.projects.superalgos.utilities.branches.findInBranch(networkNode, 'Learning Mines Data', node, true)
if (learningMinesData === undefined) {
node.payload.uiObject.setErrorMessage('Learning Mines Data node not found at Network Node ' + networkNode.name)
return
}
let exchangeLearningProducts = UI.projects.superalgos.utilities.children.findOrCreateChildWithReference(learningMinesData, 'Exchange Learning Products', cryptoExchange)
exchangeLearningProducts.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
let marketLearningProducts = UI.projects.superalgos.utilities.children.findAndRecreateChildWithReference(exchangeLearningProducts, 'Market Learning Products', market, rootNodes)
marketLearningProducts.payload.floatingObject.collapseToggle()
/*
Create the new session references.
*/
for (let i = 0; i < learningSessionsCreatedArray.length; i++) {
let session = learningSessionsCreatedArray[i]
if (UI.projects.superalgos.utilities.children.isMissingChildren(marketLearningProducts, session, true) === true) {
UI.projects.superalgos.functionLibraries.dataStorageFunctions.createSessionReference(marketLearningProducts, session, 'Learning Session Reference')
}
}
/*
Create everything inside the session references.
*/
for (let j = 0; j < marketLearningProducts.learningSessionReferences.length; j++) {
let sessionReference = marketLearningProducts.learningSessionReferences[j]
UI.projects.superalgos.utilities.menu.menuClick(sessionReference, 'Add All Learning Mine Products', true)
UI.projects.superalgos.utilities.menu.menuClickOfNodeArray(sessionReference.learningMineProducts, 'Add All Data Products', true)
sessionReference.payload.floatingObject.collapseToggle()
}
}
function installTradingMineData() {
/*
Finally we complete the missing stuff at Trading Mines Data
*/
let tradingMinesData = UI.projects.superalgos.utilities.branches.findInBranch(networkNode, 'Trading Mines Data', node, true)
if (tradingMinesData === undefined) {
node.payload.uiObject.setErrorMessage('Trading Mines Data node not found at Network Node ' + networkNode.name)
return
}
let exchangeTradingProducts = UI.projects.superalgos.utilities.children.findOrCreateChildWithReference(tradingMinesData, 'Exchange Trading Products', cryptoExchange)
exchangeTradingProducts.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
let marketTradingProducts = UI.projects.superalgos.utilities.children.findAndRecreateChildWithReference(exchangeTradingProducts, 'Market Trading Products', market, rootNodes)
marketTradingProducts.payload.floatingObject.collapseToggle()
/*
Create the new session references.
*/
for (let i = 0; i < tradingSessionsCreatedArray.length; i++) {
let session = tradingSessionsCreatedArray[i]
if (UI.projects.superalgos.utilities.children.isMissingChildren(marketTradingProducts, session, true) === true) {
UI.projects.superalgos.functionLibraries.dataStorageFunctions.createSessionReference(marketTradingProducts, session, 'Trading Session Reference')
}
}
/*
Create everything inside the session references.
*/
for (let j = 0; j < marketTradingProducts.tradingSessionReferences.length; j++) {
let sessionReference = marketTradingProducts.tradingSessionReferences[j]
UI.projects.superalgos.utilities.menu.menuClick(sessionReference, 'Add All Trading Mine Products', true)
UI.projects.superalgos.utilities.menu.menuClickOfNodeArray(sessionReference.tradingMineProducts, 'Add All Data Products', true)
sessionReference.payload.floatingObject.collapseToggle()
}
}
}
}
function installInChartingSpace(chartingSpace) {
for (let i = 0; i < dashboardsArray.length; i++) {
/*
If the Dashboard we need is not already there we create a new one.
*/
let arrayItem = dashboardsArray[i]
let dashboard = UI.projects.superalgos.utilities.children.findOrCreateChildWithReference(chartingSpace, 'Dashboard', arrayItem.environmentNode)
dashboard.name = arrayItem.environmentNode.type + ' ' + arrayItem.networkNode.name
/*
We delete all the existing Time Machines related to the market we are currently installing.
For that, we make a new array with the existing Time Machines so that the deleting
of each node does not affect the proccessing of the whole set.
*/
let timeMachines = []
for (let i = 0; i < dashboard.timeMachines.length; i++) {
let timeMachine = dashboard.timeMachines[i]
timeMachines.push(timeMachine)
}
for (let i = 0; i < timeMachines.length; i++) {
let timeMachine = timeMachines[i]
let session = timeMachine.payload.referenceParent
if (session === undefined || session.cleaned === true) {
/*
This is what usually happens, that the intall process make these
time machines to lose their reference parent since the install
process deletes them.
*/
UI.projects.superalgos.functionLibraries.nodeDeleter.deleteUIObject(timeMachine, rootNodes)
continue
}
let marketTradingTasks = UI.projects.superalgos.utilities.meshes.findNodeInNodeMesh(session, 'Market Trading Tasks', undefined, true, false, true, false)
if (marketTradingTasks === undefined) { continue }
if (marketTradingTasks.payload === undefined) { continue }
if (marketTradingTasks.payload.referenceParent === undefined) { continue }
if (marketTradingTasks.payload.referenceParent.id === market.id) {
UI.projects.superalgos.functionLibraries.nodeDeleter.deleteUIObject(timeMachine, rootNodes)
}
}
/*
We create a time machine for each session added during the previous processing.
*/
for (let j = 0; j < arrayItem.sessionsArray.length; j++) {
let session = arrayItem.sessionsArray[j]
UI.projects.superalgos.functionLibraries.chartingSpaceFunctions.createTimeMachine(dashboard, session, node, arrayItem.networkNode, rootNodes)
}
}
}
}
}
function uninstallMarket(node, rootNodes) {
node.payload.uiObject.setInfoMessage('This market is being uninstalled. Please hold on that it might take a while.')
setTimeout(uninstallMarketProcedure, 500)
function uninstallMarketProcedure() {
let market = node
for (let i = 0; i < rootNodes.length; i++) {
let rootNode = rootNodes[i]
if (rootNode.type === 'Charting Space') {
uninstallInChartingSpace(rootNode)
}
}
node.payload.uiObject.setInfoMessage('Market uninstall is complete.')
function uninstallInChartingSpace(chartingSpace) {
/* Delete all time machines which are referencing sessions inside the market being unistalled. */
let timeMachines = UI.projects.superalgos.utilities.branches.nodeBranchToArray(chartingSpace, 'Time Machine')
for (let i = 0; i < timeMachines.length; i++) {
let timeMachine = timeMachines[i]
let session = timeMachine.payload.referenceParent
if (session === undefined) { continue }
let marketTradingTasks = UI.projects.superalgos.utilities.meshes.findNodeInNodeMesh(session, 'Market Trading Tasks', undefined, true, false, true, false)
if (marketTradingTasks === undefined) { continue }
if (marketTradingTasks.payload === undefined) { continue }
if (marketTradingTasks.payload.referenceParent === undefined) { continue }
if (marketTradingTasks.payload.referenceParent.id === market.id) {
UI.projects.superalgos.functionLibraries.nodeDeleter.deleteUIObject(timeMachine, rootNodes)
}
}
/* Delete all Dashboards that does not have time machines inside. */
let dashboardArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(chartingSpace, 'Dashboard')
for (let i = 0; i < dashboardArray.length; i++) {
let dashboard = dashboardArray[i]
if (dashboard.timeMachines.length === 0) {
UI.projects.superalgos.functionLibraries.nodeDeleter.deleteUIObject(dashboard, rootNodes)
}
}
/* Scan Networks for the market being unistalled to delete it. */
for (let i = 0; i < rootNodes.length; i++) {
let rootNode = rootNodes[i]
if (rootNode.type === 'Network') {
uninstallInNetwork(rootNode)
}
}
}
function uninstallInNetwork(network) {
let marketDataTasksArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(network, 'Market Data Tasks')
let marketTradingTasksArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(network, 'Market Trading Tasks')
let marketLearningTasksArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(network, 'Market Learning Tasks')
let marketDataProductsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(network, 'Market Data Products')
let marketTradingProductsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(network, 'Market Trading Products')
let marketLearningProductsArray = UI.projects.superalgos.utilities.branches.nodeBranchToArray(network, 'Market Learning Products')
uninstalMarketArray(marketDataTasksArray)
uninstalMarketArray(marketTradingTasksArray)
uninstalMarketArray(marketLearningTasksArray)
uninstalMarketArray(marketDataProductsArray)
uninstalMarketArray(marketTradingProductsArray)
uninstalMarketArray(marketLearningProductsArray)
function uninstalMarketArray(marketArray) {
for (let i = 0; i < marketArray.length; i++) {
let marketReference = marketArray[i]
if (marketReference.payload === undefined) { continue }
if (marketReference.payload.referenceParent === undefined) { continue }
if (marketReference.payload.referenceParent.id === market.id) {
UI.projects.superalgos.functionLibraries.nodeDeleter.deleteUIObject(marketReference, rootNodes)
}
}
}
}
}
}
}
@@ -1,32 +0,0 @@
function newSuperalgosFunctionLibraryWorkspaceFunctions() {
thisObject = {
addMissingWorkspaceProjects: addMissingWorkspaceProjects
}
return thisObject
function addMissingWorkspaceProjects(node, rootNodes) {
let url = 'ProjectNames'
httpRequest(undefined, url, onResponse)
function onResponse(err, pProjects) {
let projects = JSON.parse(pProjects)
for (let i = 0; i < projects.length; i++) {
let project = projects[i]
let alreadyExist = false
for (let j = 0; j < rootNodes.length; j++) {
let rootNode = rootNodes[j]
if (rootNode.type === project + ' Project') {
alreadyExist = true
}
}
if (alreadyExist === false) {
let child = UI.projects.superalgos.functionLibraries.uiObjectsFromNodes.addUIObject(node, project + ' Project', rootNodes, project)
child.project = project
}
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

@@ -1 +1 @@
{"type":"Data Mine","name":"Alpha","config":"{ \n\"codeName\": \"Alpha\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Average True Range (ATR)","config":"{\n\"codeName\": \"ATR\",\n\"repo\": \"ATR-Indicator-Bot\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates RSI.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market ATR - SMA","project":"Superalgos","id":"5cdfa018-5ade-469b-90bf-cc523ef0a46e","savedPayload":{"position":{"x":14824.467185363952,"y":17153.56240800229},"targetPosition":{"x":15051.32778510527,"y":17433.553422503435},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2570d2c7-72e3-4131-97dc-a9f97f493e93"}}},{"type":"Output Dataset","name":"Multi-Period-Market ATR - RMA","project":"Superalgos","id":"e7e58e02-fef8-4400-9928-f29805a96f98","savedPayload":{"position":{"x":14995.21249473741,"y":17077.69824065427},"targetPosition":{"x":15051.32778510527,"y":17433.553422503435},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"67695466-be2d-43b4-9824-83baa7a08412"}}}],"outputDatasetFolders":[],"id":"b345304f-fbb3-4889-934a-8f9b4941ed29","savedPayload":{"position":{"x":15027.5645769923,"y":17379.910551027475},"targetPosition":{"x":15182.942695423619,"y":17728.650277388508},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{\"mainUtility\": \"Market Ending Point\"}","project":"Superalgos","id":"eb499527-0f18-45f9-99ab-a7bc7fabb3aa","savedPayload":{"position":{"x":15005.836154077664,"y":17146.468954298347},"targetPosition":{"x":15216.865516382564,"y":17407.20361136918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a0a61ba9-91cb-4ec7-92fe-134ff079de2d"}}},{"type":"Status Dependency","name":"Average True Range (ATR) Multi-Period-Market","config":"{\"mainUtility\": \"Self Reference\"}","project":"Superalgos","id":"025a6f70-c187-4b4d-aa62-6f4ebfc116ba","savedPayload":{"position":{"x":15251.795115631983,"y":17073.59332967888},"targetPosition":{"x":15216.865516382564,"y":17407.20361136918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"8a6c8b1e-950d-4819-88a9-9c45272a0799"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Candles Multi-Period-Market","project":"Superalgos","id":"f25ba171-3c78-4e4f-acb8-66446be1ce0a","savedPayload":{"position":{"x":15480.231901478768,"y":17193.677641293492},"targetPosition":{"x":15216.865516382564,"y":17407.20361136918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2b03494a-67d6-47f1-89b6-53fc4c9116a8"}}}],"dataMineDataDependencies":[],"id":"bf337039-bd28-4f50-89d1-5115e631e6cf","savedPayload":{"position":{"x":15223.760246305472,"y":17340.44129825625},"targetPosition":{"x":15182.942695423619,"y":17728.650277388508},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"SLine truncated
{"type":"Data Mine","name":"Alpha","config":"{ \n\"codeName\": \"Alpha\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Average True Range (ATR)","config":"{\n\"codeName\": \"ATR\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates RSI.\",\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market ATR - SMA","project":"Superalgos","id":"5cdfa018-5ade-469b-90bf-cc523ef0a46e","savedPayload":{"position":{"x":14824.467185363952,"y":17153.56240800229},"targetPosition":{"x":15051.32778510527,"y":17433.553422503435},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2570d2c7-72e3-4131-97dc-a9f97f493e93"}}},{"type":"Output Dataset","name":"Multi-Period-Market ATR - RMA","project":"Superalgos","id":"e7e58e02-fef8-4400-9928-f29805a96f98","savedPayload":{"position":{"x":14995.21249473741,"y":17077.69824065427},"targetPosition":{"x":15051.32778510527,"y":17433.553422503435},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"67695466-be2d-43b4-9824-83baa7a08412"}}}],"outputDatasetFolders":[],"id":"b345304f-fbb3-4889-934a-8f9b4941ed29","savedPayload":{"position":{"x":15027.5645769923,"y":17379.910551027475},"targetPosition":{"x":15182.942695423619,"y":17728.650277388508},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{\"mainUtility\": \"Market Ending Point\"}","project":"Superalgos","id":"eb499527-0f18-45f9-99ab-a7bc7fabb3aa","savedPayload":{"position":{"x":15005.836154077664,"y":17146.468954298347},"targetPosition":{"x":15216.865516382564,"y":17407.20361136918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a0a61ba9-91cb-4ec7-92fe-134ff079de2d"}}},{"type":"Status Dependency","name":"Average True Range (ATR) Multi-Period-Market","config":"{\"mainUtility\": \"Self Reference\"}","project":"Superalgos","id":"025a6f70-c187-4b4d-aa62-6f4ebfc116ba","savedPayload":{"position":{"x":15251.795115631983,"y":17073.59332967888},"targetPosition":{"x":15216.865516382564,"y":17407.20361136918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"8a6c8b1e-950d-4819-88a9-9c45272a0799"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Candles Multi-Period-Market","project":"Superalgos","id":"f25ba171-3c78-4e4f-acb8-66446be1ce0a","savedPayload":{"position":{"x":15480.231901478768,"y":17193.677641293492},"targetPosition":{"x":15216.865516382564,"y":17407.20361136918},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2b03494a-67d6-47f1-89b6-53fc4c9116a8"}}}],"dataMineDataDependencies":[],"id":"bf337039-bd28-4f50-89d1-5115e631e6cf","savedPayload":{"position":{"x":15223.760246305472,"y":17340.44129825625},"targetPosition":{"x":15182.942695423619,"y":17728.650277388508},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"Superalgos","id":"8a6c8b1e-950d-4819-88a9-9c45272a0799","savedPayload":{"position":{"x":15405.437118264057,"y":17422.346390022496},"targetPosition":{"x":15182.942695423619,"y":17728.650277388508},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Candles VolLine truncated
@@ -1 +1 @@
{"type":"Data Mine","name":"Buddha","config":"{ \n\"codeName\": \"Buddha\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Candle Patterns","config":"{\n\"codeName\": \"Candle-Patterns\",\n\"repo\": \"Candle-Patterns-Bot\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates Candles Patterns for Time Frames of 01-hs and above.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Basic Candles","project":"Superalgos","id":"3e933426-1c7b-45e1-845f-08a7d2684620","savedPayload":{"position":{"x":13014.983786246557,"y":11582.257138896071},"targetPosition":{"x":13214.778358558542,"y":11672.136629478231},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"07be5b9b-e8fd-49fc-ab77-20f89fdea10f"}}},{"type":"Output Dataset","name":"Multi-Period-Market One-Candle Patterns","project":"Superalgos","id":"201dc796-db42-49ce-8b9c-1b26c0f3a0ce","savedPayload":{"position":{"x":13055.077268021229,"y":11513.03974999541},"targetPosition":{"x":13214.778358558542,"y":11672.136629478231},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"144d8665-1ff0-4db3-9340-a33fd7d0d098"}}},{"type":"Output Dataset","name":"Multi-Period-Market Two-Candles Patterns","project":"Superalgos","id":"c20616cc-d93f-40fc-904a-025fc2799a75","savedPayload":{"position":{"x":13115.919477111813,"y":11469.572073629888},"targetPosition":{"x":13214.778358558542,"y":11672.136629478231},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"8c35ce52-9dee-4d20-92e6-50ba7fd2882b"}}},{"type":"Output Dataset","name":"Multi-Period-Market Three-Candles Patterns","project":"Superalgos","id":"c8074ffc-f5ff-4cb5-8e83-0adcecc82a0c","savedPayload":{"position":{"x":13212.772990245361,"y":11473.777495327486},"targetPosition":{"x":13214.778358558542,"y":11672.136629478231},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"087fc67e-78f9-4204-ba5e-8b670566a716"}}}],"outputDatasetFolders":[],"id":"0dc09316-0987-4d22-b600-bd3868942026","savedPayload":{"position":{"x":13214.778358558542,"y":11672.136629478231},"targetPosition":{"x":13428.975206324332,"y":11986.086624547845},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Candle Patterns Multi-Period-Market","config":"{\"mainUtility\": \"Self Reference\"}","project":"Superalgos","id":"2d079388-93e4-4005-b392-c4a997904ccb","savedPayload":{"position":{"x":13237.534092975908,"y":11373.149986684633},"targetPosition":{"x":13395.171017360753,"y":11533.418739091607},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"401fbf30-8faa-40e9-9707-fac2d35f6195"}}},{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{ \n\"mainUtility\": \"Market Starting Point\"\n}","project":"Superalgos","id":"418e1725-b690-4a55-b5f7-a4a92465c162","savedPayload":{"position":{"x":13379.394277197825,"y":11319.028125288029},"targetPosition":{"x":13395.171017360753,"y":11533.418739091607},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanLine truncated
{"type":"Data Mine","name":"Buddha","config":"{ \n\"codeName\": \"Buddha\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Candle Patterns","config":"{\n\"codeName\": \"Candle-Patterns\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates Candles Patterns for Time Frames of 01-hs and above.\",\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Basic Candles","project":"Superalgos","id":"3e933426-1c7b-45e1-845f-08a7d2684620","savedPayload":{"position":{"x":3129.041401383918,"y":17452.35686716615},"targetPosition":{"x":3328.835973695903,"y":17542.236357748305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"07be5b9b-e8fd-49fc-ab77-20f89fdea10f"}}},{"type":"Output Dataset","name":"Multi-Period-Market One-Candle Patterns","project":"Superalgos","id":"201dc796-db42-49ce-8b9c-1b26c0f3a0ce","savedPayload":{"position":{"x":3169.134883158589,"y":17383.139478265486},"targetPosition":{"x":3328.835973695903,"y":17542.236357748305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"144d8665-1ff0-4db3-9340-a33fd7d0d098"}}},{"type":"Output Dataset","name":"Multi-Period-Market Two-Candles Patterns","project":"Superalgos","id":"c20616cc-d93f-40fc-904a-025fc2799a75","savedPayload":{"position":{"x":3229.9770922491734,"y":17339.671801899964},"targetPosition":{"x":3328.835973695903,"y":17542.236357748305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"8c35ce52-9dee-4d20-92e6-50ba7fd2882b"}}},{"type":"Output Dataset","name":"Multi-Period-Market Three-Candles Patterns","project":"Superalgos","id":"c8074ffc-f5ff-4cb5-8e83-0adcecc82a0c","savedPayload":{"position":{"x":3326.830605382722,"y":17343.877223597563},"targetPosition":{"x":3328.835973695903,"y":17542.236357748305},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"087fc67e-78f9-4204-ba5e-8b670566a716"}}}],"outputDatasetFolders":[],"id":"0dc09316-0987-4d22-b600-bd3868942026","savedPayload":{"position":{"x":3328.835973695903,"y":17542.236357748305},"targetPosition":{"x":3543.032821461693,"y":17856.18635281792},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Candle Patterns Multi-Period-Market","config":"{\"mainUtility\": \"Self Reference\"}","project":"Superalgos","id":"2d079388-93e4-4005-b392-c4a997904ccb","savedPayload":{"position":{"x":3351.5917081132684,"y":17243.24971495471},"targetPosition":{"x":3509.228632498114,"y":17403.518467361682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"401fbf30-8faa-40e9-9707-fac2d35f6195"}}},{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{ \n\"mainUtility\": \"Market Starting Point\"\n}","project":"Superalgos","id":"418e1725-b690-4a55-b5f7-a4a92465c162","savedPayload":{"position":{"x":3493.451892335186,"y":17189.127853558104},"targetPosition":{"x":3509.228632498114,"y":17403.518467361682},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a0a61ba9-91cb-4ec7-92fe-134ff079de2d"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Candles Multi-Period-Market","project":"Superalgos","id":"85c548fd-9ef7-4936-a86d-2a206cf90039","savedPayload":{"position":{"x":3649.480915739696,"y":17240.712195185763},"targetPosition":{"x":3509.228632498114,"y":17Line truncated
@@ -1 +1 @@
{"type":"Data Mine","name":"Masters","config":"{\n\"codeName\": \"Masters\"\n}","project":"Superalgos","sensorBots":[{"type":"Sensor Bot","name":"Exchange Raw Data","config":"{\n\"codeName\": \"Exchange-Raw-Data\",\n\"repo\": \"Exchange-Raw-Data-Sensor-Bot\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Historic-OHLCVs","config":"{\n \"codeName\": \"Historic-OHLCVs\",\n \"description\": \"Retrieves and saves historical maket data\",\n \"startMode\": {\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 60000,\n \"retryWaitTime\": 10000\n}","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[],"outputDatasetFolders":[],"id":"dfe2556e-a765-4e38-9d30-7f91090c0069","savedPayload":{"position":{"x":7743.093065585781,"y":17657.26070065566},"targetPosition":{"x":7723.051525995295,"y":18063.790438218573},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Exchange Raw Data Historic-OHLCVs","config":"{\n \"mainUtility\": \"Self Reference\"\n}","project":"Superalgos","id":"ea39c98b-18e2-44ce-9f7f-86a8a76a688a","savedPayload":{"position":{"x":8159.968121568774,"y":17392.017795274412},"targetPosition":{"x":7941.750591738466,"y":17727.773847360382},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"181c7c34-7243-497a-9ddb-1b0067c6d66e"}}}],"dataDependencies":[],"dataMineDataDependencies":[],"id":"5c7fae5a-6da6-4864-a167-f2f21d578c72","savedPayload":{"position":{"x":7941.750591738466,"y":17727.773847360382},"targetPosition":{"x":7723.051525995295,"y":18063.790438218573},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"Superalgos","id":"181c7c34-7243-497a-9ddb-1b0067c6d66e","savedPayload":{"position":{"x":8080.14992890308,"y":17881.860565405637},"targetPosition":{"x":7723.051525995295,"y":18063.790438218573},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"","project":"Superalgos","id":"957e7e3a-b167-40be-93b5-9ba4ceff56e5","savedPayload":{"position":{"x":8123.742665378901,"y":18084.134120101407},"targetPosition":{"x":7723.051525995295,"y":18063.790438218573},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","project":"Superalgos","id":"fe40a546-43ee-49df-908a-7e72f598f0cd","savedPayload":{"position":{"x":8067.94916479235,"y":18272.098978495884},"targetPosition":{"x":7723.051525995295,"y":18063.790438218573},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc","savedPayload":{"position":{"x":7723.051525995295,"y":18063.790438218573},"targetPosition":{"x":7327.141714623944,"y":18266.006678666803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}}],"products":[{"type":"Product Definition","name":"Trades","config":"{\n\"codeName\": \"Trades\"\n}","project":"Superalgos","datasets":[{"type":"Dataset Definition","name":"Trades","config":"{\n\"codeName\": \"Trades\"\n}","project":"Superalgos","id":"fbb7c17e-a909-4fa2-b3c5-eb8ca86297c4","savedPayload":{"position":{"x":7988.166640893156,"y":18161.91023332179},"targetPosition":{"x":7789.749141672932,"y":18193.51426197839},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":2,"arrangementStyle":0},"uiObject":{}}}],"id":"38dbb942-a15b-450b-a867-02a94030965f","savedPayload":{"position":{"x":7789.749141672932,"y":18193.51426197839},"targetPosition":{"x":7327.141714623944,"y":18266.006678666803},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Product Definition","name"Line truncated
{"type":"Data Mine","name":"Masters","config":"{\n\"codeName\": \"Masters\"\n}","project":"Superalgos","sensorBots":[{"type":"Sensor Bot","name":"Exchange Raw Data","config":"{\n\"codeName\": \"Exchange-Raw-Data\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Historic-OHLCVs","config":"{\n \"codeName\": \"Historic-OHLCVs\",\n \"description\": \"Retrieves and saves historical maket data\",\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 60000,\n \"retryWaitTime\": 10000,\n \"botModule\": \"Exchange Raw Data Historic OHLCVs\"\n}","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[],"outputDatasetFolders":[],"id":"dfe2556e-a765-4e38-9d30-7f91090c0069","savedPayload":{"position":{"x":7542.108005075513,"y":17855.768569069416},"targetPosition":{"x":7502.950147409498,"y":18153.20202748156},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Exchange Raw Data Historic-OHLCVs","config":"{\n \"mainUtility\": \"Self Reference\"\n}","project":"Superalgos","id":"ea39c98b-18e2-44ce-9f7f-86a8a76a688a","savedPayload":{"position":{"x":7868.207004814731,"y":17677.190023306815},"targetPosition":{"x":7685.578576112114,"y":17915.196025394187},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"181c7c34-7243-497a-9ddb-1b0067c6d66e"}}}],"dataDependencies":[],"dataMineDataDependencies":[],"id":"5c7fae5a-6da6-4864-a167-f2f21d578c72","savedPayload":{"position":{"x":7685.578576112114,"y":17915.196025394187},"targetPosition":{"x":7502.950147409498,"y":18153.20202748156},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"Superalgos","id":"181c7c34-7243-497a-9ddb-1b0067c6d66e","savedPayload":{"position":{"x":7780.114007162883,"y":18038.396997772034},"targetPosition":{"x":7502.950147409498,"y":18153.20202748156},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"","project":"Superalgos","id":"957e7e3a-b167-40be-93b5-9ba4ceff56e5","savedPayload":{"position":{"x":7800.383605821641,"y":18192.359885147573},"targetPosition":{"x":7502.950147409498,"y":18153.20202748156},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","project":"Superalgos","id":"fe40a546-43ee-49df-908a-7e72f598f0cd","savedPayload":{"position":{"x":7740.956149496868,"y":18335.830456184176},"targetPosition":{"x":7502.950147409498,"y":18153.20202748156},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc","savedPayload":{"position":{"x":7502.950147409498,"y":18153.20202748156},"targetPosition":{"x":7225.786287656112,"y":18268.007057191084},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}}],"products":[{"type":"Product Definition","name":"Candles","config":"{\n\"codeName\": \"Candles\"\n}","project":"Superalgos","datasets":[{"type":"Dataset Definition","name":"One-Min","config":"{\n\"codeName\": \"One-Min\"\n}","project":"Superalgos","id":"1c3696e4-c28e-4744-9100-706d6dce0c3c","savedPayload":{"position":{"x":7675.786287656112,"y":18268.007057191084},"targetPosition":{"x":7525.786287656112,"y":18268.007057191084},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":2,"arrangementStyle":0},"uiObject":{}}}],"id":"2443883b-6959-42a3-ae3b-ef3102ffcdcc","savedPayload":{"position":{"x":7525.786287656112,"y":18268.007057191084},"targetPosition":{"x":7225.786287656112,"y":18268.007057191084},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Product Definition","name":"Volumes","config":"{\n\"codeName\": \"Volumes\"\n}","project":"Superalgos","datasets":[{"type":"Dataset Definition","name":"One-Min","config":"{\n\"codeName\": \"One-Min\"\n}","proLine truncated
@@ -1 +1 @@
{"type":"Data Mine","name":"Samurai","config":"{\n\"codeName\": \"Samurai\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Candles Probabilities","config":"{\n\"codeName\": \"Candles-Probabilities\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates SMAs for Time Frames of 01-hs and above.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Candles Probabilities","project":"Superalgos","id":"ab027b2e-4e02-4c71-8da5-6b43c37de20b","savedPayload":{"position":{"x":24441.807558180073,"y":11178.172754360203},"targetPosition":{"x":24757.91289199851,"y":11360.695699284031},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"85c67513-347a-4948-9121-dee04543fcff"}}}],"outputDatasetFolders":[],"id":"1a087bb8-af2f-4384-85dd-c86d4b39aeff","savedPayload":{"position":{"x":24740.813740813894,"y":11764.307395546577},"targetPosition":{"x":25087.495634870367,"y":11964.565651629404},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{\"mainUtility\": \"Market Ending Point\"}","project":"Superalgos","id":"09588f6b-2193-4c9e-bc33-2ac075e8e4f4","savedPayload":{"position":{"x":25757.82661988232,"y":10782.55282705661},"targetPosition":{"x":25863.974750655703,"y":11140.122654848603},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a0a61ba9-91cb-4ec7-92fe-134ff079de2d"}}},{"type":"Status Dependency","name":"Candles Probabilities Multi-Period-Market","config":"{ \n\"mainUtility\": \"Self Reference|Market Starting Point|Market Ending Point\"\n}","project":"Superalgos","id":"83721123-d178-4ab3-8d6f-da06e8416eb8","savedPayload":{"position":{"x":25902.870176218516,"y":10769.51496828574},"targetPosition":{"x":25863.974750655703,"y":11140.122654848603},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"24ca1130-0386-4088-9417-48eb5d8ac96a"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Candles Multi-Period-Market","project":"Superalgos","id":"2cfeb861-3ce6-4f63-b974-acc395f744cc","savedPayload":{"position":{"x":26042.027883264192,"y":10813.091690317255},"targetPosition":{"x":25863.974750655703,"y":11140.122654848603},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2b03494a-67d6-47f1-89b6-53fc4c9116a8"}}}],"dataMineDataDependencies":[],"id":"08845d0d-a0a1-4d26-bb44-1072dfcf0b07","savedPayload":{"position":{"x":24887.60865246401,"y":11617.60490303291},"targetPosition":{"x":25087.495634870367,"y":11964.565651629404},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"Superalgos","id":"24ca1130-0386-4088-9417-48eb5d8ac96a","savedPayload":{"position":{"x":25087.495721365332,"y":11563.948027561915},"targetPosition":{"x":25087.495634870367,"y":11964.565651629404},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Candles Volumes Multi-Period-Market","project":"SLine truncated
{"type":"Data Mine","name":"Samurai","config":"{\n\"codeName\": \"Samurai\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Candles Probabilities","config":"{\n\"codeName\": \"Candles-Probabilities\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates SMAs for Time Frames of 01-hs and above.\",\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Candles Probabilities","project":"Superalgos","id":"ab027b2e-4e02-4c71-8da5-6b43c37de20b","savedPayload":{"position":{"x":17017.002939912152,"y":17072.851514913826},"targetPosition":{"x":17333.108273730588,"y":17255.374459837658},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"85c67513-347a-4948-9121-dee04543fcff"}}}],"outputDatasetFolders":[],"id":"1a087bb8-af2f-4384-85dd-c86d4b39aeff","savedPayload":{"position":{"x":17561.343925622532,"y":17819.598525719965},"targetPosition":{"x":17821.151546757865,"y":17969.598525719965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{\"mainUtility\": \"Market Ending Point\"}","project":"Superalgos","id":"09588f6b-2193-4c9e-bc33-2ac075e8e4f4","savedPayload":{"position":{"x":18333.022001614398,"y":16677.231587610237},"targetPosition":{"x":18439.170132387782,"y":17034.801415402228},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a0a61ba9-91cb-4ec7-92fe-134ff079de2d"}}},{"type":"Status Dependency","name":"Candles Probabilities Multi-Period-Market","config":"{ \n\"mainUtility\": \"Self Reference|Market Starting Point|Market Ending Point\"\n}","project":"Superalgos","id":"83721123-d178-4ab3-8d6f-da06e8416eb8","savedPayload":{"position":{"x":18478.065557950595,"y":16664.193728839367},"targetPosition":{"x":18439.170132387782,"y":17034.801415402228},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"24ca1130-0386-4088-9417-48eb5d8ac96a"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Candles Multi-Period-Market","project":"Superalgos","id":"2cfeb861-3ce6-4f63-b974-acc395f744cc","savedPayload":{"position":{"x":18617.22326499627,"y":16707.770450870878},"targetPosition":{"x":18439.170132387782,"y":17034.801415402228},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2b03494a-67d6-47f1-89b6-53fc4c9116a8"}}}],"dataMineDataDependencies":[],"id":"08845d0d-a0a1-4d26-bb44-1072dfcf0b07","savedPayload":{"position":{"x":17671.151546757865,"y":17709.79090458463},"targetPosition":{"x":17821.151546757865,"y":17969.598525719965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"Superalgos","id":"24ca1130-0386-4088-9417-48eb5d8ac96a","savedPayload":{"position":{"x":17821.151546757865,"y":17669.598525719965},"targetPosition":{"x":17821.151546757865,"y":17969.598525719965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"82529746-2044-48f0-b1f6-d1454ba5682d","savedPayload":{"position":{"x":17971.151546757865,"y":17709.79090458463},"targetPosition":{"x":17821.151546757865,"y":17969.598525719965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Execution FinisLine truncated
@@ -1 +1 @@
{"type":"Data Mine","name":"Sparta","config":"{\n\"codeName\": \"Sparta\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Simple Moving Average (SMA)","config":"{\n\"codeName\": \"SMA\",\n\"repo\": \"SMA-Indicator-Bot\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates SMAs for Time Frames of 01-hs and above.\",\n \"startMode\": {\n \"allMonths\": {\n \"run\": \"false\",\n \"minYear\": \"\",\n \"maxYear\": \"\"\n },\n \"oneMonth\": {\n \"run\": \"false\",\n \"year\": \"\",\n \"month\": \"\"\n },\n \"noTime\": {\n \"run\": \"true\"\n },\n \"fixedInterval\": {\n \"run\": \"false\",\n \"interval\": 0\n }\n },\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\",\n \"startDate\": {\n },\n \"endDate\": {\n }\n }\n}","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Popular SMAs","project":"Superalgos","id":"5302effb-1f09-4a30-9f71-6956b8b9ca88","savedPayload":{"position":{"x":10609.044651705652,"y":17428.90566641547},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"3b21f9d7-2cbc-4c23-80c8-cc5e5b06a22c"}}},{"type":"Output Dataset","name":"Multi-Period-Market Base11 SMAs","project":"Superalgos","id":"8221593c-6298-4b4c-bfc2-cffbc9e5cd0f","savedPayload":{"position":{"x":10652.515125452519,"y":17324.96443352291},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"af9b9cef-2a74-486f-b79e-047d68669a1f"}}},{"type":"Output Dataset","name":"Multi-Period-Market Base7 SMAs","project":"Superalgos","id":"aabd0857-b4fc-404a-8967-967475ed3993","savedPayload":{"position":{"x":10721.675023375876,"y":17240.07387795538},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"78a6ef37-4df8-4ac8-83a0-c9a732467c59"}}},{"type":"Output Dataset","name":"Multi-Period-Market Base6 SMAs","project":"Superalgos","id":"dd00a77a-761e-404b-9e44-627335668112","savedPayload":{"position":{"x":10809.956405495142,"y":17178.031642194932},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"c0862b3d-d35a-4d3e-8d80-258b8a01c37c"}}},{"type":"Output Dataset","name":"Muti-Period-Market Base5 SMAs","project":"Superalgos","id":"b6426833-d2c7-4300-b610-801e12a7edfd","savedPayload":{"position":{"x":10936.584878620257,"y":17141.406608785455},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Muti-Period-Market","id":"a5d28568-fb58-469a-9820-b9f0e8eab705"}}}],"outputDatasetFolders":[],"id":"85a56739-9351-444b-95e9-a57470dca38a","savedPayload":{"position":{"x":10995.890269605332,"y":17540.32974347027},"targetPosition":{"x":11302.555356491408,"y":17880.959414907546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Simple Moving Average (SMA) Multi-Period-Market","config":"{\"mainUtility\": \"Self Reference\"}","project":"Superalgos","id":"d49d1e94-1960-4121-8e40-be8af52947ee","savedPayload":{"position":{"x":11067.618896327327,"y":17267.479023725446},"targetPosition":{"x":11205.707967556615,"y":17422.24521572421},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New StaLine truncated
{"type":"Data Mine","name":"Sparta","config":"{\n\"codeName\": \"Sparta\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Simple Moving Average (SMA)","config":"{\n\"codeName\": \"SMA\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":"{\n \"codeName\": \"Multi-Period-Market\",\n \"description\": \"Calculates SMAs for Time Frames of 01-hs and above.\",\n \"deadWaitTime\": 0,\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"sleepWaitTime\": 3600000,\n \"comaWaitTime\": 86400000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Popular SMAs","project":"Superalgos","id":"5302effb-1f09-4a30-9f71-6956b8b9ca88","savedPayload":{"position":{"x":10609.044651705652,"y":17428.90566641547},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"3b21f9d7-2cbc-4c23-80c8-cc5e5b06a22c"}}},{"type":"Output Dataset","name":"Multi-Period-Market Base11 SMAs","project":"Superalgos","id":"8221593c-6298-4b4c-bfc2-cffbc9e5cd0f","savedPayload":{"position":{"x":10652.515125452519,"y":17324.96443352291},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"af9b9cef-2a74-486f-b79e-047d68669a1f"}}},{"type":"Output Dataset","name":"Multi-Period-Market Base7 SMAs","project":"Superalgos","id":"aabd0857-b4fc-404a-8967-967475ed3993","savedPayload":{"position":{"x":10721.675023375876,"y":17240.07387795538},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"78a6ef37-4df8-4ac8-83a0-c9a732467c59"}}},{"type":"Output Dataset","name":"Multi-Period-Market Base6 SMAs","project":"Superalgos","id":"dd00a77a-761e-404b-9e44-627335668112","savedPayload":{"position":{"x":10809.956405495142,"y":17178.031642194932},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"c0862b3d-d35a-4d3e-8d80-258b8a01c37c"}}},{"type":"Output Dataset","name":"Muti-Period-Market Base5 SMAs","project":"Superalgos","id":"b6426833-d2c7-4300-b610-801e12a7edfd","savedPayload":{"position":{"x":10936.584878620257,"y":17141.406608785455},"targetPosition":{"x":10995.890269605332,"y":17540.32974347027},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Muti-Period-Market","id":"a5d28568-fb58-469a-9820-b9f0e8eab705"}}}],"outputDatasetFolders":[],"id":"85a56739-9351-444b-95e9-a57470dca38a","savedPayload":{"position":{"x":10995.890269605332,"y":17540.32974347027},"targetPosition":{"x":11302.555356491408,"y":17880.959414907546},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Simple Moving Average (SMA) Multi-Period-Market","config":"{\"mainUtility\": \"Self Reference\"}","project":"Superalgos","id":"d49d1e94-1960-4121-8e40-be8af52947ee","savedPayload":{"position":{"x":11067.618896327327,"y":17267.479023725446},"targetPosition":{"x":11205.707967556615,"y":17422.24521572421},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":2,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"81346f2f-13f2-46f4-85f5-a04b4d80f455"}}},{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{\"mainUtility\": \"Market Ending Point\"}","project":"Superalgos","id":"f7cf0949-06fc-4c98-a399-61fac1671c6e","savedPayload":{"position":{"x":11151.101786685287,"y":17208.702149919078},"targetPosition":{"x":11205.707967556615,"y":17422.24521572421},"floatingObject":{"isPinned":Line truncated
@@ -1 +1 @@
{"type":"Data Mine","name":"Zeus","config":"{ \n\"codeName\": \"Zeus\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Super Trend","config":"{ \n\"codeName\": \"Super-Trend\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":" {\n \"codeName\": \"Multi-Period-Market\",\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Super Trend","project":"Superalgos","id":"2412939e-1585-47f9-8d09-7ea8dd7c8bf0","savedPayload":{"position":{"x":22032.236320825206,"y":18198.423702743665},"targetPosition":{"x":22071.059177590585,"y":18343.312576687025},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7c8e7a6f-99c8-4d90-9f44-e3e9e909eb88"}}}],"outputDatasetFolders":[],"id":"74212c63-4a69-407d-80ec-d39ee2d8df74","savedPayload":{"position":{"x":22071.059177590585,"y":18343.312576687025},"targetPosition":{"x":22109.882034355964,"y":18488.201450630386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Super Trend Multi-Period-Market","config":"{ \n\"mainUtility\": \"Self Reference\"\n}","project":"Superalgos","id":"5a35d0d1-cfed-4893-84ab-344a77c7d252","savedPayload":{"position":{"x":22037.52774788672,"y":17938.616081608332},"targetPosition":{"x":22187.52774788672,"y":18198.423702743665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"25f05344-3e7f-4d5f-ab1c-d72689a85764"}}},{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{ \n\"mainUtility\": \"Market Ending Point\"\n}","project":"Superalgos","id":"2a42dd15-1e04-4826-bd5f-85d95fbae135","savedPayload":{"position":{"x":22265.17346141748,"y":17908.645954856944},"targetPosition":{"x":22187.52774788672,"y":18198.423702743665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a0a61ba9-91cb-4ec7-92fe-134ff079de2d"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Candles Multi-Period-Market","project":"Superalgos","id":"88a45f27-a0b8-4234-b7aa-f139400305db","savedPayload":{"position":{"x":22447.335369022054,"y":18048.423702743665},"targetPosition":{"x":22187.52774788672,"y":18198.423702743665},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2b03494a-67d6-47f1-89b6-53fc4c9116a8"}}}],"dataMineDataDependencies":[],"id":"eef38121-80a2-4471-b909-24acdc4d213a","savedPayload":{"position":{"x":22187.52774788672,"y":18198.423702743665},"targetPosition":{"x":22109.882034355964,"y":18488.201450630386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"Superalgos","id":"25f05344-3e7f-4d5f-ab1c-d72689a85764","savedPayload":{"position":{"x":22322.014068711927,"y":18276.069416274422},"targetPosition":{"x":22109.882034355964,"y":18488.201450630386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"d9b0ffbd-c1bd-4e43-a00d-04a6b6fb470b","savedPayload":{"position":{"x":22399.659782242685,"y":18410.55573709963},"targetPosition":{"x":22109.882034355964,"y":18488.201450630386},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"1824fa00-dd30-4cdc-acc0-01c2f29056c5"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","project":"Superalgos","id":"d405bfe9-74f9-4419-8288-94229cLine truncated
{"type":"Data Mine","name":"Zeus","config":"{ \n\"codeName\": \"Zeus\"\n}","project":"Superalgos","sensorBots":[],"indicatorBots":[{"type":"Indicator Bot","name":"Super Trend","config":"{ \n\"codeName\": \"Super-Trend\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Multi-Period-Market","config":" {\n \"codeName\": \"Multi-Period-Market\",\n \"normalWaitTime\": 0,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Multi-Period-Market\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Super Trend","project":"Superalgos","id":"2412939e-1585-47f9-8d09-7ea8dd7c8bf0","savedPayload":{"position":{"x":21786.83642206419,"y":17775.938049371594},"targetPosition":{"x":21825.659278829567,"y":17920.826923314955},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"7c8e7a6f-99c8-4d90-9f44-e3e9e909eb88"}}}],"outputDatasetFolders":[],"id":"74212c63-4a69-407d-80ec-d39ee2d8df74","savedPayload":{"position":{"x":21825.659278829567,"y":17920.826923314955},"targetPosition":{"x":21864.482135594946,"y":18065.715797258315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":2,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"processDependencies":{"type":"Process Dependencies","name":"New Process Dependencies","project":"Superalgos","statusDependencies":[{"type":"Status Dependency","name":"Super Trend Multi-Period-Market","config":"{ \n\"mainUtility\": \"Self Reference\"\n}","project":"Superalgos","id":"5a35d0d1-cfed-4893-84ab-344a77c7d252","savedPayload":{"position":{"x":21792.127849125704,"y":17516.13042823626},"targetPosition":{"x":21942.127849125704,"y":17775.938049371594},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"25f05344-3e7f-4d5f-ab1c-d72689a85764"}}},{"type":"Status Dependency","name":"Candles Volumes Multi-Period-Market","config":"{ \n\"mainUtility\": \"Market Ending Point\"\n}","project":"Superalgos","id":"2a42dd15-1e04-4826-bd5f-85d95fbae135","savedPayload":{"position":{"x":22019.77356265646,"y":17486.160301484873},"targetPosition":{"x":21942.127849125704,"y":17775.938049371594},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Status Report","name":"New Status Report","id":"a0a61ba9-91cb-4ec7-92fe-134ff079de2d"}}}],"dataDependencies":[{"type":"Data Dependency","name":"Candles Multi-Period-Market","project":"Superalgos","id":"88a45f27-a0b8-4234-b7aa-f139400305db","savedPayload":{"position":{"x":22201.935470261036,"y":17625.938049371594},"targetPosition":{"x":21942.127849125704,"y":17775.938049371594},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"2b03494a-67d6-47f1-89b6-53fc4c9116a8"}}}],"dataMineDataDependencies":[],"id":"eef38121-80a2-4471-b909-24acdc4d213a","savedPayload":{"position":{"x":21942.127849125704,"y":17775.938049371594},"targetPosition":{"x":21864.482135594946,"y":18065.715797258315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"statusReport":{"type":"Status Report","name":"New Status Report","project":"Superalgos","id":"25f05344-3e7f-4d5f-ab1c-d72689a85764","savedPayload":{"position":{"x":22076.61416995091,"y":17853.58376290235},"targetPosition":{"x":21864.482135594946,"y":18065.715797258315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"executionStartedEvent":{"type":"Execution Started Event","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"d9b0ffbd-c1bd-4e43-a00d-04a6b6fb470b","savedPayload":{"position":{"x":22154.259883481667,"y":17988.070083727558},"targetPosition":{"x":21864.482135594946,"y":18065.715797258315},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Execution Finished Event","name":"New Execution Finished Event","id":"1824fa00-dd30-4cdc-acc0-01c2f29056c5"}}},"executionFinishedEvent":{"type":"Execution Finished Event","name":"New Execution Finished Event","project":"Superalgos","id":"d405bfe9-74f9-4419-8288-9422Line truncated
@@ -1 +1 @@
{"type":"Trading Mine","name":"Masters","config":"{ \n\"codeName\": \"Masters\"\n}","project":"Superalgos","tradingBots":[{"type":"Trading Bot","name":"Low Frequency","config":"{\n\"codeName\": \"Low-Frequency\",\n\"repo\": \"Exchange-Raw-Data-Sensor-Bot\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Trading","config":"{\n \"codeName\": \"Trading\",\n \"sessionRunWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Low-Frequency-Trading-Process\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[],"outputDatasetFolders":[{"type":"Output Dataset Folder","name":"Trading Engine","project":"Superalgos","outputDatasets":[],"outputDatasetFolders":[{"type":"Output Dataset Folder","name":"Episode","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Episode Counters","project":"Superalgos","id":"ec8d74ba-a4ad-417a-b479-6bae95b8a1fe","savedPayload":{"position":{"x":24910.97457254189,"y":18457.46644293786},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"3ed03a02-381d-43fe-9faa-761728c53bd8"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Counters","project":"Superalgos","id":"22b0af33-8c2f-4194-839b-affd18f298d5","savedPayload":{"position":{"x":24834.601232363748,"y":18490.07023931273},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"15f5b150-f1cf-403c-8cb9-2c7c4e1db8e8"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Statistics","project":"Superalgos","id":"23ab91ae-2e78-4993-bb69-855f69cef1ce","savedPayload":{"position":{"x":24753.53736157276,"y":18508.085329665024},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"fc9dc0f7-35e0-4548-bb91-f920825d694f"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Statistics","project":"Superalgos","id":"d0a4515a-279d-4c27-8de2-168108861242","savedPayload":{"position":{"x":24670.543493063306,"y":18510.89823169951},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"91953362-8751-4440-a898-21245cd47c15"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Candle","project":"Superalgos","id":"7fc80977-5b37-413e-8107-0a89a16e08b0","savedPayload":{"position":{"x":24588.445883487308,"y":18498.413155411115},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"17fb41f5-61b5-4433-9a10-950ff8216a7e"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Candle","project":"Superalgos","id":"a6888750-1043-420c-9253-633e1399c5e4","savedPayload":{"position":{"x":24510.040268473647,"y":18471.05526509864},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"bbc9467d-28bf-432b-9e49-582d1bcabfb9"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Balances","project":"Superalgos","id":"0ad114df-f076-4a86-8c62-4c4a146da73c","savedPayload":{"position":{"x":24437.996657204443,"y":18429.756200904503},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"acddff59-b51b-4b45-b58f-71d4077755ee"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Balances","project":"Superalgos","id":"04b51302-957d-4365-ad55-9f89d60e261c","savedPayload":{"position":{"x":24374.76840845468,"y":18375.922352927064},"targetPosition":{"x":24696.86240363471,"y":18061.6685410663Line truncated
{"type":"Trading Mine","name":"Masters","config":"{ \n\"codeName\": \"Masters\"\n}","project":"Superalgos","tradingBots":[{"type":"Trading Bot","name":"Low Frequency","config":"{\n\"codeName\": \"Low-Frequency\"\n}","project":"Superalgos","processes":[{"type":"Process Definition","name":"Trading","config":"{\n \"codeName\": \"Trading\",\n \"sessionRunWaitTime\": 5000,\n \"retryWaitTime\": 10000,\n \"framework\": {\n \"name\": \"Low-Frequency-Trading-Process\"\n }\n }","project":"Superalgos","processOutput":{"type":"Process Output","name":"New Process Output","project":"Superalgos","outputDatasets":[],"outputDatasetFolders":[{"type":"Output Dataset Folder","name":"Trading Engine","project":"Superalgos","outputDatasets":[],"outputDatasetFolders":[{"type":"Output Dataset Folder","name":"Episode","project":"Superalgos","outputDatasets":[{"type":"Output Dataset","name":"Multi-Period-Market Episode Counters","project":"Superalgos","id":"ec8d74ba-a4ad-417a-b479-6bae95b8a1fe","savedPayload":{"position":{"x":24910.97457254189,"y":18457.46644293786},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"3ed03a02-381d-43fe-9faa-761728c53bd8"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Counters","project":"Superalgos","id":"22b0af33-8c2f-4194-839b-affd18f298d5","savedPayload":{"position":{"x":24834.601232363748,"y":18490.07023931273},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"15f5b150-f1cf-403c-8cb9-2c7c4e1db8e8"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Statistics","project":"Superalgos","id":"23ab91ae-2e78-4993-bb69-855f69cef1ce","savedPayload":{"position":{"x":24753.53736157276,"y":18508.085329665024},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"fc9dc0f7-35e0-4548-bb91-f920825d694f"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Statistics","project":"Superalgos","id":"d0a4515a-279d-4c27-8de2-168108861242","savedPayload":{"position":{"x":24670.543493063306,"y":18510.89823169951},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"91953362-8751-4440-a898-21245cd47c15"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Candle","project":"Superalgos","id":"7fc80977-5b37-413e-8107-0a89a16e08b0","savedPayload":{"position":{"x":24588.445883487308,"y":18498.413155411115},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"17fb41f5-61b5-4433-9a10-950ff8216a7e"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Candle","project":"Superalgos","id":"a6888750-1043-420c-9253-633e1399c5e4","savedPayload":{"position":{"x":24510.040268473647,"y":18471.05526509864},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Daily","id":"bbc9467d-28bf-432b-9e49-582d1bcabfb9"}}},{"type":"Output Dataset","name":"Multi-Period-Market Episode Balances","project":"Superalgos","id":"0ad114df-f076-4a86-8c62-4c4a146da73c","savedPayload":{"position":{"x":24437.996657204443,"y":18429.756200904503},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{},"referenceParent":{"type":"Dataset Definition","name":"Multi-Period-Market","id":"acddff59-b51b-4b45-b58f-71d4077755ee"}}},{"type":"Output Dataset","name":"Multi-Period-Daily Episode Balances","project":"Superalgos","id":"04b51302-957d-4365-ad55-9f89d60e261c","savedPayload":{"position":{"x":24374.76840845468,"y":18375.922352927064},"targetPosition":{"x":24696.86240363471,"y":18061.668541066385},"floatingObject":{"isPinned":false,"isFroLine truncated
@@ -0,0 +1 @@
{"type":"Trading System","name":"BB Top Bounce","config":"{ \n\"codeName\": \"\"\n}","project":"Superalgos","tradingStrategies":[{"type":"Trading Strategy","name":"S1 - 3H Down","project":"Superalgos","triggerStage":{"type":"Trigger Stage","name":"New Trigger Stage","project":"Superalgos","triggerOn":{"type":"Trigger On Event","name":"New Trigger On Event","project":"Superalgos","situations":[{"type":"Situation","name":"New Situation","project":"Superalgos","conditions":[{"type":"Condition","name":"Previous Breakup","project":"Superalgos","javascriptCode":{"type":"Javascript Code","name":"New Javascript Code","code":"chart.at01hs.candle.previous.close > chart.at01hs.bollingerBand.previous.movingAverage + chart.at01hs.bollingerBand.previous.deviation","project":"Superalgos","id":"99948a6e-e866-4339-86ba-eac367226312","savedPayload":{"position":{"x":30780.386277758334,"y":30051.498744389715},"targetPosition":{"x":31072.097253877637,"y":30121.53235354649},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"f0534c68-ab47-4370-b1fd-b3be4c1c262f","savedPayload":{"position":{"x":31072.097253877637,"y":30121.53235354649},"targetPosition":{"x":31363.80822999694,"y":30191.565962703262},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Condition","name":"Current Close In","project":"Superalgos","javascriptCode":{"type":"Javascript Code","name":"New Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.bollingerBand.movingAverage + chart.at01hs.bollingerBand.deviation","project":"Superalgos","id":"d3bd1690-1f94-484b-ac3c-7be2c7be85ed","savedPayload":{"position":{"x":30878.398033371974,"y":29838.894811327777},"targetPosition":{"x":31121.103131684456,"y":30015.23038701552},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"e2d7334d-cf52-44d9-a20e-5be201ca6799","savedPayload":{"position":{"x":31121.103131684456,"y":30015.23038701552},"targetPosition":{"x":31363.80822999694,"y":30191.565962703262},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Condition","name":"Current Red Candle","project":"Superalgos","javascriptCode":{"type":"Javascript Code","name":"New Javascript Code","code":"chart.at01hs.candle.close < chart.at01hs.candle.open","project":"Superalgos","id":"d1fa707f-eb75-4b1c-8117-688b9b94e539","savedPayload":{"position":{"x":31050.30909116737,"y":29679.98186409081},"targetPosition":{"x":31207.058660582155,"y":29935.773913397035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"fb71bcfb-2a40-4e8a-b8e8-b492128350a0","savedPayload":{"position":{"x":31207.058660582155,"y":29935.773913397035},"targetPosition":{"x":31363.80822999694,"y":30191.565962703262},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Condition","name":"3H Down","code":"chart.at03hs.bollingerChannel.direction * chart.atAnyTimeFrame.externalSignal.timestamp > chart.atAnyTimeFrame.externalSignal.timestamp","project":"Superalgos","javascriptCode":{"type":"Javascript Code","name":"New Javascript Code","code":"chart.at03hs.bollingerChannel.direction === \"Down\"","project":"Superalgos","id":"8c8c5128-b88b-44a5-8e36-d5c61127404f","savedPayload":{"position":{"x":31269.9475509728,"y":29598.95295834618},"targetPosition":{"x":31316.87789048487,"y":29895.25946052472},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{}}},"id":"22246178-a8f9-4ec8-b55b-36cf7a24fbff","savedPayload":{"position":{"x":31316.87789048487,"y":29895.25946052472},"targetPosition":{"x":31363.80822999694,"y":30191.565962703262},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Condition","name":"3H Medium, Steep or Extreme","project":"Superalgos","javascriptCode":{"type":"Javascript Code","name":"New Javascript Code","code":"chart.at03hs.bollingerSubChannel.slope === \"Medium\" || chart.at03hs.bollingerSubChannel.slope === \"Steep\" || chart.at03hs.bollingerSubChannel.slope === \"Extreme\" ","project":"Superalgos","id":"bf8df41c-c62f-4cd6-840e-1dd538359c3b","savedPayload":{"position":{"x":31503.875448310486,"y":29608.144010464657},"targetPosition":{"x":31433.841839153712,"y":29899.85498658396},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"diLine truncated
@@ -1 +1 @@
{"type":"Workspace","name":"BB Top Bounce","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataMining":{"type":"Data Mining","name":"New Data Mining","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"3d9fe97d-72ad-4342-89f6-8bda778f2609","savedPayload":{"position":{"x":23467.55083912308,"y":30317.55889397751},"targetPosition":{"x":23741.61447641586,"y":30195.537901054773},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"b729b63c-1bd7-4fa9-9f5a-67ce189c5795","savedPayload":{"position":{"x":23741.61447641586,"y":30195.537901054773},"targetPosition":{"x":24015.67811370864,"y":30073.516908132035},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"b1fa524f-0b97-477e-bb46-8860841619bd","savedPayload":{"position":{"x":24015.67811370864,"y":30073.516908132035},"targetPosition":{"x":24289.74175100142,"y":29951.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"624dfa33-e98e-4850-868d-c7efba73f277","savedPayload":{"position":{"x":23403.264330134767,"y":29842.449687591525},"targetPosition":{"x":23702.853190561138,"y":29826.74890071864},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"63fe5d3e-42c7-4223-9f56-579e1b3c1999","savedPayload":{"position":{"x":23435.551233304628,"y":29690.551750796778},"targetPosition":{"x":23702.853190561138,"y":29826.74890071864},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"980ceb1d-7be6-454f-90d9-01ba1fd4a65d"}}}],"id":"6ccc1257-ceee-4134-a3e2-dec8224d2eff","savedPayload":{"position":{"x":23702.853190561138,"y":29826.74890071864},"targetPosition":{"x":23996.29747078128,"y":29889.12240796397},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"ff410bdc-7db8-4260-ad27-a3ec244c5bb3","savedPayload":{"position":{"x":23996.29747078128,"y":29889.12240796397},"targetPosition":{"x":24289.74175100142,"y":29951.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Bollinger Bands","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Bollinger Bands","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Bollinger Bands Multi-Period-Market","project":"Superalgos","id":"5aaf5b42-4379-4d23-926a-f15eb36c36be","savedPayload":{"position":{"x":23636.662216802477,"y":29342.217309418353},"targetPosition":{"x":23888.263387186104,"y":29505.60901992286},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}},{"type":"IndLine truncated
{"type":"Workspace","name":"BB Top Bounce","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[{"type":"Project Data Tasks","name":"New Project Definition","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Sparta","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Sparta","project":"Superalgos","tasks":[{"type":"Task","name":"Simple Moving Average (SMA)","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Simple Moving Average (SMA)","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Simple Moving Average (SMA) Multi-Period-Market","project":"Superalgos","id":"64a0f312-b72e-494c-8802-70ebb6238c8b","savedPayload":{"position":{"x":24860.356993272086,"y":30929.11115747996},"targetPosition":{"x":24927.356993272086,"y":31018.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"456269f6-9849-4d5f-9c8c-7cd7d1eef297"}}},{"type":"Indicator Process Instance","name":"Simple Moving Average (SMA) Multi-Period-Daily","project":"Superalgos","id":"7a44ff20-1251-4157-9741-a4c2881b179e","savedPayload":{"position":{"x":24962.356993272086,"y":31086.11115747996},"targetPosition":{"x":24927.356993272086,"y":31018.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"333ae5d7-7ec1-4f08-b206-91e3b4f2c99f"}}}],"id":"ef39e485-c27d-443b-aa74-b10b8f8d10ad","savedPayload":{"position":{"x":24927.356993272086,"y":31018.11115747996},"targetPosition":{"x":24963.356993272086,"y":31085.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"01a5f038-ab20-44fe-99b0-0d5d3c0d4bcb","savedPayload":{"position":{"x":24963.356993272086,"y":31085.11115747996},"targetPosition":{"x":24878.356993272086,"y":31053.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Exponential Moving Average (EMA)","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Exponential Moving Average (EMA)","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Exponential Moving Average (EMA) Multi-Period-Market","project":"Superalgos","id":"c95597e5-5d5c-4dd7-9510-99874a022eac","savedPayload":{"position":{"x":24816.356993272086,"y":31085.11115747996},"targetPosition":{"x":24789.356993272086,"y":31068.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"278c34a7-199d-400e-8a06-087149e3d391"}}},{"type":"Indicator Process Instance","name":"Exponential Moving Average (EMA) Multi-Period-Daily","project":"Superalgos","id":"e588dc58-d99c-4938-aaab-747d23408301","savedPayload":{"position":{"x":24833.356993272086,"y":31119.11115747996},"targetPosition":{"x":24789.356993272086,"y":31068.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"943ea24f-ee24-4eae-9b4b-e8a73122b1c2"}}}],"id":"44fefa5e-341a-43c7-8967-7545a6e229ad","savedPayload":{"position":{"x":24789.356993272086,"y":31068.11115747996},"targetPosition":{"x":24874.356993272086,"y":31135.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"1e55a456-ff9c-48bf-b9ea-01082495fcc9","savedPayload":{"position":{"x":24874.356993272086,"y":31135.11115747996},"targetPosition":{"x":24878.356993272086,"y":31053.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"Line truncated
@@ -1 +1 @@
{"type":"Workspace","name":"Exchange Keys Tests","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataMining":{"type":"Data Mining","name":"New Data Mining","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2017-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"f17f9567-af83-4292-a0dc-7c1c9dbf53b8","savedPayload":{"position":{"x":23640.023749611923,"y":29498.94353234636},"targetPosition":{"x":23929.801497498644,"y":29576.589245877116},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"9eaa4440-c8bc-4daa-b858-efa1845087de","savedPayload":{"position":{"x":23929.801497498644,"y":29576.589245877116},"targetPosition":{"x":24219.579245385365,"y":29654.234959407873},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"97e5a05e-9463-41a4-8255-a9487df56496","savedPayload":{"position":{"x":24219.579245385365,"y":29654.234959407873},"targetPosition":{"x":24509.356993272086,"y":29731.88067293863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"1ef6762d-a20c-46e0-8183-6aac6dd9073a","savedPayload":{"position":{"x":23997.224958916122,"y":29000.133396312},"targetPosition":{"x":24209.356993272086,"y":29212.265430667965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"7b44275e-9446-4807-b9c2-4ba1107a5295","savedPayload":{"position":{"x":24131.71127974133,"y":28922.487682781244},"targetPosition":{"x":24209.356993272086,"y":29212.265430667965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"980ceb1d-7be6-454f-90d9-01ba1fd4a65d"}}}],"id":"78c835ef-be5b-4e74-8b0c-610afbbef304","savedPayload":{"position":{"x":24209.356993272086,"y":29212.265430667965},"targetPosition":{"x":24359.356993272086,"y":29472.073051803298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"b4da2db7-fcae-4918-b38a-5e6966a9994b","savedPayload":{"position":{"x":24359.356993272086,"y":29472.073051803298},"targetPosition":{"x":24509.356993272086,"y":29731.88067293863},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Bollinger Bands","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Bollinger Bands","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Bollinger Bands Multi-Period-Market","project":"Superalgos","id":"22a8694f-078e-4f1f-8470-7a7b25237397","savedPayload":{"position":{"x":24664.6484203336,"y":28852.32517716519},"targetPosition":{"x":24664.6484203336,"y":29152.32517716519},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"941a0543-0cc0-4a5d-a782-728d2835c100"}}},{"tLine truncated
{"type":"Workspace","name":"Exchange Keys Tests","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[{"type":"Project Data Tasks","name":"Superalgos","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2017-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"f17f9567-af83-4292-a0dc-7c1c9dbf53b8","savedPayload":{"position":{"x":23148.696821492267,"y":29710.450985700143},"targetPosition":{"x":23448.696821492267,"y":29710.450985700143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"9eaa4440-c8bc-4daa-b858-efa1845087de","savedPayload":{"position":{"x":23448.696821492267,"y":29710.450985700143},"targetPosition":{"x":23748.696821492267,"y":29710.450985700143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"97e5a05e-9463-41a4-8255-a9487df56496","savedPayload":{"position":{"x":23748.696821492267,"y":29710.450985700143},"targetPosition":{"x":24048.696821492267,"y":29710.450985700143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"1ef6762d-a20c-46e0-8183-6aac6dd9073a","savedPayload":{"position":{"x":23364.625131645007,"y":29136.186916988216},"targetPosition":{"x":23624.43275278034,"y":29286.186916988216},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"7b44275e-9446-4807-b9c2-4ba1107a5295","savedPayload":{"position":{"x":23474.43275278034,"y":29026.379295852883},"targetPosition":{"x":23624.43275278034,"y":29286.186916988216},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"980ceb1d-7be6-454f-90d9-01ba1fd4a65d"}}}],"id":"78c835ef-be5b-4e74-8b0c-610afbbef304","savedPayload":{"position":{"x":23624.43275278034,"y":29286.186916988216},"targetPosition":{"x":23836.564787136303,"y":29498.31895134418},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"b4da2db7-fcae-4918-b38a-5e6966a9994b","savedPayload":{"position":{"x":23836.564787136303,"y":29498.31895134418},"targetPosition":{"x":24048.696821492267,"y":29710.450985700143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Bollinger Bands","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Bollinger Bands","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Bollinger Bands Multi-Period-Market","project":"Superalgos","id":"22a8694f-078e-4f1f-8470-7a7b25237397","savedPayload":{"position":{"x":23971.05110796151,"y":28820.673237813422},"targetPosition":{"x":24048.696821492267,"y":29110.450985700143},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"ProcLine truncated
@@ -1 +1 @@
{"type":"Workspace","name":"Getting Started","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataMining":{"type":"Data Mining","name":"New Data Mining","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"d244c564-4e86-43c7-9796-dc3b6294ec8b","savedPayload":{"position":{"x":23993.925629940142,"y":29195.742554406977},"targetPosition":{"x":24261.227587196652,"y":29331.93970432884},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"343313ae-5705-484d-8bf8-aed25ba61cfa","savedPayload":{"position":{"x":24261.227587196652,"y":29331.93970432884},"targetPosition":{"x":24528.529544453162,"y":29468.136854250704},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"a8f38508-1cac-4ab1-815f-d338fc813377","savedPayload":{"position":{"x":24528.529544453162,"y":29468.136854250704},"targetPosition":{"x":24795.831501709672,"y":29604.334004172568},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"f0ef12ed-f0fd-4b88-8e15-27cf24265a5f","savedPayload":{"position":{"x":24387.47785744149,"y":28832.96091248782},"targetPosition":{"x":24610.421305084707,"y":29033.700094395477},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"7c8b312c-09bf-4c59-853f-1592391a9c57","savedPayload":{"position":{"x":24672.794812330034,"y":28740.255814175336},"targetPosition":{"x":24610.421305084707,"y":29033.700094395477},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"980ceb1d-7be6-454f-90d9-01ba1fd4a65d"}}}],"id":"8d58e84b-d7ef-464f-8079-0ec0887e3da3","savedPayload":{"position":{"x":24610.421305084707,"y":29033.700094395477},"targetPosition":{"x":24703.12640339719,"y":29319.017049284023},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"bf29e3bd-9386-408b-9091-042da0c53e81","savedPayload":{"position":{"x":24703.12640339719,"y":29319.017049284023},"targetPosition":{"x":24795.831501709672,"y":29604.334004172568},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Resistances and Supports","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Resistances and Supports","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Resistances and Supports Multi-Period-Market","project":"Superalgos","id":"a707814e-b81b-4102-9b5b-4e2fe969324f","savedPayload":{"position":{"x":25052.525014680516,"y":28770.141229233177},"targetPosition":{"x":25068.2258015534,"y":29069.730089659548},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"5d9b2729-43fb-4eLine truncated
{"type":"Workspace","name":"Getting Started","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[{"type":"Project Data Tasks","name":"Superalgos","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"d244c564-4e86-43c7-9796-dc3b6294ec8b","savedPayload":{"position":{"x":23490.023749611923,"y":29239.135911211026},"targetPosition":{"x":23779.801497498644,"y":29316.781624741783},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"343313ae-5705-484d-8bf8-aed25ba61cfa","savedPayload":{"position":{"x":23779.801497498644,"y":29316.781624741783},"targetPosition":{"x":24069.579245385365,"y":29394.42733827254},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"a8f38508-1cac-4ab1-815f-d338fc813377","savedPayload":{"position":{"x":24069.579245385365,"y":29394.42733827254},"targetPosition":{"x":24359.356993272086,"y":29472.073051803298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"f0ef12ed-f0fd-4b88-8e15-27cf24265a5f","savedPayload":{"position":{"x":23799.549372136753,"y":28802.457809532632},"targetPosition":{"x":24059.356993272086,"y":28952.457809532632},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"7c8b312c-09bf-4c59-853f-1592391a9c57","savedPayload":{"position":{"x":24059.356993272086,"y":28652.457809532632},"targetPosition":{"x":24059.356993272086,"y":28952.457809532632},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"980ceb1d-7be6-454f-90d9-01ba1fd4a65d"}}}],"id":"8d58e84b-d7ef-464f-8079-0ec0887e3da3","savedPayload":{"position":{"x":24059.356993272086,"y":28952.457809532632},"targetPosition":{"x":24209.356993272086,"y":29212.265430667965},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"bf29e3bd-9386-408b-9091-042da0c53e81","savedPayload":{"position":{"x":24209.356993272086,"y":29212.265430667965},"targetPosition":{"x":24359.356993272086,"y":29472.073051803298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Resistances and Supports","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Resistances and Supports","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Resistances and Supports Multi-Period-Market","project":"Superalgos","id":"a707814e-b81b-4102-9b5b-4e2fe969324f","savedPayload":{"position":{"x":24437.002706802843,"y":28602.739808143135},"targetPosition":{"x":24514.6484203336,"y":28892.517556029856},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"refeLine truncated
@@ -1 +1 @@
{"type":"Workspace","name":"Trading System Design Tutorials","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataMining":{"type":"Data Mining","name":"New Data Mining","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[],"id":"af949a76-a6f4-4ec6-869e-f240480eea81","savedPayload":{"position":{"x":24959.356993272086,"y":30511.30353634463},"targetPosition":{"x":25109.356993272086,"y":30771.11115747996},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Crypto Exchange","name":"Binance","id":"61ef0b8d-de6e-4e76-bdd5-996836760a4b"}}}],"id":"4df1cb4e-b9ca-4f62-bb5f-c7e262aaa22a","savedPayload":{"position":{"x":25109.356993272086,"y":30771.11115747996},"targetPosition":{"x":25259.356993272086,"y":31030.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"learningMining":{"type":"Learning Mining","name":"New Learning Mining","project":"Superalgos","exchangeLearningTasks":[],"id":"1b19735e-0041-438f-824b-00df4f8942dd","savedPayload":{"position":{"x":25409.356993272086,"y":30771.11115747996},"targetPosition":{"x":25259.356993272086,"y":31030.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"testingEnvironment":{"type":"Testing Environment","name":"New Testing Environment","project":"Superalgos","exchangeTradingTasks":[{"type":"Exchange Trading Tasks","name":"Binance","project":"Superalgos","marketTradingTasks":[],"id":"ba36975c-08d6-458d-b7c0-eb4684a85b5d","savedPayload":{"position":{"x":25859.356993272086,"y":31030.918778615294},"targetPosition":{"x":25559.356993272086,"y":31030.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Crypto Exchange","name":"Binance","id":"61ef0b8d-de6e-4e76-bdd5-996836760a4b"}}}],"id":"5882d8a8-345b-4188-b1c1-797e9ff805d9","savedPayload":{"position":{"x":25559.356993272086,"y":31030.918778615294},"targetPosition":{"x":25259.356993272086,"y":31030.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"productionEnvironment":{"type":"Production Environment","name":"New Production Environment","project":"Superalgos","exchangeTradingTasks":[{"type":"Exchange Trading Tasks","name":"Binance","project":"Superalgos","marketTradingTasks":[],"id":"d539c0ac-aaeb-4c44-9dba-ac26df819a8d","savedPayload":{"position":{"x":25559.356993272086,"y":31550.53402088596},"targetPosition":{"x":25409.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Crypto Exchange","name":"Binance","id":"61ef0b8d-de6e-4e76-bdd5-996836760a4b"}}}],"id":"65a40d20-837c-4652-8356-f8054b4e1346","savedPayload":{"position":{"x":25409.356993272086,"y":31290.726399750627},"targetPosition":{"x":25259.356993272086,"y":31030.918778615294},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":true,"angleToParent":1,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false,"shortcutKey":"u"}}},"dataStorage":{"type":"Data Storage","name":"New Data Storage","project":"Superalgos","dataMinesData":{"type":"Data Mines Data","name":"New Data Mines Data","project":"Superalgos","exchangeDataProducts":[{"type":"Exchange Data Products","name":"Binance","project":"Superalgos","marketDataProducts":[],"id":"9bc0aea4-0596-449c-91bd-4fb4cb672aa8","savedPayload":{"position":{"x":25264.6484203336,"y":31870.28189552407},"targetPosition":{"x":25187.002706802843,"y":31580.504147637348},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Crypto Exchange","name":"Binance","id":"61ef0b8d-de6e-4e76-bdd5-996836760a4b"}}}],"id":"32e7c122-fa5a-4d00-9d71-d54e890e32de","savedPayload":{"position":{"x":25187.002706802843,"y":31580.504147637348},"targetPosition":{"x":25109.356993272086,"y":31290.726399750627},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"tradingMineLine truncated
{"type":"Workspace","name":"Trading System Design Tutorials","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[{"type":"Project Data Tasks","name":"Superalgos","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"d0ff77d8-76de-4d50-840a-e50a55ad7d1b","savedPayload":{"position":{"x":23464.287287440642,"y":29377.997434862405},"targetPosition":{"x":23762.643856051123,"y":29409.355973842703},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"b4e33f51-f9ff-4ef3-a9b7-ccb904163cae","savedPayload":{"position":{"x":23762.643856051123,"y":29409.355973842703},"targetPosition":{"x":24061.000424661604,"y":29440.714512823},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"23ae53f9-b478-4eb4-96e7-12d07d7ae4f0","savedPayload":{"position":{"x":24061.000424661604,"y":29440.714512823},"targetPosition":{"x":24359.356993272086,"y":29472.073051803298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Candles Volumes","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Candles Volumes","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Market","project":"Superalgos","id":"dcf3d790-5362-4a95-b977-82d0cf0e07f1","savedPayload":{"position":{"x":23646.16814072914,"y":28934.397538066118},"targetPosition":{"x":23913.47009798565,"y":29070.59468798798},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"9f683143-a33f-42c1-9fa8-c601f01ee4e1"}}},{"type":"Indicator Process Instance","name":"Candles Volumes Multi-Period-Daily","project":"Superalgos","id":"775fadbd-5b55-41b2-81ac-454f5e52dca6","savedPayload":{"position":{"x":23750.078387481142,"y":28818.993517604355},"targetPosition":{"x":23913.47009798565,"y":29070.59468798798},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"980ceb1d-7be6-454f-90d9-01ba1fd4a65d"}}}],"id":"fc478301-77ac-4396-bd3b-915b4c681fa0","savedPayload":{"position":{"x":23913.47009798565,"y":29070.59468798798},"targetPosition":{"x":24136.413545628868,"y":29271.33386989564},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"30054a4b-7c93-4a3a-a6db-2e0d38507b2e","savedPayload":{"position":{"x":24136.413545628868,"y":29271.33386989564},"targetPosition":{"x":24359.356993272086,"y":29472.073051803298},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},{"type":"Task","name":"Bollinger Bands","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"Bollinger Bands","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Bollinger Bands Multi-Period-Market","project":"Superalgos","id":"bf0f739f-98b7-4c73-ae06-8d3fe83147ce","savedPayload":{"position":{"x":24098.412828859568,"y":28617.882534106506},"targetPosition":{"x":24234.60997878143,"y":28885.184491363016},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":3,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"PLine truncated
@@ -1 +1 @@
{"type":"Workspace","name":"Weak-hands Buster v2","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataMining":{"type":"Data Mining","name":"New Data Mining","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Sparta","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Sparta","project":"Superalgos","tasks":[{"type":"Task","name":"EMA","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"EMA","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Exponential Moving Average (EMA) Multi-Period-Market","project":"Superalgos","id":"9adffd60-8756-44f0-9290-692711f43349","savedPayload":{"position":{"x":23470.126508730755,"y":29652.495915209296},"targetPosition":{"x":23770.126508730755,"y":29652.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"278c34a7-199d-400e-8a06-087149e3d391"}}},{"type":"Indicator Process Instance","name":"Exponential Moving Average (EMA) Multi-Period-Daily","project":"Superalgos","id":"559fc728-018a-4e8e-a5fa-1e37958e4f6c","savedPayload":{"position":{"x":23620.126508730755,"y":29392.688294073963},"targetPosition":{"x":23770.126508730755,"y":29652.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"943ea24f-ee24-4eae-9b4b-e8a73122b1c2"}}}],"id":"82a8c05f-a6f6-4728-8271-7b327c2b72ee","savedPayload":{"position":{"x":23770.126508730755,"y":29652.495915209296},"targetPosition":{"x":24029.934129866087,"y":29802.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"8ae4fc50-8c1f-4e45-9ffd-fad368816b9a","savedPayload":{"position":{"x":24029.934129866087,"y":29802.495915209296},"targetPosition":{"x":24289.74175100142,"y":29952.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}}],"id":"51b49b9d-cb97-4d98-ad8f-9f691320bac2","savedPayload":{"position":{"x":24289.74175100142,"y":29952.495915209296},"targetPosition":{"x":24549.549372136753,"y":30102.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}}],"id":"63728769-73e4-474e-8aa3-0d82bedca08e","savedPayload":{"position":{"x":24549.549372136753,"y":30102.495915209296},"targetPosition":{"x":24809.356993272086,"y":30252.495915209296},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Data Mine","name":"Sparta","id":"9cfffc48-486a-48cb-b641-98c82e314d3a"}}},{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"149bc037-9762-4737-b1b8-945b5ccee230","savedPayload":{"position":{"x":24081.241698334637,"y":29123.489188146068},"targetPosition":{"x":24323.94679664712,"y":29299.82476383381},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"507bc325-1cbd-40f4-85eb-cfe77b78000e","savedPayload":{"position":{"x":24323.94679664712,"y":29299.82476383381},"targetPosition":{"x":24566.651894959603,"y":29476.160339521553},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"7c28a4a4-86da-43be-9e89-d01b202f27eb","savedPayload":{"position":{"x":24566.651894959603,"y":29476.160339521553},"targetPosition"Line truncated
{"type":"Workspace","name":"Weak-hands Buster v2","rootNodes":[{"type":"Network","name":"My Network","project":"Superalgos","networkNodes":[{"type":"Network Node","name":"My Computer","config":"{ \n\"host\": \"localhost\", \n\"webPort\": \"34248\", \n\"webSocketsPort\": \"18041\"\n}","project":"Superalgos","dataTasks":{"type":"Data Tasks","name":"New Data Tasks","project":"Superalgos","projectDataTasks":[{"type":"Project Data Tasks","name":"Superalgos","project":"Superalgos","exchangeDataTasks":[{"type":"Exchange Data Tasks","name":"Binance","project":"Superalgos","marketDataTasks":[{"type":"Market Data Tasks","name":"Binance BTC/USDT","project":"Superalgos","dataMineTasks":[{"type":"Data Mine Tasks","name":"Sparta","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Sparta","project":"Superalgos","tasks":[{"type":"Task","name":"EMA","project":"Superalgos","bot":{"type":"Indicator Bot Instance","name":"EMA","config":"{}","project":"Superalgos","processes":[{"type":"Indicator Process Instance","name":"Exponential Moving Average (EMA) Multi-Period-Market","project":"Superalgos","id":"9adffd60-8756-44f0-9290-692711f43349","savedPayload":{"position":{"x":23320.126508730755,"y":29392.688294073963},"targetPosition":{"x":23620.126508730755,"y":29392.688294073963},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Market","id":"278c34a7-199d-400e-8a06-087149e3d391"}}},{"type":"Indicator Process Instance","name":"Exponential Moving Average (EMA) Multi-Period-Daily","project":"Superalgos","id":"559fc728-018a-4e8e-a5fa-1e37958e4f6c","savedPayload":{"position":{"x":23470.126508730755,"y":29132.88067293863},"targetPosition":{"x":23620.126508730755,"y":29392.688294073963},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Multi-Period-Daily","id":"943ea24f-ee24-4eae-9b4b-e8a73122b1c2"}}}],"id":"82a8c05f-a6f6-4728-8271-7b327c2b72ee","savedPayload":{"position":{"x":23620.126508730755,"y":29392.688294073963},"targetPosition":{"x":23879.934129866087,"y":29542.688294073963},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"8ae4fc50-8c1f-4e45-9ffd-fad368816b9a","savedPayload":{"position":{"x":23879.934129866087,"y":29542.688294073963},"targetPosition":{"x":24139.74175100142,"y":29692.688294073963},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}}],"id":"51b49b9d-cb97-4d98-ad8f-9f691320bac2","savedPayload":{"position":{"x":24139.74175100142,"y":29692.688294073963},"targetPosition":{"x":24399.549372136753,"y":29842.688294073963},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}}],"id":"63728769-73e4-474e-8aa3-0d82bedca08e","savedPayload":{"position":{"x":24399.549372136753,"y":29842.688294073963},"targetPosition":{"x":24659.356993272086,"y":29992.688294073963},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Data Mine","name":"Sparta","id":"9cfffc48-486a-48cb-b641-98c82e314d3a"}}},{"type":"Data Mine Tasks","name":"Masters","project":"Superalgos","taskManagers":[{"type":"Task Manager","name":"Masters","project":"Superalgos","tasks":[{"type":"Task","name":"Exchange Raw Data","project":"Superalgos","bot":{"type":"Sensor Bot Instance","name":"Exchange Raw Data","config":"{ \n\"startDate\": \"2020-01-01\"\n}","project":"Superalgos","processes":[{"type":"Sensor Process Instance","name":"Exchange Raw Data Historic-OHLCVs","project":"Superalgos","id":"149bc037-9762-4737-b1b8-945b5ccee230","savedPayload":{"position":{"x":23931.241698334637,"y":28863.681567010735},"targetPosition":{"x":24173.94679664712,"y":29040.017142698478},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false},"referenceParent":{"type":"Process Definition","name":"Historic-OHLCVs","id":"5eb11d6a-022e-4f60-97c4-d6cf90b249dc"}}}],"id":"507bc325-1cbd-40f4-85eb-cfe77b78000e","savedPayload":{"position":{"x":24173.94679664712,"y":29040.017142698478},"targetPosition":{"x":24416.651894959603,"y":29216.35271838622},"floatingObject":{"isPinned":false,"isFrozen":false,"isCollapsed":false,"angleToParent":2,"distanceToParent":3,"arrangementStyle":0},"uiObject":{"isRunning":false}}},"id":"7c28a4a4-86da-43be-9e89-d01b202f27eb","sLine truncated
+547 -83
View File
@@ -25,6 +25,12 @@
"positionLocked": true,
"icon": "superalgos",
"properties": [
{
"name": "projectDefinition",
"type": "node",
"childType": "Project Definition",
"autoAdd": true
},
{
"name": "spaces",
"type": "node",
@@ -45,6 +51,40 @@
}
]
},
{
"type": "Project Definition",
"menuItems": [
{
"action": "Configure",
"actionFunction": "uiObject.configEditor.activate",
"label": "Configure",
"iconPathOn": "configuration",
"iconPathOff": "configuration"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"actionFunction": "payload.executeAction",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete"
}
],
"initialValues": {
"config": "{ \n \"codeName\": \"\"\n}"
},
"editors": {
"config": true
},
"alternativeIcons": "Use Parent",
"isTitleAllwaysVisible": false,
"addLeftIcons": true,
"isPinned": false,
"level": 2,
"propertyNameAtParent": "projectDefinition",
"icon": "bitcoin"
},
{
"type": "Spaces",
"menuItems": [
@@ -146,6 +186,12 @@
"relatedUiObject": "Tutorial",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"label": "Add Plugins",
"relatedUiObject": "Plugins",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
@@ -1945,7 +1991,7 @@
"compatibleTypes": "->Template Script->"
},
"referenceAttachesTo": {
"compatibleTypes": "->Dashboard->Data Mines Data->Trading Mines Data->Data Mining->Testing Environment->Production Environment->"
"compatibleTypes": "->Dashboard->Data Mines Data->Trading Mines Data->Data Tasks->Testing Trading Tasks->Production Trading Tasks->"
},
"properties": []
},
@@ -2028,33 +2074,33 @@
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
"propertyToCheckFor": "dataMining",
"label": "Add Data Mining",
"relatedUiObject": "Data Mining",
"propertyToCheckFor": "dataTasks",
"label": "Add Data Tasks",
"relatedUiObject": "Data Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
"propertyToCheckFor": "learningMining",
"label": "Add Learning Mining",
"relatedUiObject": "Learning Mining",
"propertyToCheckFor": "learningTasks",
"label": "Add Learning Tasks",
"relatedUiObject": "Learning Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
"propertyToCheckFor": "testingEnvironment",
"label": "Add Testing Environment",
"relatedUiObject": "Testing Environment",
"propertyToCheckFor": "testingTradingTasks",
"label": "Add Testing Trading Tasks",
"relatedUiObject": "Testing Trading Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
"propertyToCheckFor": "productionEnvironment",
"label": "Add Production Environment",
"relatedUiObject": "Production Environment",
"propertyToCheckFor": "productionTradingTasks",
"label": "Add Production Trading Tasks",
"relatedUiObject": "Production Trading Tasks",
"actionFunction": "payload.executeAction"
},
{
@@ -2089,27 +2135,27 @@
},
"properties": [
{
"name": "dataMining",
"name": "dataTasks",
"type": "node",
"childType": "Data Mining",
"childType": "Data Tasks",
"autoAdd": true
},
{
"name": "learningMining",
"name": "learningTasks",
"type": "node",
"childType": "Learning Mining",
"childType": "Learning Tasks",
"autoAdd": true
},
{
"name": "testingEnvironment",
"name": "testingTradingTasks",
"type": "node",
"childType": "Testing Environment",
"childType": "Testing Trading Tasks",
"autoAdd": true
},
{
"name": "productionEnvironment",
"name": "productionTradingTasks",
"type": "node",
"childType": "Production Environment",
"childType": "Production Trading Tasks",
"autoAdd": true
},
{
@@ -11838,8 +11884,7 @@
"childType": "Dynamic Indicators",
"autoAdd": true
}
],
"propertyNameAtParent": "tradingSystem"
]
},
{
"type": "Trading Strategy",
@@ -14551,17 +14596,17 @@
},
{
"action": "Add UI Object",
"label": "Add Dashboard",
"relatedUiObject": "Dashboard",
"label": "Add Project Dashboards",
"relatedUiObject": "Project Dashboards",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Dashboards",
"label": "Add Missing Dashboards",
"action": "Add Missing Project Dashboards",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Dashboard",
"relatedUiObject": "Project Dashboards",
"actionFunction": "payload.executeAction"
},
{
@@ -14596,12 +14641,59 @@
"childType": "Space Settings",
"autoAdd": true
},
{
"name": "projectDashboards",
"type": "array",
"childType": "Project Dashboards"
}
]
},
{
"type": "Project Dashboards",
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Dashboard",
"relatedUiObject": "Dashboard",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Dashboards",
"label": "Add Missing Dashboards",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Dashboard",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"propertyNameAtParent": "projectDashboards",
"properties": [
{
"name": "dashboards",
"type": "array",
"childType": "Dashboard"
}
]
],
"icon": "bitcoin"
},
{
"type": "Dashboard",
@@ -14634,10 +14726,10 @@
"addLeftIcons": true,
"level": 1,
"referenceAttachesTo": {
"compatibleTypes": "->Testing Environment->Production Environment->"
"compatibleTypes": "->Testing Trading Tasks->Production Trading Tasks->"
},
"chainAttachesTo": {
"compatibleTypes": "->Charting Space->"
"compatibleTypes": "->Project Dashboards->"
},
"propertyNameAtParent": "dashboards",
"properties": [
@@ -15872,7 +15964,7 @@
"compatibleTypes": "->Crypto Exchange->"
},
"chainAttachesTo": {
"compatibleTypes": "->Data Mining->"
"compatibleTypes": "->Project Data Tasks->"
},
"alternativeIcons": "Use Reference Parent",
"title": [
@@ -15943,7 +16035,7 @@
"compatibleTypes": "->Crypto Exchange->"
},
"chainAttachesTo": {
"compatibleTypes": "->Testing Environment->Production Environment->"
"compatibleTypes": "->Project Trading Tasks->"
},
"alternativeIcons": "Use Reference Parent",
"title": [
@@ -16014,7 +16106,7 @@
"compatibleTypes": "->Crypto Exchange->"
},
"chainAttachesTo": {
"compatibleTypes": "->Learning Mining->"
"compatibleTypes": "->Project Learning Tasks->"
},
"alternativeIcons": "Use Reference Parent",
"title": [
@@ -16032,7 +16124,7 @@
]
},
{
"type": "Data Mining",
"type": "Project Data Tasks",
"menuItems": [
{
"action": "Run All Exchange Data Tasks",
@@ -16079,12 +16171,19 @@
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"chainAttachesTo": {
"compatibleTypes": "->Network Node->"
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"propertyNameAtParent": "dataMining",
"chainAttachesTo": {
"compatibleTypes": "->Data Tasks->"
},
"propertyNameAtParent": "projectDataTasks",
"properties": [
{
"name": "exchangeDataTasks",
@@ -16092,10 +16191,74 @@
"childType": "Exchange Data Tasks",
"monitorChildrenRunning": true
}
],
"icon": "bitcoin"
},
{
"type": "Data Tasks",
"menuItems": [
{
"action": "Run All Project Data Tasks",
"label": "Run All Project Data Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Run",
"workDoneLabel": "Run Request Sent",
"iconPathOn": "run",
"iconPathOff": "run",
"actionFunction": "payload.executeAction"
},
{
"action": "Stop All Project Data Tasks",
"label": "Stop All Project Data Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Stop",
"workDoneLabel": "Stop Request Sent",
"iconPathOn": "stop",
"iconPathOff": "stop",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"label": "Add Project Data Tasks",
"relatedUiObject": "Project Data Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Project Data Tasks",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Project Data Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"addLeftIcons": true,
"level": 2,
"chainAttachesTo": {
"compatibleTypes": "->Network Node->"
},
"propertyNameAtParent": "dataTasks",
"properties": [
{
"name": "projectDataTasks",
"type": "array",
"childType": "Project Data Tasks",
"monitorChildrenRunning": true
}
]
},
{
"type": "Learning Mining",
"type": "Project Learning Tasks",
"menuItems": [
{
"action": "Run All Exchange Learning Tasks",
@@ -16142,12 +16305,19 @@
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"chainAttachesTo": {
"compatibleTypes": "->Network Node->"
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"propertyNameAtParent": "learningMining",
"chainAttachesTo": {
"compatibleTypes": "->Learning Tasks->"
},
"propertyNameAtParent": "projectLearningTasks",
"properties": [
{
"name": "exchangeLearningTasks",
@@ -16155,10 +16325,74 @@
"childType": "Exchange Learning Tasks",
"monitorChildrenRunning": true
}
],
"icon": "bitcoin"
},
{
"type": "Learning Tasks",
"menuItems": [
{
"action": "Run All Project Learning Tasks",
"label": "Run All Project Learning Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Run",
"workDoneLabel": "Run Request Sent",
"iconPathOn": "run",
"iconPathOff": "run",
"actionFunction": "payload.executeAction"
},
{
"action": "Stop All Project Learning Tasks",
"label": "Stop All Project Learning Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Stop",
"workDoneLabel": "Stop Request Sent",
"iconPathOn": "stop",
"iconPathOff": "stop",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"label": "Add Project Learning Tasks",
"relatedUiObject": "Project Learning Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Project Learning Tasks",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Project Learning Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"addLeftIcons": true,
"level": 2,
"chainAttachesTo": {
"compatibleTypes": "->Network Node->"
},
"propertyNameAtParent": "learningTasks",
"properties": [
{
"name": "projectLearningTasks",
"type": "array",
"childType": "Project Learning Tasks",
"monitorChildrenRunning": true
}
]
},
{
"type": "Testing Environment",
"type": "Project Trading Tasks",
"menuItems": [
{
"action": "Run All Exchange Trading Tasks",
@@ -16205,12 +16439,19 @@
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 0,
"chainAttachesTo": {
"compatibleTypes": "->Network Node->"
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"propertyNameAtParent": "testingEnvironment",
"chainAttachesTo": {
"compatibleTypes": "->Testing Trading Tasks->Production Trading Tasks->"
},
"propertyNameAtParent": "projectTradingTasks",
"properties": [
{
"name": "exchangeTradingTasks",
@@ -16218,14 +16459,15 @@
"childType": "Exchange Trading Tasks",
"monitorChildrenRunning": true
}
]
],
"icon": "bitcoin"
},
{
"type": "Production Environment",
"type": "Testing Trading Tasks",
"menuItems": [
{
"action": "Run All Exchange Trading Tasks",
"label": "Run All Exchange Trading Tasks",
"action": "Run All Project Trading Tasks",
"label": "Run All Project Trading Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Run",
"workDoneLabel": "Run Request Sent",
@@ -16234,8 +16476,8 @@
"actionFunction": "payload.executeAction"
},
{
"action": "Stop All Exchange Trading Tasks",
"label": "Stop All Exchange Trading Tasks",
"action": "Stop All Project Trading Tasks",
"label": "Stop All Project Trading Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Stop",
"workDoneLabel": "Stop Request Sent",
@@ -16245,17 +16487,17 @@
},
{
"action": "Add UI Object",
"label": "Add Exchange Trading Tasks",
"relatedUiObject": "Exchange Trading Tasks",
"label": "Add Project Trading Tasks",
"relatedUiObject": "Project Trading Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Exchange Trading Tasks",
"label": "Add Missing Exchanges",
"action": "Add Missing Project Trading Tasks",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Exchange Trading Tasks",
"relatedUiObject": "Project Trading Tasks",
"actionFunction": "payload.executeAction"
},
{
@@ -16273,12 +16515,75 @@
"chainAttachesTo": {
"compatibleTypes": "->Network Node->"
},
"propertyNameAtParent": "productionEnvironment",
"propertyNameAtParent": "testingTradingTasks",
"properties": [
{
"name": "exchangeTradingTasks",
"name": "projectTradingTasks",
"type": "array",
"childType": "Exchange Trading Tasks",
"childType": "Project Trading Tasks",
"monitorChildrenRunning": true
}
]
},
{
"type": "Production Trading Tasks",
"menuItems": [
{
"action": "Run All Project Trading Tasks",
"label": "Run All Project Trading Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Run",
"workDoneLabel": "Run Request Sent",
"iconPathOn": "run",
"iconPathOff": "run",
"actionFunction": "payload.executeAction"
},
{
"action": "Stop All Project Trading Tasks",
"label": "Stop All Project Trading Tasks",
"askConfirmation": true,
"confirmationLabel": "Confirm to Stop",
"workDoneLabel": "Stop Request Sent",
"iconPathOn": "stop",
"iconPathOff": "stop",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"label": "Add Project Trading Tasks",
"relatedUiObject": "Project Trading Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Project Trading Tasks",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Project Trading Tasks",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"addLeftIcons": true,
"level": 0,
"chainAttachesTo": {
"compatibleTypes": "->Network Node->"
},
"propertyNameAtParent": "productionTradingTasks",
"properties": [
{
"name": "projectTradingTasks",
"type": "array",
"childType": "Project Trading Tasks",
"monitorChildrenRunning": true
}
]
@@ -16322,7 +16627,7 @@
"compatibleTypes": "->Crypto Exchange->"
},
"chainAttachesTo": {
"compatibleTypes": "->Data Mines Data->"
"compatibleTypes": "->Project Data Products->"
},
"propertyNameAtParent": "exchangeDataProducts",
"properties": [
@@ -16335,6 +16640,48 @@
},
{
"type": "Data Mines Data",
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Project Data Products",
"relatedUiObject": "Project Data Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Project Data Products",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Project Data Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Data Storage->"
},
"propertyNameAtParent": "dataMinesData",
"properties": [
{
"name": "projectDataProducts",
"type": "array",
"childType": "Project Data Products"
}
]
},
{
"type": "Project Data Products",
"menuItems": [
{
"action": "Add UI Object",
@@ -16361,19 +16708,127 @@
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 3,
"chainAttachesTo": {
"compatibleTypes": "->Data Storage->"
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"propertyNameAtParent": "dataMinesData",
"chainAttachesTo": {
"compatibleTypes": "->Data Mines Data->"
},
"propertyNameAtParent": "projectDataProducts",
"properties": [
{
"name": "exchangeDataProducts",
"type": "array",
"childType": "Exchange Data Products"
}
]
],
"icon":"bitcoin"
},
{
"type": "Project Trading Products",
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Exchange Trading Products",
"relatedUiObject": "Exchange Trading Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Exchange Trading Products",
"label": "Add Missing Exchanges",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Exchange Trading Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Trading Mines Data->"
},
"propertyNameAtParent": "projectTradingProducts",
"properties": [
{
"name": "exchangeTradingProducts",
"type": "array",
"childType": "Exchange Trading Products"
}
],
"icon":"bitcoin"
},
{
"type": "Project Learning Products",
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Exchange Learning Products",
"relatedUiObject": "Exchange Learning Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Exchange Learning Products",
"label": "Add Missing Exchanges",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Exchange Learning Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete",
"actionFunction": "payload.executeAction"
}
],
"title": [
"Use Reference Parent"
],
"alternativeIcons": "Use Reference Parent Parent",
"addLeftIcons": true,
"level": 2,
"referenceAttachesTo": {
"compatibleTypes": "->Project Definition->"
},
"chainAttachesTo": {
"compatibleTypes": "->Learning Mines Data->"
},
"propertyNameAtParent": "projectLearningProducts",
"properties": [
{
"name": "exchangeLearningProducts",
"type": "array",
"childType": "Exchange Learning Products"
}
],
"icon":"bitcoin"
},
{
"type": "Exchange Trading Products",
@@ -16414,7 +16869,7 @@
"compatibleTypes": "->Crypto Exchange->"
},
"chainAttachesTo": {
"compatibleTypes": "->Trading Mines Data->"
"compatibleTypes": "->Project Trading Products->"
},
"propertyNameAtParent": "exchangeTradingProducts",
"properties": [
@@ -16465,7 +16920,7 @@
"compatibleTypes": "->Crypto Exchange->"
},
"chainAttachesTo": {
"compatibleTypes": "->Learning Mines Data->"
"compatibleTypes": "->Project Learning Products->"
},
"propertyNameAtParent": "exchangeLearningProducts",
"properties": [
@@ -16482,17 +16937,17 @@
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Exchange Trading Products",
"relatedUiObject": "Exchange Trading Products",
"label": "Add Project Trading Products",
"relatedUiObject": "Project Trading Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Exchange Trading Products",
"label": "Add Missing Exchanges",
"action": "Add Missing Project Trading Products",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Exchange Trading Products",
"relatedUiObject": "Project Trading Products",
"actionFunction": "payload.executeAction"
},
{
@@ -16513,9 +16968,9 @@
"propertyNameAtParent": "tradingMinesData",
"properties": [
{
"name": "exchangeTradingProducts",
"name": "projectTradingProducts",
"type": "array",
"childType": "Exchange Trading Products"
"childType": "Project Trading Products"
}
]
},
@@ -16524,17 +16979,17 @@
"menuItems": [
{
"action": "Add UI Object",
"label": "Add Exchange Learning Products",
"relatedUiObject": "Exchange Learning Products",
"label": "Add Project Learning Products",
"relatedUiObject": "Project Learning Products",
"actionFunction": "payload.executeAction"
},
{
"action": "Add Missing Exchange Learning Products",
"label": "Add Missing Exchanges",
"action": "Add Missing Project Learning Products",
"label": "Add Missing Projects",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
"relatedUiObject": "Exchange Learning Products",
"relatedUiObject": "Project Learning Products",
"actionFunction": "payload.executeAction"
},
{
@@ -16555,9 +17010,9 @@
"propertyNameAtParent": "learningMinesData",
"properties": [
{
"name": "exchangeLearningProducts",
"name": "projectLearningProducts",
"type": "array",
"childType": "Exchange Learning Products"
"childType": "Project Learning Products"
}
]
},
@@ -17135,6 +17590,15 @@
"workDoneLabel": "Done",
"relatedUiObject": "Plugin Project",
"actionFunction": "payload.executeAction"
},
{
"action": "Delete UI Object",
"askConfirmation": true,
"confirmationLabel": "Confirm to Delete",
"actionFunction": "payload.executeAction",
"label": "Delete",
"iconPathOn": "delete",
"iconPathOff": "delete"
}
],
"isHierarchyHead": true,
+1 -1
View File
@@ -1 +1 @@
[{"type":"Plotter Module","definition":"A plotter module is a device within a plotter that contains the definitions to create the visualization for a product definition.","content":["\r","\r","A single plotter may have several modules. Different modules may be used to plot each product of the bot, or as best suits the expected use-pattern.\r","\r","[![Plotter-Module-01](https://user-images.githubusercontent.com/13994516/71016077-eab41180-20f4-11ea-87e7-7262ff10a1ab.gif)](https://user-images.githubusercontent.com/13994516/71016077-eab41180-20f4-11ea-87e7-7262ff10a1ab.gif)\r","\r","{% include important.html content=\"For a bot to use a plotter, the corresponding product definition must establish a reference with the plotter module built to interpret the dataset.\" %}\r","\r","[![Plotter-Module-02-Product-Link](https://user-images.githubusercontent.com/13994516/71016079-eab41180-20f4-11ea-8afb-96321f9c68d8.gif)](https://user-images.githubusercontent.com/13994516/71016079-eab41180-20f4-11ea-8afb-96321f9c68d8.gif)\r","\r"],"folderName":"data_mine","adding":["\r","\r","To add a plotter module, select *Add Plotter Module* on the plotter node menu.\r","\r"],"configuring":["\r","\r","Select *Configure Plotter Module* on the menu to access the configuration.\r","\r","```json\r","{\r"," \"codeName\": \"Module Name\",\r"," \"isLegacy\": true,\r"," \"icon\": \"histogram\"\r","}\r","```\r","\r","* ```codeName``` is the name used in the code to refer to the module.\r","\r","* ```isLegacy``` is ```true``` for plotters modules developed entirely in JavaScript, and is ```false``` or doesn't show in the configuration for plotters built with the tools featured in the data mine hierarchy.\r","\r","* ```icon``` is the name of the image that illustrates the corresponding layer in the Layers Panel. In case the banner is not specified, the *default banner* is used. Current possible values are:\r","\r","\r","| Value | Image|\r","| :---: | :---: |\r","| ```default``` | ![Default-Banner](https://user-images.githubusercontent.com/13994516/70896073-5bbdd100-1ff0-11ea-84e3-5bd90475976f.png) |\r","| ```histogram``` | ![Histogram-Banner](https://user-images.githubusercontent.com/13994516/70896074-5bbdd100-1ff0-11ea-8dad-ccc6cfe64beb.png) |\r","| ```band``` | ![Bands-Banner](https://user-images.githubusercontent.com/13994516/70896072-5bbdd100-1ff0-11ea-8626-61c36bd22a3c.png) |\r","| ```moving-average``` | ![Moving-Average-Banner](https://user-images.githubusercontent.com/13994516/70896075-5bbdd100-1ff0-11ea-9909-dae33e57f65f.png) |\r","| ```oscillator``` | ![Oscillator-Banner](https://user-images.githubusercontent.com/13994516/70896076-5c566780-1ff0-11ea-8a5b-20077257c560.png) |\r","| ```trend``` | ![trends-banner](https://user-images.githubusercontent.com/13994516/70989177-744aeb80-20c3-11ea-88f5-d10473fa79b9.png) |\r","\r","Custom images may be added too. To do that:\r","\r","1. name the image in the following format: ```image-name.png```;\r","\r","1. place images in the ```WebServer\\Images\\Icons``` folder;\r","\r","1. in the configuration of the module, use the name as is (e.g.: ```\"icon\": \"image-name\"```)\r","\r"],"starting":["\r","\r","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r","\r"]},{"type":"Nodes Values"},{"type":"Nodes Highlights"},{"type":"Nodes Errors"},{"type":"Nodes Warnings"},{"type":"Nodes Infos"},{"type":"Nodes Status"},{"type":"Nodes Progress"},{"type":"Nodes Running"},{"type":"Nodes Announcements"},{"type":"Record Values"},{"type":"Shapes","definition":"The shapes node contains definitions of polygons and styles to be rendered on a per-period basis over the charts.","content":["\r","\r","A plotter recursively renders graphics for each period or candle. This means that a definition of what shapes need to be drawn for a single period is required. The plotter does the same thing for every period or candle that is visible on the screen at any point in time, for any time frame, as long as the corresponding data is available.\r","\r","[![Shapes-01-show-off](https://user-images.githubusercontent.com/13994516/71016080-eab41180-20f4-11ea-84c6-a514b87d568f.gif)](https://user-images.githubusercontent.com/13994516/71016080-eab41180-20f4-11ea-84c6-a514b87d568f.gif)\r","\r","In general terms, the process of defining what is going to be rendered on the screen consist of the following four steps:\r","\r"," 1. Identifying points defined by ```[x,y]``` coordinates\r"," 2. Defining polygons\r"," 3. Defining default styles\r"," 4. Defining conditional styles\r","\r"],"folderName":"data_mine","adding":["\r","\r","To add a shapes node, select *Add Shapes* on the plotter module menu.\r","\r"],"configuring":["\r","\r","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r","\r"],"starting":["\r","\r","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r","\r"]},{"type":"Plotter Panel","definition":"A plotter panel usually shows numerical representations of data over the charts on a small window or panel. Line truncated
[{"type":"Plotter Module","definition":"A plotter module is a device within a plotter that contains the definitions to create the visualization for a product definition.","content":["\r","\r","A single plotter may have several modules. Different modules may be used to plot each product of the bot, or as best suits the expected use-pattern.\r","\r","[![Plotter-Module-01](https://user-images.githubusercontent.com/13994516/71016077-eab41180-20f4-11ea-87e7-7262ff10a1ab.gif)](https://user-images.githubusercontent.com/13994516/71016077-eab41180-20f4-11ea-87e7-7262ff10a1ab.gif)\r","\r","{% include important.html content=\"For a bot to use a plotter, the corresponding product definition must establish a reference with the plotter module built to interpret the dataset.\" %}\r","\r","[![Plotter-Module-02-Product-Link](https://user-images.githubusercontent.com/13994516/71016079-eab41180-20f4-11ea-8afb-96321f9c68d8.gif)](https://user-images.githubusercontent.com/13994516/71016079-eab41180-20f4-11ea-8afb-96321f9c68d8.gif)\r","\r"],"folderName":"data_mine","adding":["\r","\r","To add a plotter module, select *Add Plotter Module* on the plotter node menu.\r","\r"],"configuring":["\r","\r","Select *Configure Plotter Module* on the menu to access the configuration.\r","\r","```json\r","{\r"," \"codeName\": \"Module Name\",\r"," \"isLegacy\": true,\r"," \"icon\": \"histogram\"\r","}\r","```\r","\r","* ```codeName``` is the name used in the code to refer to the module.\r","\r","* ```isLegacy``` is ```true``` for plotters modules developed entirely in JavaScript, and is ```false``` or doesn't show in the configuration for plotters built with the tools featured in the data mine hierarchy.\r","\r","* ```icon``` is the name of the image that illustrates the corresponding layer in the Layers Panel. In case the banner is not specified, the *default banner* is used. Current possible values are:\r","\r","\r","| Value | Image|\r","| :---: | :---: |\r","| ```default``` | ![Default-Banner](https://user-images.githubusercontent.com/13994516/70896073-5bbdd100-1ff0-11ea-84e3-5bd90475976f.png) |\r","| ```histogram``` | ![Histogram-Banner](https://user-images.githubusercontent.com/13994516/70896074-5bbdd100-1ff0-11ea-8dad-ccc6cfe64beb.png) |\r","| ```band``` | ![Bands-Banner](https://user-images.githubusercontent.com/13994516/70896072-5bbdd100-1ff0-11ea-8626-61c36bd22a3c.png) |\r","| ```moving-average``` | ![Moving-Average-Banner](https://user-images.githubusercontent.com/13994516/70896075-5bbdd100-1ff0-11ea-9909-dae33e57f65f.png) |\r","| ```oscillator``` | ![Oscillator-Banner](https://user-images.githubusercontent.com/13994516/70896076-5c566780-1ff0-11ea-8a5b-20077257c560.png) |\r","| ```trend``` | ![trends-banner](https://user-images.githubusercontent.com/13994516/70989177-744aeb80-20c3-11ea-88f5-d10473fa79b9.png) |\r","\r","Custom images may be added too. To do that:\r","\r","1. name the image in the following format: ```image-name.png```;\r","\r","1. place images in the ```WebServer\\Images\\Icons``` folder;\r","\r","1. in the configuration of the module, use the name as is (e.g.: ```\"icon\": \"image-name\"```)\r","\r"],"starting":["\r","\r","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r","\r"]},{"type":"Nodes Values"},{"type":"Nodes Highlights"},{"type":"Nodes Errors"},{"type":"Nodes Warnings"},{"type":"Nodes Infos"},{"type":"Nodes Status"},{"type":"Nodes Progress"},{"type":"Nodes Running"},{"type":"Nodes Announcements"},{"type":"Record Values"},{"type":"Shapes","definition":"The shapes node contains definitions of polygons and styles to be rendered on a per-period basis over the charts.","content":["\r","\r","A plotter recursively renders graphics for each period or candle. This means that a definition of what shapes need to be drawn for a single period is required. The plotter does the same thing for every period or candle that is visible on the screen at any point in time, for any time frame, as long as the corresponding data is available.\r","\r","[![Shapes-01-show-off](https://user-images.githubusercontent.com/13994516/71016080-eab41180-20f4-11ea-84c6-a514b87d568f.gif)](https://user-images.githubusercontent.com/13994516/71016080-eab41180-20f4-11ea-84c6-a514b87d568f.gif)\r","\r","In general terms, the process of defining what is going to be rendered on the screen consist of the following four steps:\r","\r"," 1. Identifying points defined by ```[x,y]``` coordinates\r"," 2. Defining polygons\r"," 3. Defining default styles\r"," 4. Defining conditional styles\r","\r"],"folderName":"data_mine","adding":["\r","\r","To add a shapes node, select *Add Shapes* on the plotter module menu.\r","\r"],"configuring":["\r","\r","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r","\r"],"starting":["\r","\r","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r","\r"]},{"type":"Plotter Panel","definition":"A plotter panel usually shows numerical representations of data over the charts on a small window or panel. Line truncated
@@ -0,0 +1,848 @@
exports.newSuperalgosBotModulesBollingerBandsMultiPeriodDaily = function (processIndex) {
const MODULE_NAME = "Bollinger Bands Multi Period Daily"
const CANDLES_FOLDER_NAME = "Candles"
const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands"
const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth"
thisObject = {
initialize: initialize,
start: start
};
let fileStorage = TS.projects.superalgos.taskModules.fileStorage.newFileStorage(processIndex);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
statusDependencies = pStatusDependencies;
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] initialize -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles from Candles Volumes and produce daily files with bollinger bands.
*/
function start(callBackFunction) {
try {
/* Context Variables */
let contextVariables = {
lastBandFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxBandFile: undefined // Datetime of the last file available to be used as an input of this process.
};
let previousDay; // Holds the date of the previous day relative to the processing date.
let processDate; // Holds the processing date.
getContextVariables();
function getContextVariables() {
try {
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.firstTradeFile = new Date(
thisReport.beginingOfMarket.year + "-" +
thisReport.beginingOfMarket.month + "-" +
thisReport.beginingOfMarket.days + " " +
thisReport.beginingOfMarket.hours + ":" +
thisReport.beginingOfMarket.minutes +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
/* Second, we get the report from Candles Volumes, to know when the marted ends. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxBandFile = new Date(thisReport.lastFile.valueOf());
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Daily"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastBandFile = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastBandFile = new Date(
contextVariables.lastBandFile.valueOf() +
TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS);
buildBands();
return;
}
contextVariables.lastBandFile = new Date(thisReport.lastFile);
/*
The bands objects needs to be calculated over more than one day in some situations. In order to simplify the calculations we will
always load the last 2 days of candles, and run the calculations on that array.
1. So first we will load the candles file of processDay -1.
2. Secondly we will load the candles file of processDay.
After reading these two files we will add all candles to a unique array.
*/
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> thisReport.lastFile !== undefined")
buildBands();
return;
} else {
/*
In the case when there is no status report, we take the date of the file with the first trades as the begining of the market. Then we will
go one day further in time, so that the previous day does fine a file at the begining of the market.
*/
contextVariables.lastBandFile = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastBandFile = new Date(
contextVariables.lastBandFile.valueOf() +
TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS);
beginingOfMarket = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> thisReport.lastFile === undefined")
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> contextVariables.lastBandFile = " + contextVariables.lastBandFile)
buildBands();
return;
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
let n;
processDate = new Date(contextVariables.lastBandFile.valueOf() - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS); // Go back one day to start well when we advance time at the begining of the loop.
let fromDate = new Date(processDate.valueOf())
let lastDate = new Date()
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildBands -> processDate = " + processDate)
advanceTime();
function advanceTime() {
try {
processDate = new Date(processDate.valueOf() + TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS);
previousDay = new Date(processDate.valueOf() - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildBands -> advanceTime -> processDate = " + processDate)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildBands -> advanceTime -> previousDay = " + previousDay)
/* Validation that we are not going past the head of the market. */
if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) {
const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + ".";
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildBands -> advanceTime -> " + logText)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
return
}
/* Telling the world we are alive and doing well */
let currentDateString =
processDate.getUTCFullYear() + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCMonth() + 1, 2) + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCDate(), 2);
let currentDate = new Date(processDate)
let percentage = TS.projects.superalgos.utilities.dateTimeFunctions.getPercentage(fromDate, currentDate, lastDate)
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, currentDateString, percentage)
if (TS.projects.superalgos.utilities.dateTimeFunctions.areTheseDatesEqual(currentDate, new Date()) === false) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newInternalLoop(currentDate, percentage);
}
periodsLoop();
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> advanceTime -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function periodsLoop() {
try {
/*
We will iterate through all posible timeFrames
*/
n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function loopBody() {
try {
const timeFrame = TS.projects.superalgos.globals.timeFrames.dailyFilePeriods()[n][1];
let candles = []; // Here we will put all the candles of the 2 files read.
let previousDayFile;
let processDayFile;
getPreviousDayFile();
function getPreviousDayFile() {
try {
let dateForPath =
previousDay.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(previousDay.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(previousDay.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot =
'Project/' +
TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.type.replace(' ', '-') + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "/" +
"Candles-Volumes" + '/' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onCurrentDayFileReceived);
function onCurrentDayFileReceived(err, text) {
try {
previousDayFile = JSON.parse(text);
getProcessDayFile()
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> filePath = " + filePath);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> onCurrentDayFileReceived -> market = " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + '_' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getPreviousDayFile -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function getProcessDayFile() {
try {
let dateForPath =
processDate.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot =
'Project/' +
TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.type.replace(' ', '-') + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "/" +
"Candles-Volumes" + '/' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + "Multi-Period-Daily" + "/" + timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onCurrentDayFileReceived);
function onCurrentDayFileReceived(err, text) {
try {
processDayFile = JSON.parse(text);
buildBands();
} catch (err) {
if (processDate.valueOf() > contextVariables.maxBandFile.valueOf()) {
processDayFile = []; // we are past the head of the market, then no worries if this file is non existent.
buildBands();
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> filePath = " + filePath);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> onCurrentDayFileReceived -> market = " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + '_' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> getProcessDayFile -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
addCandlesToSingleArray(previousDayFile);
addCandlesToSingleArray(processDayFile);
calculateBands();
function addCandlesToSingleArray(candlesFile) {
try {
for (let i = 0; i < candlesFile.length; i++) {
let candle = {
open: undefined,
close: undefined,
min: 10000000000000,
max: 0,
begin: undefined,
end: undefined,
direction: undefined
}
candle.min = candlesFile[i][0];
candle.max = candlesFile[i][1];
candle.open = candlesFile[i][2];
candle.close = candlesFile[i][3];
candle.begin = candlesFile[i][4];
candle.end = candlesFile[i][5];
if (candle.open > candle.close) { candle.direction = 'down'; }
if (candle.open < candle.close) { candle.direction = 'up'; }
if (candle.open === candle.close) { candle.direction = 'side'; }
candles.push(candle);
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> addCandlesToSingleArray -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
return;
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> buildBands -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
return;
}
}
function calculateBands() {
try {
let bandsArray = [];
let pBArray = [];
let numberOfPeriodsBB = 20;
let numberOfStandardDeviations = 2;
let numberOfPeriodsPB = 10;
/* Building bands */
let band
for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them.
/* Calculating the bollinger bands. */
let movingAverage = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the moving average.
movingAverage = movingAverage + candles[j].close;
}
movingAverage = movingAverage / numberOfPeriodsBB;
let standardDeviation = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last n candles to calculate the standard deviation.
standardDeviation = standardDeviation + Math.pow(candles[j].close - movingAverage, 2);
}
standardDeviation = standardDeviation / numberOfPeriodsBB;
standardDeviation = Math.sqrt(standardDeviation);
if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later.
band = {
begin: candles[i].begin,
end: candles[i].end,
movingAverage: movingAverage,
standardDeviation: standardDeviation,
deviation: standardDeviation * numberOfStandardDeviations
}
/* Will only add to the array the bands of the current day */
if (band.begin >= processDate.valueOf()) { bandsArray.push(band); }
/* Calculating %B */
let lowerBB
let upperBB
lowerBB = band.movingAverage - band.deviation;
upperBB = band.movingAverage + band.deviation;
let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100
/* Moving Average Calculation */
let numberOfPreviousPeriods;
let currentPosition = pBArray.length;
if (currentPosition < numberOfPeriodsPB) { // Avoinding to get into negative array indexes
numberOfPreviousPeriods = currentPosition;
} else {
numberOfPreviousPeriods = numberOfPeriodsPB;
}
movingAverage = 0
for (let j = currentPosition - numberOfPreviousPeriods; j < currentPosition; j++) { // go through the last numberOfPeriodsPBs to calculate the moving average.
movingAverage = movingAverage + pBArray[j].value;
}
movingAverage = movingAverage + value;
movingAverage = movingAverage / (numberOfPreviousPeriods + 1);
let bandwidth = (upperBB - lowerBB) / band.movingAverage;
let percentageBandwidth = {
begin: candles[i].begin,
end: candles[i].end,
value: value,
movingAverage: movingAverage,
bandwidth: bandwidth
};
/* Will only add to the array the pBs of the current day */
if (percentageBandwidth.begin >= processDate.valueOf()) { pBArray.push(percentageBandwidth); }
}
writeBandsFile(bandsArray, pBArray);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> calculateBands -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
return
}
}
function writeBandsFile(pBands, pPB) {
try {
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < pBands.length; i++) {
let band = pBands[i];
fileContent = fileContent + separator + '[' +
band.begin + "," +
band.end + "," +
band.movingAverage + "," +
band.standardDeviation + "," +
band.deviation + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++
}
fileContent = "[" + fileContent + "]";
let dateForPath =
processDate.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCDate(), 2);
let fileName = 'Data.json';
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
BOLLINGER_BANDS_FOLDER_NAME + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" +
timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack)
callBackFunction(err)
return
}
const logText = "[WARN] Finished with File @ " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + "";
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> " + logText)
writePBFile(pPB);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writeBandsFile -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writePBFile(pPercentageBandwidths) {
try {
let separator = ""
let fileRecordCounter = 0
let fileContent = ""
for (let i = 0; i < pPercentageBandwidths.length; i++) {
let pB = pPercentageBandwidths[i];
fileContent = fileContent + separator + '[' +
pB.begin + "," +
pB.end + "," +
pB.value + "," +
pB.movingAverage + "," +
pB.bandwidth + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++
}
fileContent = "[" + fileContent + "]";
let dateForPath =
processDate.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processDate.getUTCDate(), 2);
let fileName = 'Data.json';
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" +
timeFrame + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack)
callBackFunction(err)
return
}
const logText = "[WARN] Finished with File @ " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName + "";
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildBands -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> " + logText)
controlLoop()
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> writeBandBandsFile -> writePBFile -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> loopBody -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function controlLoop() {
try {
n++
if (n < TS.projects.superalgos.globals.timeFrames.dailyFilePeriods().length) {
loopBody()
} else {
n = 0
writeDataRanges(onWritten);
function onWritten(err) {
try {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> controlLoop -> onWritten -> err = " + err.stack)
callBackFunction(err)
return
}
writeStatusReport(processDate, advanceTime);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> onWritten -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> controlLoop -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> err.message = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRanges(callBack) {
try {
writeDataRange(contextVariables.firstTradeFile, processDate, BOLLINGER_BANDS_FOLDER_NAME, onBandsBandsDataRangeWritten);
function onBandsBandsDataRangeWritten(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> err = " + err.stack)
callBack(err)
return
}
writeDataRange(contextVariables.firstTradeFile, processDate, PERCENTAGE_BANDWIDTH_FOLDER_NAME, onPercentageBandwidthDataRangeWritten);
function onPercentageBandwidthDataRangeWritten(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] writeDataRanges -> writeDataRanges -> onBandsBandsDataRangeWritten -> onPercentageBandwidthDataRangeWritten -> err = " + err.stack)
callBack(err)
return
}
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeDataRanges -> err = " + err.stack)
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
try {
let dataRange = {
begin: pBegin.valueOf(),
end: pEnd.valueOf() + TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS
}
let fileContent = JSON.stringify(dataRange);
let fileName = 'Data.Range.json';
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
pProductFolder + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack)
callBack(err)
return
}
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeDataRange -> err = " + err.stack)
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
function writeStatusReport(lastFileDate, callBack) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate)
try {
let reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Daily"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> err.message = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
@@ -1,149 +1,96 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE) {
exports.newSuperalgosBotModulesBollingerBandsMultiPeriodMarket = function (processIndex) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const MODULE_NAME = "User Bot";
const TRADES_FOLDER_NAME = "Trades";
const CANDLES_FOLDER_NAME = "Candles";
const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands";
const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth";
const MODULE_NAME = "Bollinger Bands Multi Period Market"
const CANDLES_FOLDER_NAME = "Candles"
const BOLLINGER_BANDS_FOLDER_NAME = "Bollinger-Bands"
const PERCENTAGE_BANDWIDTH_FOLDER_NAME = "Percentage-Bandwidth"
thisObject = {
initialize: initialize,
start: start
};
let statusDependencies;
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
statusDependencies = pStatusDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
/*
let statusDependencies
let fileStorage = TS.projects.superalgos.taskModules.fileStorage.newFileStorage(processIndex)
This process is going to do the following:
Read the candles from Candles Volumes and produce the bollinger bands out of them.
*/
function start(callBackFunction) {
return thisObject
function initialize(pStatusDependencies, callBackFunction) {
try {
statusDependencies = pStatusDependencies;
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = bot.market;
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] initialize -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles from Candles Volumes and produce the bollinger bands out of them.
*/
function start(callBackFunction) {
try {
buildBands();
function buildBands() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> Entering function."); }
try {
let n;
periodsLoop();
let n
periodsLoop()
function periodsLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> periodsLoop -> Entering function."); }
/*
We will iterate through all posible periods.
*/
n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
loopBody()
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> periodsLoop -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function loopBody() {
try {
const outputPeriod = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][0];
const timeFrame = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][1];
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> Entering function."); }
const outputPeriod = global.marketFilesPeriods[n][0];
const timeFrame = global.marketFilesPeriods[n][1];
nextCandleFile();
nextCandleFile()
function nextCandleFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> Entering function."); }
let fileName = "Data.json";
let filePathRoot = 'Project/' + bot.project + "/" + bot.mineType + "/" + bot.dataMine + "/" + "Candles-Volumes" + '/' + bot.exchange + "/" + bot.market.baseAsset + "-" + bot.market.quotedAsset
let filePathRoot =
'Project/' + TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.type.replace(' ', '-') + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "/" +
"Candles-Volumes" + '/' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
function onFileReceived(err, text) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> text = " + text); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
return;
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack)
callBackFunction(err)
return
}
let marketFile = JSON.parse(text);
let candles = [];
buildCandles();
let marketFile = JSON.parse(text)
let candles = []
buildCandles()
function buildCandles() {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> Entering function."); }
try {
for (let i = 0; i < marketFile.length; i++) {
let candle = {
@@ -166,24 +113,19 @@
candle.end = marketFile[i][5];
candles.push(candle);
}
buildBands();
buildBands()
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildCandles -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function buildBands() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> Entering function."); }
let bandsArray = [];
let pBArray = [];
let numberOfPeriodsBB = 20;
@@ -192,22 +134,21 @@
/* Building bands */
let band;
let band
for (let i = numberOfPeriodsBB - 1; i < candles.length; i++) { // Go through all the candles to generate a band segment for each of them.
let movingAverage = 0;
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the moving average.
movingAverage = movingAverage + candles[j].close;
movingAverage = movingAverage + candles[j].close
}
movingAverage = movingAverage / numberOfPeriodsBB;
movingAverage = movingAverage / numberOfPeriodsBB
let standardDeviation = 0;
let standardDeviation = 0
for (let j = i - numberOfPeriodsBB + 1; j < i + 1; j++) { // go through the last numberOfPeriodsBB candles to calculate the standard deviation.
standardDeviation = standardDeviation + Math.pow (candles[j].close - movingAverage, 2);
standardDeviation = standardDeviation + Math.pow(candles[j].close - movingAverage, 2)
}
standardDeviation = standardDeviation / numberOfPeriodsBB;
standardDeviation = Math.sqrt(standardDeviation);
standardDeviation = standardDeviation / numberOfPeriodsBB
standardDeviation = Math.sqrt(standardDeviation)
if (standardDeviation === 0) { standardDeviation = 0.000000001; } // This is to prevent a division by zero later.
band = {
@@ -218,20 +159,18 @@
deviation: standardDeviation * numberOfStandardDeviations
};
bandsArray.push(band);
bandsArray.push(band)
/* Calculating %B */
let lowerBB;
let upperBB;
let lowerBB
let upperBB
lowerBB = band.movingAverage - band.deviation;
upperBB = band.movingAverage + band.deviation;
let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100;
let value = (candles[i].close - lowerBB) / (upperBB - lowerBB) * 100
/* Moving Average Calculation */
let numberOfPreviousPeriods;
let currentPosition = pBArray.length;
@@ -248,7 +187,7 @@
movingAverage = movingAverage + value;
movingAverage = movingAverage / (numberOfPreviousPeriods + 1);
let bandwidth = (upperBB- lowerBB) / band.movingAverage;
let bandwidth = (upperBB - lowerBB) / band.movingAverage;
let percentageBandwidth = {
begin: candles[i].begin,
@@ -256,29 +195,25 @@
value: value,
movingAverage: movingAverage,
bandwidth: bandwidth
};
}
pBArray.push(percentageBandwidth);
pBArray.push(percentageBandwidth)
}
writeBandsFile(bandsArray, pBArray);
writeBandsFile(bandsArray, pBArray)
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> buildBands -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeBandsFile(pBands, pPB) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
let separator = ""
let fileRecordCounter = 0
let fileContent = ""
for (let i = 0; i < pBands.length; i++) {
@@ -292,63 +227,56 @@
band.deviation + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
fileRecordCounter++
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + BOLLINGER_BANDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame;
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
BOLLINGER_BANDS_FOLDER_NAME + "/" +
"Multi-Period-Market" + "/" +
timeFrame
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset);
callBackFunction(err);
return;
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> filePath = " + filePath);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> market = " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName)
callBackFunction(err)
return
}
writePBFile(pPB);
writePBFile(pPB)
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writeBandsFile -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writePBFile(pPercentageBandwidths) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
let separator = ""
let fileRecordCounter = 0
let fileContent = ""
for (let i = 0; i < pPercentageBandwidths.length; i++) {
@@ -370,126 +298,118 @@
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = bot.filePathRoot + "/Output/" + PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + timeFrame;
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
PERCENTAGE_BANDWIDTH_FOLDER_NAME + "/" +
"Multi-Period-Market" + "/" +
timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> market = " + market.baseAsset + "_" + market.quotedAsset);
callBackFunction(err);
return;
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> filePath = " + filePath);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> market = " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName);
callBackFunction(err)
return
}
controlLoop();
controlLoop()
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> onFileCreated -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> writePBFile -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> nextCandleFile -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> loopBody -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> loopBody -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function controlLoop() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> buildBands -> controlLoop -> Entering function."); }
n++;
if (n < global.marketFilesPeriods.length) {
loopBody();
n++
if (n < TS.projects.superalgos.globals.timeFrames.marketFilesPeriods().length) {
loopBody()
} else {
writeStatusReport(callBackFunction);
writeStatusReport(callBackFunction)
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> controlLoop -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> controlLoop -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> buildBands -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildBands -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(callBack) {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStatusReport -> Entering function."); }
try {
let reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Market"
let reportKey = "Masters" + "-" + "Bollinger-Bands" + "-" + "Multi-Period-Market"
let thisReport = statusDependencies.statusReports.get(reportKey);
thisReport.file.lastExecution = bot.processDatetime;
thisReport.file.lastExecution = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME;
thisReport.save(callBack);
if (global.areEqualDates(bot.processDatetime, new Date()) === false) {
logger.newInternalLoop(bot.codeName, bot.process, bot.processDatetime);
if (TS.projects.superalgos.utilities.dateTimeFunctions.areTheseDatesEqual(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME, new Date()) === false) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newInternalLoop(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME);
}
/* Telling the world we are alive and doing well */
let currentDateString = bot.processDatetime.getUTCFullYear() + '-' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '-' + utilities.pad(bot.processDatetime.getUTCDate(), 2);
let currentDate = new Date(bot.processDatetime)
let currentDateString = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME.getUTCFullYear() + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME.getUTCMonth() + 1, 2) + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME.getUTCDate(), 2);
let currentDate = new Date(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME)
let lastDate = new Date()
bot.processHeartBeat(currentDateString, global.getPercentage(currentDate, currentDate, lastDate))
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, currentDateString, TS.projects.superalgos.utilities.dateTimeFunctions.getPercentage(currentDate, currentDate, lastDate))
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
};
}
@@ -0,0 +1,750 @@
exports.newSuperalgosBotModulesCandlesVolumesMultiPeriodDaily = function (processIndex) {
const MODULE_NAME = "Candles Volumes Multi Period Daily";
const CANDLES_FOLDER_NAME = "Candles";
const CANDLES_ONE_MIN = "One-Min";
const VOLUMES_FOLDER_NAME = "Volumes";
const VOLUMES_ONE_MIN = "One-Min";
thisObject = {
initialize: initialize,
start: start
}
let fileStorage = TS.projects.superalgos.taskModules.fileStorage.newFileStorage(processIndex)
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
statusDependencies = pStatusDependencies
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] initialize -> err = " + err.stack)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
/*
This process is going to do the following:
Read the candles and volumes from Exchange Raw Data and produce a file for each day and for each period with candles and volumes.
*/
function start(callBackFunction) {
try {
/* Context Variables */
let contextVariables = {
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
getContextVariables();
function getContextVariables() {
try {
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message)
callBackFunction(customOK)
return
}
contextVariables.firstTradeFile = new Date(
thisReport.beginingOfMarket.year + "-" +
thisReport.beginingOfMarket.month + "-" +
thisReport.beginingOfMarket.days + " " +
thisReport.beginingOfMarket.hours + ":" +
thisReport.beginingOfMarket.minutes +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS)
/* Second, we get the report from Exchange Raw Data, to know when the marted ends. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return
}
thisReport = statusDependencies.statusReports.get(reportKey).file
if (thisReport.lastFile === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message)
callBackFunction(customOK)
return
}
contextVariables.maxCandleFile = new Date(
thisReport.lastFile.year + "-" +
thisReport.lastFile.month + "-" +
thisReport.lastFile.days + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS)
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey)
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
return
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey)
beginingOfMarket = new Date(thisReport.beginingOfMarket)
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(contextVariables.firstTradeFile.getUTCFullYear() + "-" + (contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" + contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" + TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastCandleFile = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS)
contextVariables.lastCandleFile = new Date(
contextVariables.lastCandleFile.valueOf() -
TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS) // Go back one day to start well.
buildCandles()
return
}
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS)
buildCandles()
return
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS)
contextVariables.lastCandleFile = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS)
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles()
return
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> err = " + err.stack)
if (err.message === "Cannot read property 'file' of undefined") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys))
}
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
function buildCandles() {
try {
let fromDate = new Date(contextVariables.lastCandleFile.valueOf())
let lastDate = new Date()
let outputCandles
let outputVolumes
advanceTime()
function advanceTime() {
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + ".")
/* Validation that we are not going past the head of the market. */
if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + ".")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE) // Here is where we finish processing and wait for the platform to run this module again.
return
}
/* Telling the world we are alive and doing well */
let currentDateString =
contextVariables.lastCandleFile.getUTCFullYear() + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2)
let currentDate = new Date(contextVariables.lastCandleFile)
let percentage = TS.projects.superalgos.utilities.dateTimeFunctions.getPercentage(fromDate, currentDate, lastDate)
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, currentDateString, percentage)
if (TS.projects.superalgos.utilities.dateTimeFunctions.areTheseDatesEqual(currentDate, new Date()) === false) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newInternalLoop(currentDate, percentage);
}
/*
We prepere the arrays that will accumulate all the information for each output file.
*/
outputCandles = []
outputVolumes = []
for (let n = 0; n < TS.projects.superalgos.globals.timeFrames.dailyFilePeriods().length; n++) {
const emptyArray1 = []
const emptyArray2 = []
outputCandles.push(emptyArray1)
outputVolumes.push(emptyArray2)
}
periodsLoop();
}
function periodsLoop() {
/*
We will iterate through all posible periods.
*/
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody();
function loopBody() {
const outputPeriod = TS.projects.superalgos.globals.timeFrames.dailyFilePeriods()[n][0]
const timeFrame = TS.projects.superalgos.globals.timeFrames.dailyFilePeriods()[n][1]
nextCandleFile();
function nextCandleFile() {
let dateForPath =
contextVariables.lastCandleFile.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2)
let fileName = "Data.json"
let filePathRoot =
'Project/' +
TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.type.replace(' ', '-') + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "/" +
"Exchange-Raw-Data" + '/' +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> getting file at dateForPath = " + dateForPath)
function onFileReceived(err, text) {
try {
let candlesFile;
if (err.result === TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return;
}
} else {
if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + err.stack)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack)
callBackFunction(err);
return
}
}
const inputCandlesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputCandles = inputFilePeriod / outputPeriod;
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputCandles; i++) {
let outputCandle = {
open: 0,
close: 0,
min: 0,
max: 0,
begin: 0,
end: 0
}
let saveCandle = false;
outputCandle.begin = beginingOutputTime + i * outputPeriod;
outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < candlesFile.length; j++) {
let candle = {
open: candlesFile[j][2],
close: candlesFile[j][3],
min: candlesFile[j][0],
max: candlesFile[j][1],
begin: candlesFile[j][4],
end: candlesFile[j][5]
};
/* Here we discard all the candles out of range. */
if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) {
if (saveCandle === false) { // this will set the value only once.
outputCandle.open = candle.open;
outputCandle.min = candle.min
outputCandle.max = candle.max
}
saveCandle = true;
outputCandle.close = candle.close; // only the last one will be saved
if (candle.min < outputCandle.min) {
outputCandle.min = candle.min
}
if (candle.max > outputCandle.max) {
outputCandle.max = candle.max
}
}
}
if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range.
outputCandles[n].push(outputCandle)
}
}
nextVolumeFile();
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
function nextVolumeFile() {
try {
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot =
'Project/' +
TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.type.replace(' ', '-') + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "/" +
"Exchange-Raw-Data" + '/' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let filePath =
filePathRoot +
"/Output/" +
VOLUMES_FOLDER_NAME + '/' +
VOLUMES_ONE_MIN + '/' +
dateForPath
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
let volumesFile
if (err.result === TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack)
callBackFunction(err)
return
}
const inputVolumesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputVolumes; i++) {
let outputVolume = {
buy: 0,
sell: 0,
begin: 0,
end: 0
}
let saveVolume = false;
outputVolume.begin = beginingOutputTime + i * outputPeriod;
outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < volumesFile.length; j++) {
let volume = {
buy: volumesFile[j][0],
sell: volumesFile[j][1],
begin: volumesFile[j][2],
end: volumesFile[j][3]
}
/* Here we discard all the Volumes out of range. */
if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) {
saveVolume = true
outputVolume.buy = outputVolume.buy + volume.buy
outputVolume.sell = outputVolume.sell + volume.sell
}
}
if (saveVolume === true) {
outputVolumes[n].push(outputVolume);
}
}
writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop)
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
function controlLoop() {
n++;
if (n < TS.projects.superalgos.globals.timeFrames.dailyFilePeriods().length) {
loopBody()
} else {
writeDataRanges(onWritten);
function onWritten(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack)
callBack(err)
return
}
writeStatusReport(contextVariables.lastCandleFile, advanceTime);
}
}
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeFiles(candles, volumes, timeFrame, callBack) {
/*
Here we will write the contents of the Candles and Volumens files.
*/
try {
writeCandles()
function writeCandles() {
let separator = ""
let fileRecordCounter = 0
let fileContent = ""
for (let i = 0; i < candles.length; i++) {
let candle = candles[i]
fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let dateForPath =
contextVariables.lastCandleFile.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2)
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
CANDLES_FOLDER_NAME + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" +
timeFrame + "/" +
dateForPath
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
writeVolumes();
}
}
function writeVolumes() {
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < volumes.length; i++) {
let candle = volumes[i];
fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let dateForPath =
contextVariables.lastCandleFile.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2)
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
VOLUMES_FOLDER_NAME + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" +
timeFrame + "/" + dateForPath
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack)
callBackFunction(err)
return
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
callBack()
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeFiles -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRanges(callBack) {
try {
writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, CANDLES_FOLDER_NAME, onCandlesDataRangeWritten);
function onCandlesDataRangeWritten(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] writeDataRanges -> writeDataRanges -> onCandlesDataRangeWritten -> err = " + err.stack)
callBack(err)
return
}
writeDataRange(contextVariables.firstTradeFile, contextVariables.lastCandleFile, VOLUMES_FOLDER_NAME, onVolumeDataRangeWritten);
function onVolumeDataRangeWritten(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] writeDataRanges -> writeDataRanges -> onVolumeDataRangeWritten -> err = " + err.stack)
callBack(err)
return
}
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeDataRanges -> err = " + err.stack);
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeDataRange(pBegin, pEnd, pProductFolder, callBack) {
try {
let dataRange = {
begin: pBegin.valueOf(),
end: pEnd.valueOf() + TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS
}
let fileContent = JSON.stringify(dataRange);
let fileName = 'Data.Range.json';
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT + "/Output/" + pProductFolder + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack)
callBack(err)
return
}
let key =
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.config.codeName + "-" +
pProductFolder + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + '/' +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let event = {
dateRange: dataRange
}
TS.projects.superalgos.globals.taskConstants.EVENT_SERVER_CLIENT_MODULE_OBJECT.raiseEvent(key, 'Data Range Updated', event)
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeDataRange -> err = " + err.stack)
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
function writeStatusReport(lastFileDate, callBack) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate)
try {
let reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Daily"
let thisReport = statusDependencies.statusReports.get(reportKey)
thisReport.file.lastExecution = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME;
thisReport.file.lastFile = lastFileDate;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack);
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] 'Start' - ERROR : " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
}
@@ -0,0 +1,880 @@
exports.newSuperalgosBotModulesCandlesVolumesMultiPeriodMarket = function (processIndex) {
const MODULE_NAME = "Candles Volumes Multi Period Market"
const CANDLES_FOLDER_NAME = "Candles"
const CANDLES_ONE_MIN = "One-Min"
const VOLUMES_FOLDER_NAME = "Volumes"
const VOLUMES_ONE_MIN = "One-Min"
thisObject = {
initialize: initialize,
start: start
}
let fileStorage = TS.projects.superalgos.taskModules.fileStorage.newFileStorage(processIndex);
let statusDependencies;
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, callBackFunction) {
try {
statusDependencies = pStatusDependencies;
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] initialize -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
/*
This process is going to do the following:
Read the candles and volumes from Exchange Raw Data and produce a single Index File for Market Period. But this is the situation:
Exchange Raw Data has a dataset organized with daily files with candles of 1 min. Candles Volumes is writting in this process a single file for each timeFrame for the whole market.
Everytime this process run, must be able to resume its job and process everything pending until reaching the head of the market. So the tactic to do this is the
following:
1. First we need to read the last file written by this process, and load all the information into in-memory arrays. We will then append to this arrays the new
information we will get from Exchange Raw Data.
2. We know from out status report which was the last DAY we processed from Exchange Raw Data, but we must be carefull, because that day mightn not have been complete, if the
last run found the head of the market. That means that we have to be carefull not to append candles that are already there. To simplify what we do is to discard
all candles of the last processed day, and then we can process that full day again adding all the candles.
*/
function start(callBackFunction) {
try {
/* Context Variables */
let contextVariables = {
lastCandleFile: undefined, // Datetime of the last file files sucessfully produced by this process.
firstTradeFile: undefined, // Datetime of the first trade file in the whole market history.
maxCandleFile: undefined // Datetime of the last file available to be used as an input of this process.
};
getContextVariables();
function getContextVariables() {
try {
let thisReport;
let reportKey;
let statusReport;
/* We look first for Exchange Raw Data in order to get when the market starts. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.beginingOfMarket === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.");
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK)
return
}
contextVariables.firstTradeFile = new Date(
thisReport.beginingOfMarket.year + "-" +
thisReport.beginingOfMarket.month + "-" +
thisReport.beginingOfMarket.days + " " +
thisReport.beginingOfMarket.hours + ":" +
thisReport.beginingOfMarket.minutes +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
/* Second, we get the report from Exchange Raw Data, to know when the marted ends. */
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> reportKey = " + reportKey);
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message);
callBackFunction(customOK);
return;
}
contextVariables.maxCandleFile = new Date(
thisReport.lastFile.year + "-" +
thisReport.lastFile.month + "-" +
thisReport.lastFile.days + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
/* Finally we get our own Status Report. */
reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Market"
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
statusReport = statusDependencies.statusReports.get(reportKey);
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Status Report does not exist. Retrying Later. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
return
}
thisReport = statusDependencies.statusReports.get(reportKey).file;
if (thisReport.lastFile !== undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Process Running not for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(thisReport.beginingOfMarket);
if (beginingOfMarket.valueOf() !== contextVariables.firstTradeFile.valueOf()) { // Reset Mechanism for Begining of the Market
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated. -> reportKey = " + reportKey);
beginingOfMarket = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastCandleFile = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastCandleFile = new Date(
contextVariables.lastCandleFile.valueOf() -
TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles()
return
}
contextVariables.lastCandleFile = new Date(thisReport.lastFile);
/*
Here we assume that the last day written might contain incomplete information. This actually happens every time the head of the market is reached.
For that reason we go back one day, the partial information is discarded and added again with whatever new info is available.
*/
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS);
findPreviousContent()
return
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Process Running for the very first time. -> reportKey = " + reportKey);
beginingOfMarket = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastCandleFile = new Date(
contextVariables.firstTradeFile.getUTCFullYear() + "-" +
(contextVariables.firstTradeFile.getUTCMonth() + 1) + "-" +
contextVariables.firstTradeFile.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
contextVariables.lastCandleFile = new Date(
contextVariables.lastCandleFile.valueOf() -
TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS); // Go back one day to start well.
buildCandles()
return
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ");
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Dependencies loaded -> Double check that you are not running a process that only can be run at noTime mode at a certain month when it is not prepared to do so.");
}
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function findPreviousContent() {
/*
This is where we read the current files we have produced at previous runs of this same process. We just read all the content and organize it
in arrays and keep them in memory.
*/
try {
let n = 0 // loop Variable representing each possible period as defined at the periods array.
let allPreviousCandles = [];
let allPreviousVolumes = [];
loopBody()
function loopBody() {
let timeFrame = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][1];
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> findPreviousContent -> loopBody -> timeFrame = " + timeFrame)
let previousCandles
let previousVolumes
getCandles()
function getCandles() {
let fileName = 'Data.json';
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
CANDLES_FOLDER_NAME + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" +
timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> findPreviousContent -> loopBody -> getCandles -> getting file.");
function onFileReceived(err, text) {
let candlesFile
if (err.result === TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
previousCandles = candlesFile;
getVolumes();
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> fileName = " + fileName);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> filePath = " + filePath);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
}
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getCandles -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
}
}
}
function getVolumes() {
let fileName = 'Data.json';
let filePath =
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
VOLUMES_FOLDER_NAME + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" +
timeFrame;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> findPreviousContent -> loopBody -> getVolumes -> getting file.");
function onFileReceived(err, text) {
let volumesFile
if (err.result === TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
previousVolumes = volumesFile;
allPreviousCandles.push(previousCandles);
allPreviousVolumes.push(previousVolumes);
controlLoop();
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> fileName = " + fileName);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> filePath = " + filePath);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
}
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> loopBody -> getVolumes -> onFileReceived -> err = " + err.stack);
callBackFunction(err);
}
}
}
}
function controlLoop() {
n++
if (n < TS.projects.superalgos.globals.timeFrames.marketFilesPeriods().length) {
loopBody()
} else {
buildCandles(allPreviousCandles, allPreviousVolumes);
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> findPreviousContent -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function buildCandles(allPreviousCandles, allPreviousVolumes) {
try {
let fromDate = new Date(contextVariables.lastCandleFile.valueOf())
let lastDate = new Date()
/*
Firstly we prepere the arrays that will accumulate all the information for each output file.
*/
let outputCandles = [];
let outputVolumes = [];
for (let n = 0; n < TS.projects.superalgos.globals.timeFrames.marketFilesPeriods().length; n++) {
const emptyArray1 = [];
const emptyArray2 = [];
outputCandles.push(emptyArray1);
outputVolumes.push(emptyArray2);
}
advanceTime()
function advanceTime() {
contextVariables.lastCandleFile = new Date(contextVariables.lastCandleFile.valueOf() + TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> advanceTime -> New processing time @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + ".")
/* Validation that we are not going past the head of the market. */
if (contextVariables.lastCandleFile.valueOf() > contextVariables.maxCandleFile.valueOf()) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> advanceTime -> Head of the market found @ " + contextVariables.lastCandleFile.getUTCFullYear() + "/" + (contextVariables.lastCandleFile.getUTCMonth() + 1) + "/" + contextVariables.lastCandleFile.getUTCDate() + ".")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE); // Here is where we finish processing and wait for the platform to run this module again.
return
}
/* Telling the world we are alive and doing well */
let currentDateString =
contextVariables.lastCandleFile.getUTCFullYear() + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let currentDate = new Date(contextVariables.lastCandleFile)
let percentage = TS.projects.superalgos.utilities.dateTimeFunctions.getPercentage(fromDate, currentDate, lastDate)
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, currentDateString, percentage)
if (TS.projects.superalgos.utilities.dateTimeFunctions.areTheseDatesEqual(currentDate, new Date()) === false) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newInternalLoop(currentDate, percentage);
}
periodsLoop()
}
function periodsLoop() {
/*
We will iterate through all posible periods.
*/
let n = 0 // loop Variable representing each possible period as defined at the periods array.
loopBody()
function loopBody() {
let previousCandles
let previousVolumes
if (allPreviousCandles !== undefined) {
previousCandles = allPreviousCandles[n];
previousVolumes = allPreviousVolumes[n];
}
const outputPeriod = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][0];
const timeFrame = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][1];
/*
Here we are inside a Loop that is going to advance 1 day at the time, at each pass, will ready one of Exchange Raw Data's daily files and
add all its candles to our in memory arrays. At the first iteration of this loop, we will add the candles that we are carrying
from our previous run, the ones we already have in-memory. You can see below how we discard from those candles the ones that
are belonging to the first day we are processing at this run, that it is exactly the same as the last day processed the privious
run. By discarding these candles, we are ready to run after that standard function that will just add ALL the candles found each
day at Exchange Raw Data.
*/
if (previousCandles !== undefined) {
for (let i = 0; i < previousCandles.length; i++) {
let candle = {
open: previousCandles[i][2],
close: previousCandles[i][3],
min: previousCandles[i][0],
max: previousCandles[i][1],
begin: previousCandles[i][4],
end: previousCandles[i][5]
}
if (candle.end < contextVariables.lastCandleFile.valueOf()) {
outputCandles[n].push(candle);
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Candle # " + i + " @ " + timeFrame + " discarded for closing past the current process time.")
}
}
allPreviousCandles[n] = [] // erasing these so as not to duplicate them.
}
if (previousVolumes !== undefined) {
for (let i = 0; i < previousVolumes.length; i++) {
let volume = {
begin: previousVolumes[i][2],
end: previousVolumes[i][3],
buy: previousVolumes[i][0],
sell: previousVolumes[i][1]
}
if (volume.end < contextVariables.lastCandleFile.valueOf()) {
outputVolumes[n].push(volume);
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> Volume # " + i + " @ " + timeFrame + " discarded for closing past the current process time.")
}
}
allPreviousVolumes[n] = []; // erasing these so as not to duplicate them.
}
/*
From here on is where every iteration of the loop fully runs. Here is where we read Exchange Raw Data's files and add their content to whatever
we already have in our arrays in-memory. In this way the process will run as many days needed and it should only stop when it reaches
the head of the market.
*/
nextCandleFile();
function nextCandleFile() {
let dateForPath = contextVariables.lastCandleFile.getUTCFullYear() + '/' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot =
'Project/' +
TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.type.replace(' ', '-') + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "/" +
"Exchange-Raw-Data" + '/' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let filePath = filePathRoot + "/Output/" + CANDLES_FOLDER_NAME + '/' + CANDLES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
try {
let candlesFile
if (err.result === TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
try {
candlesFile = JSON.parse(text);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return
}
} else {
if (err.message === 'File does not exist.' || err.code === 'The specified key does not exist.') {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Dependency Not Ready -> err = " + JSON.stringify(err));
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err)
return
}
}
const inputCandlesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputCandles = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
/*
The algorithm that follows is going to agregate candles of 1 min timeFrame read from Exchange Raw Data, into candles of each timeFrame
that Candles Volumes generates. For market files those timePediods goes from 1h to 24hs.
*/
for (let i = 0; i < totalOutputCandles; i++) {
let outputCandle = {
open: 0,
close: 0,
min: 0,
max: 0,
begin: 0,
end: 0
};
let saveCandle = false;
outputCandle.begin = beginingOutputTime + i * outputPeriod;
outputCandle.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < candlesFile.length; j++) {
let candle = {
open: candlesFile[j][2],
close: candlesFile[j][3],
min: candlesFile[j][0],
max: candlesFile[j][1],
begin: candlesFile[j][4],
end: candlesFile[j][5]
}
/* Here we discard all the candles out of range. */
if (candle.begin >= outputCandle.begin && candle.end <= outputCandle.end) {
if (saveCandle === false) { // this will set the value only once.
outputCandle.open = candle.open;
outputCandle.min = candle.min;
outputCandle.max = candle.max;
}
saveCandle = true;
outputCandle.close = candle.close; // only the last one will be saved
if (candle.min < outputCandle.min) {
outputCandle.min = candle.min;
}
if (candle.max > outputCandle.max) {
outputCandle.max = candle.max;
}
}
}
if (saveCandle === true) { // then we have a valid candle, otherwise it means there were no candles to fill this one in its time range.
outputCandles[n].push(outputCandle);
}
}
nextVolumeFile();
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextCandleFile -> onFileReceived -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
function nextVolumeFile() {
try {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> Entering function.")
let dateForPath =
contextVariables.lastCandleFile.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(contextVariables.lastCandleFile.getUTCDate(), 2);
let fileName = "Data.json"
let filePathRoot =
'Project/' +
TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.type.replace(' ', '-') + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "/" +
"Exchange-Raw-Data" + '/' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let filePath = filePathRoot + "/Output/" + VOLUMES_FOLDER_NAME + '/' + VOLUMES_ONE_MIN + '/' + dateForPath;
filePath += '/' + fileName
fileStorage.getTextFile(filePath, onFileReceived);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> getting file at dateForPath = " + dateForPath);
function onFileReceived(err, text) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Entering function.")
let volumesFile
if (err.result === TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
try {
volumesFile = JSON.parse(text);
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Parsing JSON -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry.");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
} else {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> Error Received -> err = " + err.stack);
callBackFunction(err);
return;
}
const inputVolumesPerdiod = 60 * 1000; // 1 min
const inputFilePeriod = 24 * 60 * 60 * 1000; // 24 hs
let totalOutputVolumes = inputFilePeriod / outputPeriod; // this should be 2 in this case.
let beginingOutputTime = contextVariables.lastCandleFile.valueOf();
for (let i = 0; i < totalOutputVolumes; i++) {
let outputVolume = {
buy: 0,
sell: 0,
begin: 0,
end: 0
}
let saveVolume = false;
outputVolume.begin = beginingOutputTime + i * outputPeriod;
outputVolume.end = beginingOutputTime + (i + 1) * outputPeriod - 1;
for (let j = 0; j < volumesFile.length; j++) {
let volume = {
buy: volumesFile[j][0],
sell: volumesFile[j][1],
begin: volumesFile[j][2],
end: volumesFile[j][3]
}
/* Here we discard all the Volumes out of range. */
if (volume.begin >= outputVolume.begin && volume.end <= outputVolume.end) {
saveVolume = true;
outputVolume.buy = outputVolume.buy + volume.buy;
outputVolume.sell = outputVolume.sell + volume.sell;
}
}
if (saveVolume === true) {
outputVolumes[n].push(outputVolume);
}
}
writeFiles(outputCandles[n], outputVolumes[n], timeFrame, controlLoop);
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> periodsLoop -> loopBody -> nextVolumeFile -> onFileReceived -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
}
function controlLoop() {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> buildCandles -> periodsLoop -> controlLoop -> Entering function.")
n++
if (n < TS.projects.superalgos.globals.timeFrames.marketFilesPeriods().length) {
loopBody()
} else {
writeStatusReport(contextVariables.lastCandleFile, advanceTime);
}
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> buildCandles -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeFiles(candles, volumes, timeFrame, callBack) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeFiles -> Entering function.")
/*
Here we will write the contents of the Candles and Volumens files.
*/
try {
writeCandles()
function writeCandles() {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeFiles -> writeCandles -> Entering function.")
let separator = ""
let fileRecordCounter = 0
let fileContent = ""
for (let i = 0; i < candles.length; i++) {
let candle = candles[i];
fileContent = fileContent + separator + '[' + candles[i].min + "," + candles[i].max + "," + candles[i].open + "," + candles[i].close + "," + candles[i].begin + "," + candles[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT +
"/Output/" +
CANDLES_FOLDER_NAME + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" +
timeFrame
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeFiles -> writeCandles -> creating file at filePath = " + filePath);
function onFileCreated(err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeFiles -> writeCandles -> onFileCreated -> Entering function.")
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeFiles -> writeCandles -> onFileCreated -> err = " + err.stack)
callBackFunction(err)
return
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> writeFiles -> writeCandles -> onFileCreated -> Finished with File @ " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName)
writeVolumes()
}
}
function writeVolumes() {
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < volumes.length; i++) {
let candle = volumes[i];
fileContent = fileContent + separator + '[' + volumes[i].buy + "," + volumes[i].sell + "," + volumes[i].begin + "," + volumes[i].end + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++
}
fileContent = "[" + fileContent + "]";
let fileName = 'Data.json';
let filePath = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT + "/Output/" + VOLUMES_FOLDER_NAME + "/" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + "/" + timeFrame;
filePath += '/' + fileName
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeFiles -> writeVolumes -> creating file at filePath = " + filePath);
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeFiles -> writeVolumes -> onFileCreated -> err = " + err.stack);
callBackFunction(err);
return;
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> writeFiles -> writeVolumes -> onFileCreated -> Finished with File @ " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + "_" + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName + ", " + fileRecordCounter + " records inserted into " + filePath + "/" + fileName);
callBack()
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeFiles -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function writeStatusReport(lastFileDate, callBack) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> writeStatusReport -> lastFileDate = " + lastFileDate)
try {
let reportKey = "Masters" + "-" + "Candles-Volumes" + "-" + "Multi-Period-Market"
let thisReport = statusDependencies.statusReports.get(reportKey)
thisReport.file.lastExecution = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME
thisReport.file.lastFile = lastFileDate
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
thisReport.save(callBack)
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
}
@@ -1,38 +1,27 @@
exports.newIndicatorOutput = function newIndicatorOutput(bot, logger, UTILITIES, FILE_STORAGE) {
const FULL_LOG = true;
exports.newSuperalgosBotModulesIndicatorOutput = function (processIndex) {
const MODULE_NAME = "Indicator Bot";
let thisObject = {
initialize: initialize,
finalize: finalize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(logger);
let fileStorage = FILE_STORAGE.newFileStorage(logger);
const COMMONS = require('./Commons.js');
let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE);
}
return thisObject;
function finalize() {
thisObject = undefined
utilities = undefined
fileStorage = undefined
commons = undefined
}
function initialize(callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
callBackFunction(global.DEFAULT_OK_RESPONSE);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] initialize -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
@@ -51,15 +40,15 @@
}
/* The first phase here is about checking that we have everything we need at the definition level. */
let dataDependencies = global.NODE_BRANCH_TO_ARRAY(bot.processNode.referenceParent.processDependencies, 'Data Dependency')
let dataDependencies = TS.projects.superalgos.utilities.nodeFunctions.nodeBranchToArray(TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.processDependencies, 'Data Dependency')
if (commons.validateDataDependencies(dataDependencies, callBackFunction) !== true) { return }
if (TS.projects.superalgos.functionLibraries.singleMarketFunctions.validateDataDependencies(processIndex, dataDependencies, callBackFunction) !== true) { return }
let outputDatasets = global.NODE_BRANCH_TO_ARRAY (bot.processNode.referenceParent.processOutput, 'Output Dataset')
if (commons.validateOutputDatasets(outputDatasets, callBackFunction) !== true) { return }
let outputDatasets = TS.projects.superalgos.utilities.nodeFunctions.nodeBranchToArray (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.processOutput, 'Output Dataset')
if (TS.projects.superalgos.functionLibraries.singleMarketFunctions.validateOutputDatasets(processIndex, outputDatasets, callBackFunction) !== true) { return }
/* The second phase is about transforming the inputs into a format that can be used to apply the user defined code. */
commons.inflateDatafiles(dataFiles, dataDependencies, products, mainDependency, timeFrame)
TS.projects.superalgos.functionLibraries.singleMarketFunctions.inflateDatafiles(processIndex, dataFiles, dataDependencies, products, mainDependency, timeFrame)
/* During the third phase, we need to generate the data of the different products this process produces an output */
for (let i = 0; i < outputDatasets.length; i++) {
@@ -88,7 +77,7 @@
}
/* Build the data */
jsonData = commons.dataBuildingProcedure(
jsonData = TS.projects.superalgos.functionLibraries.singleMarketFunctions.dataBuildingProcedure(processIndex,
products,
mainDependency,
recordDefinition,
@@ -105,7 +94,7 @@
/* Add the calculated properties */
if (outputDatasetNode.referenceParent.parentNode.calculations !== undefined) {
outputData = commons.calculationsProcedure(jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timeFrame)
outputData = TS.projects.superalgos.functionLibraries.singleMarketFunctions.calculationsProcedure(processIndex, jsonData, recordDefinition, outputDatasetNode.referenceParent.parentNode.calculations, singularVariableName, timeFrame)
} else {
outputData = jsonData
}
@@ -130,28 +119,28 @@
contextSummary.dataset = outputDatasetNode.referenceParent.config.codeName
contextSummary.product = outputDatasetNode.referenceParent.parentNode.config.codeName
let botNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Indicator Bot')
let botNode = TS.projects.superalgos.utilities.nodeFunctions.findNodeInNodeMesh(outputDatasetNode, 'Indicator Bot')
contextSummary.bot = botNode.config.codeName
let dataMineNode = global.FIND_NODE_IN_NODE_MESH(outputDatasetNode, 'Data Mine')
let dataMineNode = TS.projects.superalgos.utilities.nodeFunctions.findNodeInNodeMesh(outputDatasetNode, 'Data Mine')
contextSummary.dataMine = dataMineNode.config.codeName
contextSummary.mineType = dataMineNode.type.replace(' ', '-')
contextSummary.project = dataMineNode.project
let fileContent = commons.generateFileContent(outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction)
commons.writeFile(contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timeFrameLabel, currentDay, callBackFunction)
let fileContent = TS.projects.superalgos.functionLibraries.singleMarketFunctions.generateFileContent(processIndex, outputData, outputDatasetNode.referenceParent.parentNode.record, resultsWithIrregularPeriods, processingDailyFiles, currentDay, callBackFunction)
TS.projects.superalgos.functionLibraries.singleMarketFunctions.writeFile(processIndex, contextSummary, fileContent, anotherFileWritten, processingDailyFiles, timeFrameLabel, currentDay, callBackFunction)
}
function anotherFileWritten() {
totalFilesWritten++
if (totalFilesWritten === outputDatasets.length) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -0,0 +1,625 @@
exports.newSuperalgosBotModulesMultiPeriodDaily = function (processIndex) {
const MODULE_NAME = "Multi Period Daily"
thisObject = {
initialize: initialize,
finalize: finalize,
start: start
};
let fileStorage = TS.projects.superalgos.taskModules.fileStorage.newFileStorage(processIndex)
let statusDependencies
let dataDependenciesModule
let dataFiles = new Map
let indicatorOutputModule
let bootstrappingTheProcess = false
let beginingOfMarket
return thisObject;
function initialize(pStatusDependencies, pDataDependencies, callBackFunction) {
statusDependencies = pStatusDependencies;
dataDependenciesModule = pDataDependencies;
indicatorOutputModule = TS.projects.superalgos.botModules.indicatorOutput.newSuperalgosBotModulesIndicatorOutput(processIndex)
indicatorOutputModule.initialize(callBackFunction)
}
function finalize() {
dataFiles = undefined
statusDependencies = undefined
dataDependenciesModule = undefined
indicatorOutputModule = undefined
fileStorage = undefined
thisObject = undefined
}
function start(callBackFunction) {
try {
/* Context Variables */
let contextVariables = {
lastFile: undefined, // Datetime of the last file files sucessfully produced by this process.
dateBeginOfMarket: undefined, // Datetime of the first trade file in the whole market history.
dateEndOfMarket: undefined // Datetime of the last file available to be used as an input of this process.
};
let previousDay; // Holds the date of the previous day relative to the processing date.
let interExecutionMemoryArray;
getContextVariables()
function getContextVariables() {
try {
let thisReport;
let statusReport;
/*
We look first for the bot who knows the begining of the market in order to get when the market starts.
*/
statusReport = statusDependencies.reportsByMainUtility.get("Market Starting Point")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Market Starting Point -> Status Report does not exist or Market Starting Point not defined. Retrying Later. ")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
thisReport = statusReport.file;
if (thisReport.beginingOfMarket === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> thisReport.beginingOfMarket === undefined ")
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> It is too early too run this process since the trade history of the market is not there yet.")
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency does not exist."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message)
callBackFunction(customOK)
return
}
contextVariables.dateBeginOfMarket = new Date(
thisReport.beginingOfMarket.year + "-" +
thisReport.beginingOfMarket.month + "-" +
thisReport.beginingOfMarket.days + " " +
thisReport.beginingOfMarket.hours + ":" +
thisReport.beginingOfMarket.minutes +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS
)
/*
Here we get the status report from the bot who knows which is the end of the market.
*/
statusReport = statusDependencies.reportsByMainUtility.get("Market Ending Point")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Market Ending Point -> Status Report does not exist or Market Ending Point not defined. Retrying Later. ")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
thisReport = statusReport.file;
if (thisReport.lastFile === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Undefined Last File. -> thisReport.lastFile === undefined")
let customOK = {
result: TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result,
message: "Dependency not ready."
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> customOK = " + customOK.message)
callBackFunction(customOK)
return
}
contextVariables.dateEndOfMarket = new Date(thisReport.lastFile.valueOf())
/* Finally we get our own Status Report. */
statusReport = statusDependencies.reportsByMainUtility.get("Self Reference")
if (statusReport === undefined) { // This means the status report does not exist, that could happen for instance at the begining of a month.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> getContextVariables -> Self Reference -> Status Report does not exist or Self Reference not defined or badly configured. Check that at the Status Dependency you have chosen Self Reference at the config. Retrying Later. ")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
if (statusReport.status === "Status Report is corrupt.") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because self dependecy Status Report is corrupt. Aborting Process.")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
return
}
thisReport = statusReport.file;
if (thisReport.lastFile !== undefined) {
beginingOfMarket = new Date(thisReport.beginingOfMarket)
if (beginingOfMarket.valueOf() !== contextVariables.dateBeginOfMarket.valueOf()) { // Reset Mechanism for Begining of the Market
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Reset Mechanism for Begining of the Market Activated.")
beginingOfMarket = new Date(contextVariables.dateBeginOfMarket.valueOf())
startFromBegining()
return
}
contextVariables.lastFile = new Date(thisReport.lastFile)
interExecutionMemoryArray = thisReport.interExecutionMemoryArray;
processTimeFrames()
} else {
/*
In the case when there is no status report, we assume like the last processed file is the one on the date of Begining of Market.
*/
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> Starting from the begining of the market because own status report not found or lastFile was undefined.")
beginingOfMarket = new Date(contextVariables.dateBeginOfMarket.valueOf())
startFromBegining()
}
function startFromBegining() {
contextVariables.lastFile = new Date(
contextVariables.dateBeginOfMarket.getUTCFullYear() + "-" +
(contextVariables.dateBeginOfMarket.getUTCMonth() + 1) + "-" +
contextVariables.dateBeginOfMarket.getUTCDate() + " " + "00:00" +
TS.projects.superalgos.globals.timeConstants.GMT_SECONDS
)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> startFromBegining -> contextVariables.lastFile = " + contextVariables.lastFile)
/*
The first time the process is running is the right time to create the data structure that is going to be shared across different executions.
This data structure has one object per each timeFrame.
*/
interExecutionMemoryArray = []
/*
Also, we will remember that we are bootstrapping the process, this will allow us, to advanceTime if necesary.
It can happen that a process depends on data from another process that does not produce a file during the first day, or even more days,
in that situation we want the process not to just wait for that data that will never arrive, but to advance time until it finds valid data.
*/
bootstrappingTheProcess = true
for (let i = 0; i < TS.projects.superalgos.globals.timeFrames.dailyFilePeriods().length; i++) {
let emptyObject = {};
interExecutionMemoryArray.push(emptyObject)
}
processTimeFrames()
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> err = " + err.stack)
if (err.message === "Cannot read property 'file' of undefined") {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Check the bot configuration to see if all of its statusDependencies declarations are correct. ")
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys))
}
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
function processTimeFrames() {
let n;
let botNeverRan = true;
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME =
new Date(contextVariables.lastFile.valueOf() - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS) // Go back one day to start well when we advance time at the begining of the loop.
let fromDate = new Date(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.valueOf())
let lastDate = new Date()
advanceTime()
function advanceTime() {
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME =
new Date(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.valueOf() +
TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS)
previousDay = new Date(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.valueOf() -
TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> processTimeFrames -> advanceTime -> TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME = " +
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> processTimeFrames -> advanceTime -> previousDay = " + previousDay)
/* Validation that we are not going past the head of the market. */
if (TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.valueOf() > contextVariables.dateEndOfMarket.valueOf()) {
const logText = "Head of the market found @ " + previousDay.getUTCFullYear() + "/" + (previousDay.getUTCMonth() + 1) + "/" + previousDay.getUTCDate() + ".";
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> processTimeFrames -> advanceTime -> " + logText)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
return
}
/* Telling the world we are alive and doing well */
let currentDateString = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.getUTCFullYear() + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.getUTCMonth() + 1, 2) + '-' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.getUTCDate(), 2)
let currentDate = new Date(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME)
let percentage = TS.projects.superalgos.utilities.dateTimeFunctions.getPercentage(fromDate, currentDate, lastDate)
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, currentDateString, percentage)
if (TS.projects.superalgos.utilities.dateTimeFunctions.areTheseDatesEqual(currentDate, new Date()) === false) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newInternalLoop(currentDate, percentage)
}
checkStopTaskGracefully()
}
function checkStopTaskGracefully() {
/* Validation that we dont need to stop. */
if (TS.projects.superalgos.globals.taskVariables.IS_TASK_STOPPING === true) {
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
return
}
timeFramesLoop()
}
function timeFramesLoop() {
/*
We will iterate through all posible timeFrames.
*/
n = 0 // loop Variable representing each possible period as defined at the timeFrames array.
timeFramesLoopBody()
}
function timeFramesLoopBody() {
const timeFrame = TS.projects.superalgos.globals.timeFrames.dailyFilePeriods()[n][0]
const timeFrameLabel = TS.projects.superalgos.globals.timeFrames.dailyFilePeriods()[n][1]
/* Check Time Frames Filter */
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.dailyTimeFrames !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.dailyTimeFrames.includes(timeFrameLabel) === false) {
/* We are not going to process this Time Frame */
timeFramesControlLoop()
return
}
}
}
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.framework.validPeriods !== undefined) {
let validPeriod = false;
for (let i = 0; i < TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.framework.validPeriods.length; i++) {
let period = TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.framework.validPeriods[i]
if (period === timeFrameLabel) { validPeriod = true }
}
if (validPeriod === false) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> processTimeFrames -> timeFramesLoopBody -> Discarding period for not being listed as a valid period. -> timeFrameLabel = " + timeFrameLabel)
timeFramesControlLoop()
return
}
}
let dependencyIndex = 0
dataFiles = new Map()
dependencyLoopBody()
function dependencyLoopBody() {
let dependency = dataDependenciesModule.nodeArray[dependencyIndex]
if (dependency === undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> You need to add at least one Data Dependency to the process Multi Period Daily. Aborting process.")
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
return
}
let datasetModule = dataDependenciesModule.dataSetsModulesArray[dependencyIndex]
let previousFile
let currentFile
if (TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.valueOf() > contextVariables.dateBeginOfMarket.valueOf()) {
getPreviousFile()
} else {
previousFile = []
getCurrentFile()
}
function getPreviousFile() {
let dateForPath = previousDay.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(previousDay.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(previousDay.getUTCDate(), 2)
let filePath
if (dependency.referenceParent.config.codeName === "Multi-Period-Daily") {
filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + timeFrameLabel + "/" + dateForPath;
} else {
filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + dateForPath;
}
let fileName = "Data.json"
datasetModule.getTextFile(filePath, fileName, onFileReceived)
function onFileReceived(err, text) {
if ((err.message === "File does not exist." && botNeverRan === true) || err.code === 'The specified key does not exist.') {
/*
Sometimes one of the dependencies of an indicator for some reasons are not calculated from the begining of the market.
When that happens we can not find those files. What we do in this situation is to move the time fordward until we can find
all the dependencies and the first run of the bot is successful.
After that, we will not accept more missing files on any of the dependencies, and if any is missing we will abort the processing.
*/
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> Skipping day because file " + filePath + "/" + fileName + " was not found.")
advanceTime()
return
}
if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = " + err.stack)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getPreviousFile -> onFileReceived -> err = " + err.stack)
callBackFunction(err)
return
}
previousFile = JSON.parse(text)
getCurrentFile()
}
}
function getCurrentFile() {
let dateForPath = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.getUTCFullYear() + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME.getUTCDate(), 2)
let filePath
if (dependency.referenceParent.config.codeName === "Multi-Period-Daily") {
filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + timeFrameLabel + "/" + dateForPath;
} else {
filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + dateForPath;
}
let fileName = "Data.json";
datasetModule.getTextFile(filePath, fileName, onFileReceived)
function onFileReceived(err, text) {
if ((err.result === "Fail Because" && err.message === "File does not exist.") || err.code === 'The specified key does not exist.') {
if (bootstrappingTheProcess === true) {
/*
In the special situation where we are running this process for the first time (no status report found),
we will consider the possibility that a depanant process does not produce a file during the first day of the market,
or even more than one day. That is why we are going to advance time here.
*/
advanceTime()
return
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> err = " + err.code)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> filePath = " + filePath)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getCurrentFile -> onFileReceived -> fileName = " + fileName)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE)
return
}
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getCurrentFile -> Not OK -> onFileReceived -> err = " + err.code)
callBackFunction(err)
return
}
currentFile = JSON.parse(text)
let dataFile = previousFile.concat(currentFile)
dataFiles.set(dependency.id, dataFile)
dependencyControlLoop()
}
}
}
function dependencyControlLoop() {
dependencyIndex++;
if (dependencyIndex < dataDependenciesModule.nodeArray.length) {
dependencyLoopBody()
} else {
generateOutput()
}
function generateOutput() {
indicatorOutputModule.start(
dataFiles,
timeFrame,
timeFrameLabel,
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME,
interExecutionMemoryArray[n],
onOutputGenerated)
function onOutputGenerated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
callBackFunction(err)
return
}
botNeverRan = false;
timeFramesControlLoop()
}
}
}
}
function timeFramesControlLoop() {
n++
if (n < TS.projects.superalgos.globals.timeFrames.dailyFilePeriods().length) {
timeFramesLoopBody()
} else {
n = 0;
writeDataRanges(onWritten)
function onWritten(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> controlLoop -> onWritten -> err = " + err.stack)
callBackFunction(err)
return
}
writeStatusReport(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME, advanceTime)
}
}
}
}
function writeDataRanges(callBack) {
let outputDatasets = TS.projects.superalgos.utilities.nodeFunctions.nodeBranchToArray(
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.processOutput, 'Output Dataset')
let outputDatasetIndex = -1;
controlLoop()
function productLoopBody() {
let productCodeName = outputDatasets[outputDatasetIndex].referenceParent.parentNode.config.codeName;
writeDataRange(
contextVariables.dateBeginOfMarket,
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).DAILY_FILES_PROCESS_DATETIME,
productCodeName,
controlLoop
)
}
function controlLoop() {
outputDatasetIndex++
if (outputDatasetIndex < outputDatasets.length) {
productLoopBody()
} else {
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
}
}
}
function writeDataRange(pBegin, pEnd, productCodeName, callBack) {
let dataRange = {
begin: pBegin.valueOf(),
end: pEnd.valueOf() + TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS
};
let fileContent = JSON.stringify(dataRange)
let fileName = '/Data.Range.json';
let filePath = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT + "/Output/" + productCodeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + fileName;
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated)
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeDataRange -> onFileCreated -> err = " + err.stack)
callBack(err)
return
}
let key = TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.config.codeName + "-" + productCodeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + '/' +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
let event = {
dateRange: dataRange
}
TS.projects.superalgos.globals.taskConstants.EVENT_SERVER_CLIENT_MODULE_OBJECT.raiseEvent(key, 'Data Range Updated', event)
writeTimeFramesFile(productCodeName, callBack)
}
}
function writeTimeFramesFile(productCodeName, callBack) {
let timeFramesArray = []
for (let n = 0; n < TS.projects.superalgos.globals.timeFrames.dailyFilePeriods().length; n++) {
let timeFrameLabel = TS.projects.superalgos.globals.timeFrames.dailyFilePeriods()[n][1]
/* Check Time Frames Filter */
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.dailyTimeFrames !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.dailyTimeFrames.includes(timeFrameLabel) === true) {
timeFramesArray.push(timeFrameLabel)
}
} else {
timeFramesArray.push(timeFrameLabel)
}
} else {
timeFramesArray.push(timeFrameLabel)
}
}
let fileContent = JSON.stringify(timeFramesArray)
let fileName = '/Time.Frames.json';
let filePath = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT + "/Output/" + productCodeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + fileName;
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated)
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeTimeFramesFiles -> onFileCreated -> err = " + err.stack)
callBack(err)
return
}
callBack(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
}
}
function writeStatusReport(lastFileDate, callBack) {
let reportKey = TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.config.codeName + "-" + "Multi-Period-Daily"
let thisReport = statusDependencies.statusReports.get(reportKey)
thisReport.file.lastFile = lastFileDate;
thisReport.file.interExecutionMemoryArray = interExecutionMemoryArray;
thisReport.file.beginingOfMarket = beginingOfMarket.toUTCString()
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.dailyTimeFrames !== undefined) {
thisReport.file.timeFrames = TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.dailyTimeFrames
}
}
thisReport.save(callBack)
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> err = " + err.stack)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
};
@@ -0,0 +1,227 @@
exports.newSuperalgosBotModulesMultiPeriodMarket = function (processIndex) {
const MODULE_NAME = "Multi Period Market";
thisObject = {
initialize: initialize,
finalize: finalize,
start: start
};
let fileStorage = TS.projects.superalgos.taskModules.fileStorage.newFileStorage(processIndex)
let statusDependencies
let dataDependenciesModule
let dataFiles = new Map()
let indicatorOutputModule
return thisObject;
function initialize(pStatusDependencies, pDataDependencies, callBackFunction) {
statusDependencies = pStatusDependencies;
dataDependenciesModule = pDataDependencies;
indicatorOutputModule = TS.projects.superalgos.botModules.indicatorOutput.newSuperalgosBotModulesIndicatorOutput(processIndex)
indicatorOutputModule.initialize(callBackFunction)
}
function finalize() {
fileStorage = undefined
dataFiles = undefined
statusDependencies = undefined
dataDependenciesModule = undefined
indicatorOutputModule = undefined
thisObject = undefined
}
function start(callBackFunction) {
try {
processTimeFrames()
function processTimeFrames() {
let n;
timeFramesLoop()
function timeFramesLoop() {
/* We will iterate through all posible timeFrames.*/
n = 0 // loop Variable representing each possible period as defined at the timeFrames array.
timeFramesLoopBody()
}
function timeFramesLoopBody() {
const timeFrame = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][0]
const timeFrameLabel = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][1]
/* Check Time Frames Filter */
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.marketTimeFrames !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.marketTimeFrames.includes(timeFrameLabel) === false) {
/* We are not going to process this Time Frame */
timeFramesControlLoop()
return
}
}
}
let dependencyIndex = 0;
dataFiles = new Map;
dependencyLoopBody()
function dependencyLoopBody() {
let dependency = dataDependenciesModule.nodeArray[dependencyIndex]
let datasetModule = dataDependenciesModule.dataSetsModulesArray[dependencyIndex]
getFile()
function getFile() {
let fileName = "Data.json";
let filePath = dependency.referenceParent.parentNode.config.codeName + '/' + dependency.referenceParent.config.codeName + "/" + timeFrameLabel;
datasetModule.getTextFile(filePath, fileName, onFileReceived)
function onFileReceived(err, text) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> processTimeFrames -> timeFramesLoopBody -> dependencyLoopBody -> getFile -> onFileReceived -> err = " + err.stack)
callBackFunction(err)
return;
}
let dataFile = JSON.parse(text)
dataFiles.set(dependency.id, dataFile)
dependencyControlLoop()
}
}
}
function dependencyControlLoop() {
dependencyIndex++;
if (dependencyIndex < dataDependenciesModule.nodeArray.length) {
dependencyLoopBody()
} else {
generateOutput()
}
function generateOutput() {
indicatorOutputModule.start(
dataFiles,
timeFrame,
timeFrameLabel,
undefined,
undefined,
onOutputGenerated)
function onOutputGenerated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
callBackFunction(err)
return;
}
timeFramesControlLoop()
}
}
}
}
function timeFramesControlLoop() {
n++;
if (n < TS.projects.superalgos.globals.timeFrames.marketFilesPeriods().length) {
timeFramesLoopBody()
} else {
writeTimeFramesFiles(onTimeFrameFilesWritten)
function onTimeFrameFilesWritten() {
writeStatusReport(callBackFunction)
}
}
}
}
function writeTimeFramesFiles(callBack) {
let outputDatasets =
TS.projects.superalgos.utilities.nodeFunctions.nodeBranchToArray(TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.processOutput, 'Output Dataset')
let outputDatasetIndex = -1;
controlLoop()
function productLoopBody() {
let productCodeName = outputDatasets[outputDatasetIndex].referenceParent.parentNode.config.codeName;
writeTimeFramesFile(productCodeName, controlLoop)
}
function controlLoop() {
outputDatasetIndex++
if (outputDatasetIndex < outputDatasets.length) {
productLoopBody()
} else {
callBack()
}
}
}
function writeTimeFramesFile(productCodeName, callBack) {
let timeFramesArray = []
for (let n = 0; n < TS.projects.superalgos.globals.timeFrames.marketFilesPeriods().length; n++) {
let timeFrameLabel = TS.projects.superalgos.globals.timeFrames.marketFilesPeriods()[n][1]
/* Check Time Frames Filter */
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.marketTimeFrames !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.marketTimeFrames.includes(timeFrameLabel) === true) {
timeFramesArray.push(timeFrameLabel)
}
} else {
timeFramesArray.push(timeFrameLabel)
}
} else {
timeFramesArray.push(timeFrameLabel)
}
}
let fileContent = JSON.stringify(timeFramesArray)
let fileName = '/Time.Frames.json';
let filePath = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT + "/Output/" + productCodeName + "/" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.codeName + fileName;
fileStorage.createTextFile(filePath, fileContent + '\n', onFileCreated)
function onFileCreated(err) {
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeTimeFramesFile -> onFileCreated -> err = " + err.stack)
callBack(err)
return
}
callBack()
}
}
function writeStatusReport(callBack) {
let reportKey = TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.parentNode.config.codeName + "-" +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.parentNode.config.codeName + "-" + "Multi-Period-Market"
let thisReport = statusDependencies.statusReports.get(reportKey)
thisReport.file.lastExecution = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME;
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.marketTimeFrames !== undefined) {
thisReport.file.timeFrames = TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.timeFramesFilter.config.marketTimeFrames
}
}
thisReport.save(callBack)
if (TS.projects.superalgos.utilities.dateTimeFunctions.areTheseDatesEqual(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME, new Date()) === false) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newInternalLoop(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME)
}
/* Telling the world we are alive and doing well */
let currentDateString = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME.getUTCFullYear() + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME.getUTCMonth() + 1, 2) + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME.getUTCDate(), 2)
let currentDate = new Date(TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME)
let lastDate = new Date()
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, currentDateString, TS.projects.superalgos.utilities.dateTimeFunctions.getPercentage(currentDate, currentDate, lastDate))
}
}
catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> err = " + err.stack)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
}
@@ -0,0 +1,823 @@
exports.newSuperalgosBotModulesSingleMarketIndicatorBot = function (processIndex) {
const MODULE_NAME = "Single Market Indicator Bot";
let thisObject = {
initialize: initialize,
run: run
}
let nextLoopTimeoutHandle
let botModuleObject
return thisObject
function initialize(callBackFunction) {
try {
/* We will check that we have received all the nodes needed to run this bot. */
if (TS.projects.superalgos.functionLibraries.singleMarketFunctions.checkUpstreamOfTaskNode(processIndex) === false) {
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
return
}
/* Here we setup the path prefix that will be used when writting data or logs to disk. */
TS.projects.superalgos.functionLibraries.singleMarketFunctions.initializeFilePathRoot(processIndex)
/*
Bots can be defined at the UI, using one of the available existing frameworks for that,
or they might be coded at a bot module. In the later case, the name of the botModule
must be specified at the bot process config.
*/
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.botModule === undefined) {
/* The code of the bot is defined at the UI. No need to load a file with the code. */
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
return
} else {
/*
Here we will need to scan the PROJECTS SCHEMA in order to find the botModule
defined for this Sensor Bot.
*/
let botModuleDefinition = TS.projects.superalgos.functionLibraries.taskFunctions.getBotModuleByName(
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.botModule
)
let project = TS.projects[TS.projects.superalgos.globals.taskConstants.PROJECT_DEFINITION_NODE.config.codeName.toLowerCase()]
let botModule = project.botModules[botModuleDefinition.propertyName]
let moduleFunction = botModule[botModuleDefinition.functionName]
botModuleObject = moduleFunction(processIndex)
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
return
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_INSTANCE_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] initialize -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
}
}
function run(callBackFunction) {
try {
loop();
function loop() {
try {
function pad(str, max) {
str = str.toString();
return str.length < max ? pad(" " + str, max) : str;
}
/* For each loop we want to create a new log file. */
if (TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT !== undefined) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.finalize()
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT =
TS.projects.superalgos.taskModules.debugLog.newSuperalgosTaskModulesDebugLog(processIndex)
TS.projects.superalgos.globals.taskVariables.LOGGER_MAP.set(
MODULE_NAME + TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].id,
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT
)
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).MAIN_LOOP_COUNTER++;
/* We tell the UI that we are running. */
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex)
/* We define the datetime for the process that we are running now. This will be the official processing time for both the infraestructure and the bot. */
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_DATETIME = new Date(); // This will be considered the process date and time, so as to have it consistenly all over the execution.
/* Main Loop Console Logging */
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newMainLoop()
/* We will prepare first the infraestructure needed for the bot to run. There are 3 modules we need to sucessfullly initialize first. */
let processExecutionEvents
let processFramework;
let statusDependencies;
let dataDependencies;
let nextWaitTime;
initializeProcessExecutionEvents();
function initializeProcessExecutionEvents() {
try {
processExecutionEvents = TS.projects.superalgos.processModules.processExecutionEvents.newSuperalgosProcessModulesProcessExecutionEvents(processIndex)
processExecutionEvents.initialize(onInizialized);
function onInizialized(err) {
try {
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Execution finished well.");
startProcessExecutionEvents()
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeProcessExecutionEvents -> onInizialized -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeProcessExecutionEvents -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function startProcessExecutionEvents() {
try {
processExecutionEvents.start(onStarted);
function onStarted(err) {
try {
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> startProcessExecutionEvents -> onStarted -> Execution finished well.");
if (TS.projects.superalgos.globals.taskVariables.IS_TASK_STOPPING === true) {
loopControl()
return
}
initializeStatusDependencies();
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> startProcessExecutionEvents -> onStarted -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessExecutionEvents -> onStarted -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessExecutionEvents -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function initializeStatusDependencies() {
try {
statusDependencies = TS.projects.superalgos.processModules.statusDependencies.newSuperalgosProcessModulesStatusDependencies(processIndex);
statusDependencies.initialize(onInizialized);
function onInizialized(err) {
try {
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> initializeStatusDependencies -> onInizialized -> Execution finished well.");
initializeDataDependencies();
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> initializeStatusDependencies -> onInizialized -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeStatusDependencies -> onInizialized -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeStatusDependencies -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function initializeDataDependencies() {
try {
dataDependencies = TS.projects.superalgos.processModules.dataDependencies.newSuperalgosProcessModulesDataDependencies(processIndex);
dataDependencies.initialize(onInizialized);
function onInizialized(err) {
try {
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
/* If the process is configured to run inside a framework, we continue there, otherwise we run the bot directly. */
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.framework === undefined) {
initializeBotModuleObject();
return;
}
switch (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.framework.name) {
case 'Multi-Period-Market': {
processFramework = TS.projects.superalgos.botModules.multiPeriodMarket.newSuperalgosBotModulesMultiPeriodMarket(processIndex);
intitializeProcessFramework();
break;
}
case 'Multi-Period-Daily': {
processFramework = TS.projects.superalgos.botModules.multiPeriodDaily.newSuperalgosBotModulesMultiPeriodDaily(processIndex);
intitializeProcessFramework();
break;
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework not Supported.");
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Process Framework Name = " +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.framework.name);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> initializeDataDependencies -> onInizialized -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
processStopped()
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeDataDependencies -> onInizialized -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeDataDependencies -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function initializeBotModuleObject() {
try {
botModuleObject.initialize(statusDependencies, onInizialized);
function onInizialized(err) {
try {
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> initializeBotModuleObject -> onInizialized -> Execution finished well.");
startBotModuleObject();
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> initializeBotModuleObject -> onInizialized -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeBotModuleObject -> onInizialized -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
case TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result: {
switch (err.message) {
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeBotModuleObject -> onInizialized > Unhandled custom response received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeBotModuleObject -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeBotModuleObject -> onInizialized -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeBotModuleObject -> onInizialized -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> initializeBotModuleObject -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function startBotModuleObject() {
try {
botModuleObject.start(onFinished);
function onFinished(err) {
try {
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> startBotModuleObject -> onFinished -> Execution finished well.");
finishProcessExecutionEvents()
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> startBotModuleObject -> onFinished -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startBotModuleObject -> onFinished -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
case TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result: {
switch (err.message) {
case "Dependency does not exist.": {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> startBotModuleObject -> onFinished -> Dependency does not exist. This Loop will go to sleep.");
nextWaitTime = 'Sleep';
loopControl(nextWaitTime);
return;
}
case "Dependency not ready.": {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> startBotModuleObject -> onFinished -> Dependency not ready. Will Retry Later.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startBotModuleObject -> onFinished -> Unhandled custom response received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startBotModuleObject -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startBotModuleObject -> onFinished -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startBotModuleObject -> onFinished -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startBotModuleObject -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function intitializeProcessFramework() {
try {
processFramework.initialize(statusDependencies, dataDependencies, onInizialized);
function onInizialized(err) {
try {
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> intitializeProcessFramework -> onInizialized -> Execution finished well.");
startProcessFramework();
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> intitializeProcessFramework -> onInizialized -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
case TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result: {
switch (err.message) {
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized > Unhandled custom response received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> intitializeProcessFramework -> onInizialized -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> intitializeProcessFramework -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function startProcessFramework() {
try {
processFramework.start(onFinished);
function onFinished(err) {
try {
processFramework.finalize()
processFramework = undefined
dataDependencies.finalize()
dataDependencies = undefined
statusDependencies.finalize()
statusDependencies = undefined
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well.");
finishProcessExecutionEvents()
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> startProcessFramework -> onFinished -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessFramework -> onFinished -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
case TS.projects.superalgos.globals.standardResponses.CUSTOM_OK_RESPONSE.result: {
switch (err.message) {
case "Dependency does not exist.": {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency does not exist. This Loop will go to sleep.");
nextWaitTime = 'Sleep';
loopControl(nextWaitTime);
return;
}
case "Dependency not ready.": {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> startProcessFramework -> onFinished -> Dependency not ready. This Loop will go to sleep.");
nextWaitTime = 'Sleep';
loopControl(nextWaitTime);
return;
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled custom response received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessFramework -> onFinished -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessFramework -> onFinished -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> startProcessFramework -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function finishProcessExecutionEvents() {
try {
processExecutionEvents.finish(onFinished);
function onFinished(err) {
try {
processExecutionEvents.finalize()
processExecutionEvents = undefined
switch (err.result) {
case TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> finishProcessExecutionEvents -> onFinished -> Execution finished well.");
nextWaitTime = 'Normal';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[WARN] run -> loop -> finishProcessExecutionEvents -> onFinished -> Retry Later. Requesting Execution Retry.");
nextWaitTime = 'Retry';
loopControl(nextWaitTime);
return;
}
case TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle.
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Operation Failed. Aborting the process.");
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
default: {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err.result = " + err.result);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> Unhandled err.result received. -> err = " + err.message);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> finishProcessExecutionEvents -> onFinished -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> finishProcessExecutionEvents -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function loopControl(nextWaitTime) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> loopControl -> nextWaitTime = " + nextWaitTime)
/* We show we reached the end of the loop. */
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex)
/* Here we check if we must stop the loop gracefully. */
shallWeStop(onStop, onContinue);
function onStop() {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> loopControl -> onStop -> Stopping the Loop Gracefully. See you next time!")
processStopped()
return;
}
function onContinue() {
/* Indicator bots are going to be executed after a configured period of time after the last execution ended. This is to avoid overlapping executions. */
switch (nextWaitTime) {
case 'Normal': {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> loopControl -> Restarting Loop in " +
(TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.normalWaitTime / 1000) + " seconds.")
nextLoopTimeoutHandle = setTimeout(loop, TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.normalWaitTime);
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, undefined, undefined, "Waiting " +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.normalWaitTime / 1000 + " seconds for next execution.")
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.persist();
}
break;
case 'Retry': {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> loopControl -> Restarting Loop in " +
(TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.retryWaitTime / 1000) + " seconds.")
nextLoopTimeoutHandle = setTimeout(loop, TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.retryWaitTime);
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, undefined, undefined, "Trying to recover from some problem. Waiting " +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.retryWaitTime / 1000 + " seconds for next execution.")
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.persist();
}
break;
case 'Sleep': {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> loopControl -> Restarting Loop in " +
(TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.sleepWaitTime / 60000) + " minutes.")
nextLoopTimeoutHandle = setTimeout(loop, TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.sleepWaitTime);
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, undefined, undefined, "Waiting " +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.sleepWaitTime / 60000 + " minutes for next execution.")
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.persist();
}
break;
case 'Coma': {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] run -> loop -> loopControl -> Restarting Loop in " +
(TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.comaWaitTime / 3600000) + " hours.")
nextLoopTimeoutHandle = setTimeout(loop, TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.comaWaitTime);
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, undefined, undefined, "Waiting " +
TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.processes[processIndex].referenceParent.config.comaWaitTime / 3600000 + " hours for next execution.")
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.persist();
}
break;
}
}
}
function shallWeStop(stopCallBack, continueCallBack) {
try {
/* IMPORTANT: This function is exactly the same on the 3 modules. */
if (!TS.projects.superalgos.globals.taskVariables.IS_TASK_STOPPING) {
continueCallBack();
} else {
stopCallBack();
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> shallWeStop -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_INSTANCE_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> loop -> err = " + err.stack);
TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE = err.message
processStopped()
return
}
}
function processStopped() {
if (TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE !== undefined) {
TS.projects.superalgos.functionLibraries.processFunctions.processError(
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_KEY, undefined, "An unexpected error caused the Process to stop.")
} else {
TS.projects.superalgos.globals.taskConstants.EVENT_SERVER_CLIENT_MODULE_OBJECT.raiseEvent(
TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_KEY, 'Stopped')
}
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.persist();
clearTimeout(nextLoopTimeoutHandle);
if (TS.projects.superalgos.globals.taskVariables.FATAL_ERROR_MESSAGE !== undefined) {
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE)
} else {
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE)
}
}
} catch (err) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).PROCESS_INSTANCE_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] run -> err = " + err.stack);
clearTimeout(nextLoopTimeoutHandle);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -1,9 +1,7 @@

exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_STORAGE, STATUS_REPORT, EXCHANGE_API) {
exports.newSuperalgosBotModulesHistoricOHLCVs = function (processIndex) {
const FULL_LOG = true;
const GMT_SECONDS = ':00.000 GMT+0000';
const MODULE_NAME = "User Bot";
const MODULE_NAME = "Historic OHLCVs";
const CANDLES_FOLDER_NAME = "Candles/One-Min";
const VOLUMES_FOLDER_NAME = "Volumes/One-Min";
@@ -12,15 +10,11 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger)
let fileStorage = FILE_STORAGE.newFileStorage(logger);
let fileStorage = TS.projects.superalgos.taskModules.fileStorage.newFileStorage(processIndex);
let statusDependencies
const ONE_MIN = 60000
const ONE_DAY = ONE_MIN * 60 * 24
const MAX_OHLCVs_PER_EXECUTION = 10000000
const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset
const symbol = TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.baseAsset.referenceParent.config.codeName + '/' + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.quotedAsset.referenceParent.config.codeName
const ccxt = require('ccxt')
let fetchType = "by Time"
@@ -36,7 +30,7 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
let options = {}
let rateLimit = 500
let exchange
let uiStartDate = new Date(bot.uiStartDate)
let uiStartDate = new Date(TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.config.startDate)
let fisrtTimeThisProcessRun = false
let limit = 1000 // This is the default value
let hostname
@@ -48,52 +42,44 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
let exchangeClass
try {
logger.fileName = MODULE_NAME;
logger.initialize();
statusDependencies = pStatusDependencies;
exchangeId = bot.exchange.toLowerCase().replace(' ', '')
exchangeId = TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name.toLowerCase().replace(' ', '')
/* Applying the parameters defined by the user at the Exchange Node Config */
if (bot.exchangeNode.config.API !== undefined) {
for (let i = 0; i < bot.exchangeNode.config.API.length; i++) {
if (bot.exchangeNode.config.API[i].method === 'fetch_ohlcv') {
if (bot.exchangeNode.config.API[i].class !== undefined) {
exchangeId = bot.exchangeNode.config.API[i].class
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API !== undefined) {
for (let i = 0; i < TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API.length; i++) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].method === 'fetch_ohlcv') {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].class !== undefined) {
exchangeId = TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].class
}
if (bot.exchangeNode.config.API[i].fetchOHLCVsMethod !== undefined) {
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].fetchOHLCVsMethod !== undefined) {
options = {
'fetchOHLCVsMethod': bot.exchangeNode.config.API[i].fetchOHLCVsMethod
'fetchOHLCVsMethod': TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].fetchOHLCVsMethod
}
}
if (bot.exchangeNode.config.API[i].firstId !== undefined) {
firstId = bot.exchangeNode.config.API[i].firstId
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].firstId !== undefined) {
firstId = TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].firstId
}
if (bot.exchangeNode.config.API[i].rateLimit !== undefined) {
rateLimit = bot.exchangeNode.config.API[i].rateLimit
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].rateLimit !== undefined) {
rateLimit = TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].rateLimit
}
if (bot.exchangeNode.config.API[i].hostname !== undefined) {
hostname = bot.exchangeNode.config.API[i].hostname
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].hostname !== undefined) {
hostname = TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].hostname
}
if (bot.exchangeNode.config.API[i].fetchType !== undefined) {
fetchType = bot.exchangeNode.config.API[i].fetchType
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].fetchType !== undefined) {
fetchType = TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.config.API[i].fetchType
}
}
}
}
if (bot.config.fetchLimit !== undefined) {
limit = bot.config.fetchLimit
if (TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.config.fetchLimit !== undefined) {
limit = TS.projects.superalgos.globals.taskConstants.TASK_NODE.bot.config.fetchLimit
}
let key = process.env.KEY
let secret = process.env.SECRET
if (key === "undefined") { key = undefined }
if (secret === "undefined") { secret = undefined }
let key
let secret
exchangeClass = ccxt[exchangeId]
const exchangeConstructorParams = {
@@ -113,10 +99,10 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
exchange = new exchangeClass(exchangeConstructorParams)
callBackFunction(global.DEFAULT_OK_RESPONSE);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
/* CCXT Supported Exchanges */
console.log('CCXT Library current supported exchanges:')
@@ -127,15 +113,15 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
console.log('Exchange Class ' + exchangeId)
console.log(exchangeClass)
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
function start(callBackFunction) {
try {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
if (TS.projects.superalgos.globals.taskVariables.IS_TASK_STOPPING === true) {
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
return
}
@@ -149,8 +135,8 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
await getFirstId()
await getOHLCVs()
if (abort === true) { return }
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
if (TS.projects.superalgos.globals.taskVariables.IS_TASK_STOPPING === true) {
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
return
}
await saveOHLCVs()
@@ -162,19 +148,21 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
let reportKey;
reportKey = "Masters" + "-" + "Exchange-Raw-Data" + "-" + "Historic-OHLCVs"
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getContextVariables -> reportKey = " + reportKey); }
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getContextVariables -> reportKey = " + reportKey)
if (statusDependencies.statusReports.get(reportKey).status === "Status Report is corrupt.") {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(global.DEFAULT_RETRY_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> Can not continue because dependecy Status Report is corrupt. ");
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
return;
}
thisReport = statusDependencies.statusReports.get(reportKey)
if (thisReport.file.beginingOfMarket !== undefined) { // This means this is not the first time this process run.
beginingOfMarket = new Date(thisReport.file.beginingOfMarket.year + "-" + thisReport.file.beginingOfMarket.month + "-" + thisReport.file.beginingOfMarket.days + " " + thisReport.file.beginingOfMarket.hours + ":" + thisReport.file.beginingOfMarket.minutes + GMT_SECONDS);
lastFile = new Date(thisReport.file.lastFile.year + "-" + thisReport.file.lastFile.month + "-" + thisReport.file.lastFile.days + " " + thisReport.file.lastFile.hours + ":" + thisReport.file.lastFile.minutes + GMT_SECONDS);
beginingOfMarket = new Date(thisReport.file.beginingOfMarket.year + "-" + thisReport.file.beginingOfMarket.month + "-" + thisReport.file.beginingOfMarket.days + " " + thisReport.file.beginingOfMarket.hours + ":" + thisReport.file.beginingOfMarket.minutes + TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
lastFile = new Date(thisReport.file.lastFile.year + "-" + thisReport.file.lastFile.month + "-" + thisReport.file.lastFile.days + " " + thisReport.file.lastFile.hours + ":" + thisReport.file.lastFile.minutes + TS.projects.superalgos.globals.timeConstants.GMT_SECONDS);
lastId = thisReport.file.lastId
lastCandleOfTheDay = thisReport.file.lastCandleOfTheDay
} else { // This means this is the first time this process run.
@@ -207,12 +195,15 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> getContextVariables -> err = " + err.stack);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getContextVariables -> err = " + err.stack);
if (err.message === "Cannot read property 'file' of undefined") {
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Check the bot Status Dependencies. ");
logger.write(MODULE_NAME, "[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Check the bot Status Dependencies. ");
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[HINT] start -> getContextVariables -> Dependencies loaded -> keys = " + JSON.stringify(statusDependencies.keys));
}
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
@@ -258,15 +249,16 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
/* Reporting we are doing well */
function heartBeat(noNewInternalLoop) {
let processingDate = new Date(since)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> Fetching OHLCVs @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit) }
let heartBeatText = "Fetching " + allOHLCVs.length.toFixed(0) + " OHLCVs from " + bot.exchange + " " + symbol + " @ " + processingDate
processingDate = processingDate.getUTCFullYear() + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processingDate.getUTCMonth() + 1, 2) + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processingDate.getUTCDate(), 2);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getOHLCVs -> Fetching OHLCVs @ " + processingDate + "-> exchange = " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit)
let heartBeatText = "Fetching " + allOHLCVs.length.toFixed(0) + " OHLCVs from " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + " " + symbol + " @ " + processingDate
let currentDate = new Date(since)
let percentage = global.getPercentage(fromDate, currentDate, lastDate)
bot.processHeartBeat(heartBeatText, percentage) // tell the world we are alive and doing well
if (global.areEqualDates(currentDate, new Date()) === false) {
let percentage = TS.projects.superalgos.utilities.dateTimeFunctions.getPercentage(fromDate, currentDate, lastDate)
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, heartBeatText, percentage) // tell the world we are alive and doing well
if (TS.projects.superalgos.utilities.dateTimeFunctions.areTheseDatesEqual(currentDate, new Date()) === false) {
if (noNewInternalLoop !== true) {
logger.newInternalLoop(bot.codeName, bot.process, currentDate, percentage);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.newInternalLoop(currentDate, percentage);
}
}
}
@@ -304,18 +296,21 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
]
*/
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> OHLCVs Fetched = " + OHLCVs.length) }
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getOHLCVs -> OHLCVs Fetched = " + OHLCVs.length)
if (OHLCVs.length > 0) {
let beginDate = new Date(OHLCVs[0].timestamp)
let endDate = new Date(OHLCVs[OHLCVs.length - 1].timestamp)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> OHLCVs Fetched From " + beginDate + " -> timestamp = " + OHLCVs[0].timestamp) }
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getOHLCVs -> OHLCVs Fetched to " + endDate + " -> timestamp = " + OHLCVs[OHLCVs.length - 1].timestamp) }
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getOHLCVs -> OHLCVs Fetched From " + beginDate + " -> timestamp = " + OHLCVs[0].timestamp)
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> getOHLCVs -> OHLCVs Fetched to " + endDate + " -> timestamp = " + OHLCVs[OHLCVs.length - 1].timestamp)
if (fisrtTimeThisProcessRun === true) {
let OHLCV = OHLCVs[0]
initialProcessTimestamp = OHLCV[0] // 'timestamp'
beginingOfMarket = new Date(Math.trunc(OHLCV[0] / ONE_DAY) * ONE_DAY) // 'timestamp'
beginingOfMarket = new Date(Math.trunc(OHLCV[0] / TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS) * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS) // 'timestamp'
fromDate = new Date(beginingOfMarket.valueOf())
fisrtTimeThisProcessRun = false
}
@@ -347,7 +342,7 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
if (
OHLCVs.length < limit - 1 ||
global.STOP_TASK_GRACEFULLY === true ||
TS.projects.superalgos.globals.taskVariables.IS_TASK_STOPPING === true ||
allOHLCVs.length >= MAX_OHLCVs_PER_EXECUTION
) {
break
@@ -355,22 +350,26 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
}
} catch (err) {
if (err.stack.toString().indexOf('ERR_RATE_LIMIT') >= 0) {
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + bot.exchange + " is saying you are requesting data too often. I will retry the request later, no action is required. To avoid this happening again please increase the rateLimit at the Exchange node config. You might continue seeing this if you are retrieving data from multiple markets at the same time. In this case I tried to get 1 min OHLCVs from " + symbol);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + " is saying you are requesting data too often. I will retry the request later, no action is required. To avoid this happening again please increase the rateLimit at the Exchange node config. You might continue seeing this if you are retrieving data from multiple markets at the same time. In this case I tried to get 1 min OHLCVs from " + symbol);
return
}
if (err.stack.toString().indexOf('RequestTimeout') >= 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.");
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + " 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
}
if (err.stack.toString().indexOf('ExchangeNotAvailable') >= 0) {
logger.write(MODULE_NAME, "[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + bot.exchange + " is not available at the moment. I will save the data already fetched and try to reconnect later to fetch the rest of the missing data.");
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getOHLCVs -> Retrying Later -> The Exchange " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + " is not available 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);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> getOHLCVs -> Retrying Later -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_RETRY_RESPONSE);
abort = true
return
}
@@ -382,8 +381,8 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
let candlesFileContent = '['
let volumesFileContent = '['
let previousDay = Math.trunc((initialProcessTimestamp - ONE_DAY) / ONE_DAY)
let currentDay = Math.trunc((initialProcessTimestamp - ONE_DAY) / ONE_DAY)
let previousDay = Math.trunc((initialProcessTimestamp - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS) / TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS)
let currentDay = Math.trunc((initialProcessTimestamp - TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS) / TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS)
let needSeparator = false
let error
let separator
@@ -424,37 +423,38 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
for (let j = 0; j < 60 * 24; j++) {
let candle = {
begin: currentDay * ONE_DAY + ONE_MIN * j,
end: currentDay * ONE_DAY + ONE_MIN * j + ONE_MIN - 1,
begin: currentDay * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS + TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS * j,
end: currentDay * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS + TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS * j + TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS - 1,
open: lastCandle.close,
close: lastCandle.close,
min: lastCandle.close,
max: lastCandle.close
}
let volume = {
begin: currentDay * ONE_DAY + ONE_MIN * j,
end: currentDay * ONE_DAY + ONE_MIN * j + ONE_MIN - 1,
begin: currentDay * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS + TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS * j,
end: currentDay * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS + TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS * j + TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS - 1,
buy: lastVolume.buy,
sell: lastVolume.sell
}
let processingDate = new Date(candle.begin)
processingDate = processingDate.getUTCFullYear() + '-' + utilities.pad(processingDate.getUTCMonth() + 1, 2) + '-' + utilities.pad(processingDate.getUTCDate(), 2);
processingDate = processingDate.getUTCFullYear() + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processingDate.getUTCMonth() + 1, 2) + '-' + TS.projects.superalgos.utilities.miscellaneousFunctions.pad(processingDate.getUTCDate(), 2);
if (candle.begin > (new Date()).valueOf()) {
/* We stop when the current candle is pointing to a time in the future.*/
headOfTheMarketReached = true
saveFile(currentDay)
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> Before Fetch -> Saving OHLCVs @ " + processingDate + " -> i = " + i + " -> total = " + allOHLCVs.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " / " + allOHLCVs.length + " OHLCVs from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> saveOHLCVs -> Before Fetch -> Saving OHLCVs @ " + processingDate + " -> i = " + i + " -> total = " + allOHLCVs.length)
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, "Saving " + i.toFixed(0) + " / " + allOHLCVs.length + " OHLCVs from " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
/* We exit the loop and we aint comming back*/
return
}
let OHLCV = { // these values should be overrided unless there are no OHLVCs fetched from the exchange.
timestamp: (new Date()).valueOf() + ONE_MIN,
timestamp: (new Date()).valueOf() + TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS,
open: 0,
hight: 0,
low: 0,
@@ -475,7 +475,7 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
}
}
let candleMinute = Math.trunc(candle.begin / ONE_MIN)
let candleMinute = Math.trunc(candle.begin / TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS)
let OHLCVMinute
checkOHLCVMinute()
@@ -486,7 +486,7 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
UTC minute. It is also not guaranteed that the distance between timestamps will be the same. To fix this, we will do this.
*/
OHLCVMinute = Math.trunc(OHLCV.timestamp / ONE_MIN)
OHLCVMinute = Math.trunc(OHLCV.timestamp / TS.projects.superalgos.globals.timeConstants.ONE_MIN_IN_MILISECONDS)
if (OHLCVMinute < candleMinute) {
if (i >= allOHLCVs.length - 1) {
@@ -513,8 +513,9 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
heartBeatCounter--
if (heartBeatCounter <= 0) {
heartBeatCounter = 1440
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> After Fetch -> Saving OHLCVs @ " + processingDate + " -> i = " + i + " -> total = " + allOHLCVs.length) }
bot.processHeartBeat("Saving " + i.toFixed(0) + " / " + allOHLCVs.length + " OHLCVs from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> saveOHLCVs -> After Fetch -> Saving OHLCVs @ " + processingDate + " -> i = " + i + " -> total = " + allOHLCVs.length)
TS.projects.superalgos.functionLibraries.processFunctions.processHeartBeat(processIndex, "Saving " + i.toFixed(0) + " / " + allOHLCVs.length + " OHLCVs from " + TS.projects.superalgos.globals.taskConstants.TASK_NODE.parentNode.parentNode.parentNode.referenceParent.parentNode.parentNode.name + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well
}
/* End Reporting */
@@ -568,10 +569,10 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
let fileName = 'Data.json'
filesToCreate++
fileStorage.createTextFile(getFilePath(day * ONE_DAY, CANDLES_FOLDER_NAME) + '/' + fileName, candlesFileContent + '\n', onFileCreated);
fileStorage.createTextFile(getFilePath(day * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS, CANDLES_FOLDER_NAME) + '/' + fileName, candlesFileContent + '\n', onFileCreated);
filesToCreate++
fileStorage.createTextFile(getFilePath(day * ONE_DAY, VOLUMES_FOLDER_NAME) + '/' + fileName, volumesFileContent + '\n', onFileCreated);
fileStorage.createTextFile(getFilePath(day * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS, VOLUMES_FOLDER_NAME) + '/' + fileName, volumesFileContent + '\n', onFileCreated);
candlesFileContent = '['
volumesFileContent = '['
@@ -580,13 +581,14 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
}
function onFileCreated(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> OHLCVsReadyToBeSaved -> onFileBCreated -> err = " + JSON.stringify(err));
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> OHLCVsReadyToBeSaved -> onFileBCreated -> err = " + JSON.stringify(err));
error = err // This allows the loop to be breaked.
return;
}
filesCreated++
lastFile = new Date((currentDay * ONE_DAY))
lastFile = new Date((currentDay * TS.projects.superalgos.globals.timeConstants.ONE_DAY_IN_MILISECONDS))
if (filesCreated === filesToCreate) {
controlLoop()
}
@@ -595,16 +597,16 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
function getFilePath(timestamp, folderName) {
let datetime = new Date(timestamp)
let dateForPath = datetime.getUTCFullYear() + '/' +
utilities.pad(datetime.getUTCMonth() + 1, 2) + '/' +
utilities.pad(datetime.getUTCDate(), 2)
let filePath = bot.filePathRoot + "/Output/" + folderName + '/' + dateForPath;
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(datetime.getUTCMonth() + 1, 2) + '/' +
TS.projects.superalgos.utilities.miscellaneousFunctions.pad(datetime.getUTCDate(), 2)
let filePath = TS.projects.superalgos.globals.processVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).FILE_PATH_ROOT + "/Output/" + folderName + '/' + dateForPath;
return filePath
}
}
function controlLoop() {
if (global.STOP_TASK_GRACEFULLY === true) {
callBackFunction(global.DEFAULT_OK_RESPONSE);
if (TS.projects.superalgos.globals.taskVariables.IS_TASK_STOPPING === true) {
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
return
}
@@ -622,7 +624,8 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
if (i >= allOHLCVs.length - 1) {
writeStatusReport()
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> controlLoop -> Exit because i reached the end of the candles array. ") }
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> saveOHLCVs -> controlLoop -> Exit because i reached the end of the candles array. ")
return
}
@@ -631,14 +634,16 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
if (headOfTheMarketReached === false) {
setImmediate(loop)
} else {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> saveOHLCVs -> controlLoop -> Exit because we reached the end of the market. ") }
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[INFO] start -> saveOHLCVs -> controlLoop -> Exit because we reached the end of the market. ")
writeStatusReport()
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> saveOHLCVs -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> saveOHLCVs -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
abort = true
}
}
@@ -672,23 +677,26 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, FILE_S
thisReport.save(onSaved);
function onSaved(err) {
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack);
if (err.result !== TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE.result) {
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeStatusReport -> onSaved -> err = " + err.stack);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_OK_RESPONSE);
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> writeStatusReport -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
"[ERROR] start -> err = " + err.stack);
callBackFunction(TS.projects.superalgos.globals.standardResponses.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -0,0 +1,6 @@
.git
.dockerignore
Dockerfile
node_modules
*.swp
*.pyc
@@ -0,0 +1,4 @@
*.* text eol=lf
php/test/*.php linguist-generated=true
python/ccxt/*.py linguist-generated=true
python/ccxt/__init__.py linguist-generated=false
@@ -0,0 +1,926 @@
# Contributing To The CCXT Library
- [How To Submit A Question Or Issue](#how-to-submit-an-issue)
- [How To Contribute Code](#how-to-contribute-code)
- [What You Need To Have](#what-you-need-to-have)
- [What You Need To Know](#what-you-need-to-know)
## How To Submit An Issue
If you want to submit an issue and you want your issue to be resolved quickly, here's a checklist for you:
- Read the [Manual](https://github.com/ccxt/ccxt/wiki/Manual), and especially carefully read the following sections:
- [Exchange Properties](https://github.com/ccxt/ccxt/wiki/Manual#exchange-properties)
- [Rate Limit](https://github.com/ccxt/ccxt/wiki/Manual#rate-limit)
- [DDoS Protection](https://github.com/ccxt/ccxt/wiki/Manual#ddos-protection-by-cloudflare--incapsula)
- [Authentication](https://github.com/ccxt/ccxt/wiki/Manual#authentication)
- [API Keys Setup](https://github.com/ccxt/ccxt/wiki/Manual#api-keys-setup)
- Read the [Troubleshooting](https://github.com/ccxt/ccxt/wiki/Manual#troubleshooting) section and follow troubleshooting steps.
- Read the [FAQ](https://github.com/ccxt/ccxt/wiki/FAQ) for most frequently asked questions.
- Read the [API docs](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) for your exchange.
- Search for similar issues first to avoid duplicates.
- If your issue is unique, along with a basic description of the failure, the following **IS REQUIRED**:
- **set `exchange.verbose = true` property on the exchange instance before calling its functions or methods**
- **DON'T POST SCREENSHOTS OF CODE OR ERRORS, POST THE OUTPUT AND CODE IN PLAIN TEXT!**
- **surround code and output with triple backticks: &#096;&#096;&#096;GOOD&#096;&#096;&#096;**
- don't confuse the backtick symbol (&#096;) with the quote symbol (\'): '''BAD'''
- don't confuse a single backtick with triple backticks: &#096;BAD&#096;
- paste a complete code snippet you're having difficulties with, avoid one-liners
- paste the **full verbose output** of the failing method without your keys
- the verbose output should include the request and response from the exchange (not just an error callstack)
- write your language **and version**
- write ccxt library version
- which exchange it is
- which method you're trying to call
### Reporting Vulnerabilities And Critical Issues
If you found a security issue or a critical vulnerability and reporting it in public would impose risk – please feel free to send us a message to <a href="mailto:info@ccxt.trade">info@ccxt.trade</a>.
## How To Contribute Code
- **[MAKE SURE YOUR CODE IS UNIFIED](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#derived-exchange-classes)!**
↑ This is the most important rule of all.
- **PLEASE, DO NOT COMMIT THE FOLLOWING FILES IN PULL REQUESTS:**
- `/doc/*` (these files are generated from `/wiki/*`, place your edits there)
- `/build/*` (these are generated automatically)
- `/php/*` (except for base classes)
- `/python/*` (except for base classes)
- `/ccxt.js`
- `/README.md` (exchange lists are generated automatically)
- `/package.json`
- `/package.lock`
- `/wiki/*` (except for real edits, exchange lists are generated automatically)
- `/dist/ccxt.browser.js` (this is also browserified automatically)
These files are generated ([explained below](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#multilanguage-support)) and will be overwritten upon build. Please don't commit them to avoid bloating the repository which is already quite large. Most often, you have to commit just one single source file to submit an edit to the implementation of an exchange.
- **PLEASE, SUBMIT ATOMIC EDITS, ONE PULL REQUEST PER ONE EXCHANGE, DO NOT MIX THEM**
- **MAKE SURE YOUR CODE PASSES ALL SYNTAX CHECKS BY RUNNING `npm run build`**
## Pending Tasks
Below is a list of functionality we would like to have implemented and fully **unified** in the library in the first place at this time. Most of these tasks are already in progress, implemented for some exchanges, but not all of them:
- Margin trading
- Leverage
- Derivatives (futures, options)
- Main account / subaccounts
- Conditional orders (stop loss, take profit)
- `transfer` between subaccounts and main account
- `fetchTransfer`
- `fetchTransfers`
- `fetchLedger`
- `fetchPositions`
- `closePosition`
- `closePositions`
If you want to contribute by submitting partial implementations be sure to look up examples of how it's done inside the library (where implemented already) and copy the adopted practices.
If your proposal, suggestion or improvement does not relate to the above list of tasks before submitting it make sure it is:
1. really needed by the majority of ccxt users
2. designed to be a general-purpose solution, not hardcoded for your specific needs
3. done in a generalized way compatible with all exchanges (not exchange-specific)
4. portable (available in all supported languages)
5. robust
6. explicit in what it's doing
7. doesn't break anything (if you change a method, make sure that all other methods calling the edited method are not broken)
The following is a set of rules for contributing to the ccxt library codebase.
## What You Need To Have
If you're not going to develop CCXT and contribute code to the CCXT library, then you don't need the Docker image nor the CCXT repository. If you just want to use CCXT inside your project simply install it as a regular package into the project folder as documented in the Manual (https://github.com/ccxt/ccxt/wiki/Install):
- [JavaScript / Node.js / NPM](https://github.com/ccxt/ccxt/wiki/Install#javascript-npm)
```shell
# JavaScript / Node.js / NPM
npm install ccxt
```
- [Python / PIP](https://github.com/ccxt/ccxt/wiki/Install#python)
```shell
# Python
pip install ccxt # or pip3 install ccxt
```
- [PHP / Composer](https://github.com/ccxt/ccxt/wiki/Install#php)
```shell
# PHP / Composer
composer install ccxt
```
### With Docker
The easiest way is to use Docker to run an isolated build & test enviroment with all the dependencies installed:
```shell
docker-compose run --rm ccxt
```
That builds a container and opens a shell, where the `npm run build` and `node run-tests` commands should simply work out of the box.
The CCXT folder is mapped inside of the container, except the `node_modules` folder — the container would have its own ephemeral copy — so that won't mess up your locally installed modules. This means that you can edit sources on your host machine using your favorite editor and build/test them in the running container.
This way you can keep the build tools and processes isolated, not having to work through the painful process of installing all those dependencies to your host machine manually.
### Without Docker
#### Dependencies
- Git
- [Node.js](https://nodejs.org/en/download/) 8+
- [Python](https://www.python.org/downloads/) 3.5.3+
- requests (`pip install requests`)
- [aiohttp](https://docs.aiohttp.org/) (`pip install aiohttp`)
- [tox](https://tox.readthedocs.io)
- via pip: `pip install tox`
- MacOS with [brew](https://brew.sh): `brew install tox`
- Ubuntu Linux: `apt-get install tox`
- [PHP](https://secure.php.net/downloads.php) 5.3+ with the following extensions installed and enabled:
- cURL
- iconv
- mbstring
- PCRE
- bcmath (php<7.1)
#### Build Steps
```shell
git clone https://github.com/ccxt/ccxt.git
```
```shell
cd ccxt
```
```shell
npm install
```
```shell
npm run build
```
## What You Need To Know
### Repository Structure
The contents of the repository are structured as follows:
```shell
/ # root directory aka npm module/package folder for Node.js
/.babelrc # babel config used for making the ES5 version of the library
/.eslintrc # linter
/.gitattributes # contains linguist settings for language detection in repo
/.gitignore # ignore it
/.npmignore # files to exclude from the NPM package
/.travis.yml # a YAML config for travis-ci (continuous integration)
/CONTRIBUTING.md # this file
/LICENSE.txt # MIT
/README.md # master markdown for GitHub, npmjs.com, npms.io, yarn and others
/build/ # build scripts
/build/export-exchanges.js # used to create tables of exchanges in the docs during the build
/build/transpile.js # the transpilation script
/build/update-badges.js # a JS script to update badges in the README and in docs
/build/vss.js # reads single-sourced version from package.json and writes it everywhere
/dist/ # a folder for the generated browser bundle of CCXT
/ccxt.js # entry point for the master JS version of the ccxt library
/ccxt.php # entry point for the PHP version of the ccxt library
/doc/ # Sphinx-generated rst-docs for http://ccxt.readthedocs.io/
/js/ # the JS version of the library
/php/ # PHP ccxt module/package folder
/python/ # Python ccxt module/package folder for PyPI
/python/__init__.py # entry point for the Python version of the ccxt.library
/python/async_support/ # asynchronous version of the ccxt.library for Python 3.5.3+ asyncio
/python/base/ # base code for the Python version of the ccxt library
/python/MANIFEST.in # a PyPI-package file listing extra package files (license, configs, etc...)
/python/README.md # a copy of README.md for PyPI
/python/setup.cfg # wheels config file for the Python package
/python/setup.py # pip/setuptools script (build/install) for ccxt in Python
/python/tox.ini # tox config for Python
/examples/ # self-explanatory
/examples/js # ...
/examples/php # ...
/examples/py # ...
/exchanges.cfg # custom bundle config for including only the exchanges you need
/package.json # npm package file, also used in setup.py for version single-sourcing
/run-tests.js # a front-end to run individual tests of all exchanges in all languages (JS/PHP/Python)
/wiki/ # the source of all docs (edits go here)
```
### Multilanguage Support
The ccxt library is available in three different languages (more to come). We encourage developers to design *portable* code, so that a single-language user could read the code in other languages and understand it easily. This helps the adoption of the library. The main goal is to provide a generalized, unified, consistent and robust interface to as many existing cryptocurrency exchanges as possible.
At first, all language-specific versions were developed in parallel, but separately from each other. But when it became too hard to maintain and keep the code consistent among all supported languages we have decided to switch to what we call a *source/generated* process. There is now a single source version in one language, that is JavaScript. Other language-specific versions are syntactically derived (transpiled, generated) automatically from the source version. But it doesn't mean that you have to be a JS coder to contribute. The portability principle allows Python and PHP devs to effectively participate in developing the source version as well.
The module entry points are:
- `./python/__init__.py` for the Python pip package
- `./python/async/__init__.py` for the Python 3.5.3+ ccxt.async_support subpackage
- `./ccxt.js` for the Node.js npm package
- `./dist/ccxt.browser.js` for the browser bundle
- `./ccxt.php` for PHP
Generated versions and docs are transpiled from the source `ccxt.js` file and files in `./js/` by the `npm run build` command.
### Transpiled (generated) files
- All derived exchange classes are transpiled automatically from source JS files. The source files are language-agnostic, easily mapped line-to-line to any other language and written in a cross-language-compatible way. Any coder can read it (by design).
- All base classes are **not** transpiled, those are language-specific.
#### JavaScript
The `ccxt.browser.js` is generated with Babel from source.
#### Python
These files containing derived exchange classes are transpiled from JS into Python:
- `js/[_a-z].js` → `python/ccxt/async/[_a-z].py`
- `python/ccxt/async[_a-z].py` → `python/ccxt/[_a-z].py` (Python 3 asyncio → Python 2 sync transpilation stage)
- `python/test/test_async.py` → `python/test/test.py` (the sync test is generated from the async test)
These Python base classes and files are not transpiled:
- `python/ccxt/base/*`
- `python/ccxt/async/base/*`
#### PHP
These files containing derived exchange classes are transpiled from JS into PHP:
- `js/[_a-z].js` → `php/[_a-z].php`
These PHP base classes and files are not transpiled:
- `php/base/*`
#### Typescript
- `js/[_a-z].js` → `ccxt.d.ts`
### Base Class
```UNDER CONSTRUCTION```
### Derived Exchange Classes
Transpiler is regex-based and heavily relies on specific formatting rules. If you break them then the transpiler will either
fail to generate Python/PHP classes at all or will generate malformed Python/PHP syntax.
Below are key notes on how to keep the JS code transpileable.
Use the linter `npm run lint js/your-exchange-implementation.js` before you build. It will cover many (but not all) the issues,
so manual checking will still be required if transpilation fails.
If you see a `[TypeError] Cannot read property '1' of null` exception or any other transpilation error when you `npm run build`, check if your code satisfies the following rules:
- don't put empty lines inside your methods
- always use Python-style indentation, it is preserved as is for all languages
- indent with 4 spaces **exactly**, avoid tabs
- put an empty line between each of your methods
- avoid mixed comment styles, use double-slash `//` in JS for line comments
- avoid multi-line comments
If the transpiling process finishes successfully, but generates incorrect Python/PHP syntax, check for the following:
- every opening bracket like `(` or `{` should have a space before it!
- do not use language-specific code syntax sugar, even if you really want to
- unfold all maps and comprehensions to basic for-loops
- don't change the arguments of overridden inherited methods, keep them uniform across all exchanges
- do everything with base class methods only (for example, use `this.json ()` for converting objects to json).
- always put a semicolon `;` at the end of each statement, as in PHP/C-style
- all associative keys must be single-quoted strings everywhere, `array['good'], array.bad`
- variables should be declared with `const` or `let` keywords semantically (no `var`!), prefer `const` everywhere
And structurally:
- if you need another base method you will have to implement it in all three languages
- do not issue more than one HTTP request from a unified method
- try to reduce syntax to basic one-liner expressions
- multiple lines are ok, but you should avoid deep nesting with lots of brackets
- avoid changing the contents of the arguments and params passed by reference into function calls
- do not use conditional statements that are too complex (heavy if-bracketing)
- do not use heavy ternary conditionals
- avoid operators clutter (**don't do this**: `a && b || c ? d + 80 : e ** f`)
- do not use `.includes()`, use `.indexOf()` instead!
- never use `.toString()` on floats: `Number (0.00000001).toString () === '1e-8'`
- do not use closures, `a.map` or `a.filter (x => (x === 'foobar'))` is not acceptable in derived classes
- do not use the `in` operator to check if a value is in a non-associative array (list)
- don't add custom currency or symbol/pair conversions and formatting, copy from existing code instead
- **don't access non-existent keys, `array['key'] || {}` won't work in other languages!**
- keep it simple, don't do more than one statement in one line
#### Sending Market Ids
Most of exchanges' API endpoints will require an exchange-specific market symbol or trading pair or instrument to be specified in the request.
**We don't send unified symbols to exchanges directly!** They are not interchangeable! There is a significant difference between *exchange-specific market-ids* and *unified symbols*! This is explained in the Manual, here:
- https://github.com/ccxt/ccxt/wiki/Manual#markets
- https://github.com/ccxt/ccxt/wiki/Manual#symbols-and-market-ids
**NEVER DO THIS:**
```JavaScript
async fetchTicker (symbol, params = {}) {
const request = {
'pair': symbol, // very bad, sending unified symbols to the exchange directly
};
const response = await this.publicGetEndpoint (request);
// parse in a unified way...
}
```
**DO NOT DO THIS EITHER:**
```JavaScript
async fetchTicker (symbol, params = {}) {
const request = {
'symbol': symbol, // very bad, sending unified symbols to the exchange directly
};
const response = await this.publicGetEndpoint (request);
// parse in a unified way...
}
```
Instead of sending a unified CCXT symbol to the exchange, we **always** take the exchange-specific market-`id` that corresponds to that symbol. If it so happens that an exchange specific market-id is exactly the same as the CCXT unified symbol – that's a happy coincidence, but we never rely on that in the unified CCXT API.
To get the exchange-specific market-id by a unified CCXT symbol, use the following methods:
- `this.market (symbol)` – returns the entire unified market structure, containing the `id`, `baseId`, `quoteId`, and many other interesting things
- `this.marketId (symbol)` – returns just the exchange-specific `id` of a market by a unified symbol (if you don't need anything else)
**GOOD EXAMPLES:**
```JavaScript
async fetchTicker (symbol, params = {}) {
const market = this.market (symbol); // the entire market structure
const request = {
'pair': market['id'], // good, they may me equal, but often differ, it's ok
};
const response = await this.publicGetEndpoint (this.extend (request, params));
// parse in a unified way...
}
```
```JavaScript
async fetchTicker (symbol, params = {}) {
const marketId = this.marketId (symbol); // just the id
const request = {
'symbol': marketId, // good, they may me equal, but often differ, it's ok
};
const response = await this.publicGetEndpoint (this.extend (request, params));
// parse in a unified way...
}
```
#### Parsing Symbols
When sending requests to the exchange unified symbols have to be _"converted"_ to exchange-specific market-`id`s like shown above. The same is true on the other side – when receiving an exchange response it has an exchange-specific market-`id` inside it that has to be _"converted back"_ to a unified CCXT symbol.
**We don't put exchange-specific market-`id`s in unified structures directly!** We can't freely interchange symbols with ids! There is a significant difference between an *exchange-specific market-ids* and *unified symbols*! This is explained in the Manual, here:
- https://github.com/ccxt/ccxt/wiki/Manual#markets
- https://github.com/ccxt/ccxt/wiki/Manual#symbols-and-market-ids
**NEVER DO THIS:**:
```JavaScript
parseTrade (trade, market = undefined) {
// parsing code...
return {
'info': trade,
'symbol': trade['pair'], // very bad, returning exchange-specific market-ids in a unified structure!
// other fields...
};
}
```
**DO NOT DO THIS EITHER**
```JavaScript
parseTrade (trade, market = undefined) {
// parsing code...
return {
'info': trade,
'symbol': trade['symbol'], // very bad, returning exchange-specific market-ids in a unified structure!
// other fields...
};
}
```
In order to handle the market-`id` properly it has to be looked-up in the info cached on this exchange with `loadMarkets()`:
**GOOD EXAMPLE:**
```JavaScript
parseTrade (trade, market = undefined) {
const marketId = this.safeString (trade, 'pair');
// safeSymbol is used to parse the market id to a unified symbol
const symbol = this.safeSymbol (marketId, market);
return {
'info': trade,
'symbol': symbol, // very good, a unified symbol here now
// other fields...
};
}
```
#### Accessing Dictionary Keys
In JavaScript, dictionary keys can be accessed in two notations:
- `object['key']` (single-quoted string key notation)
- `object.key` (property notation)
Both work almost identically, and one is implicitly converted to another upon executing the JavaScript code.
While the above does work in JavaScript, it will not work in Python or PHP. In most languages, associative dictionary keys are not treated in the same way as properties. Therefore, in Python `object.key` is not the same as `object['key']`. In PHP `$object->key` is not the same as `$object['key']` as well. Languages that differentiate between associative keys and properties use different notations for the two.
To keep the code transpileable, please, remember this simple rule: *always use the single-quoted string key notation `object['key']` for accessing all associative dictionary keys in all languages everywhere throughout this library!*
#### Sanitizing Input With `safe`-Methods
JavaScript is less restrictive than other languages. It will tolerate an attempt to dereference a non-existent key where other languages will throw an Exception:
```JavaScript
// JavaScript
const someObject = {}
if (someObject['nonExistentKey']) {
// the body of this conditional will not execute in JavaScript
// because someObject['nonExistentKey'] === undefined === false
// but JavaScript will not throw an exception on the if-clause
}
```
However, the above logic with *"an undefined value by default"* will not work in Python or PHP.
```Python
# Python
some_dictionary = {}
# breaks
if some_dictionary['nonExistentKey']:
# in Python the attempt to dereference the nonExistentKey value
# will throw a standard built-in KeyError exception
# works
if 'nonExistentKey' in some_dictionary and some_dictionary['nonExistentKey']:
# this is a way to check if the key exists before accessing the value
# also works
if some_dictionary.get('nonExistentKey'):
# another a way to check if the key exists before accessing the value...
```
Most languages will not tolerate an attempt to access a non-existent key in an object.
For the above reasons, please, **never do this** in the transpiled JS files:
```JavaScript
// JavaScript
const value = object['key'] || other_value; // will not work in Python or PHP!
if (object['key'] || other_value) { /* will not work in Python or PHP! */ }
```
Therefore we have a family of `safe*` functions:
- `safeInteger (object, key)`, `safeInteger2 (object, key1, key2)`
- `safeFloat (object, key)`, `safeFloat2 (object, key1, key2)`
- `safeString (object, key)`, `safeString2 (object, key1, key2)`
- `safeValue (object, key)`, `safeValue2 (object, key1, key2)`
The `safeValue` function is used for objects inside objects, arrays inside objects and boolean `true/false` values.
The above safe-functions will check for the existence of the key in the object and will properly return `undefined/None/null` values for JS/Python/PHP. Each function also accepts the default value to be returned instead of `undefined/None/null` in the last argument.
Alternatively, you could check for the key existence first...
So, you have to change this:
```JavaScript
if (params['foo'] !== undefined) {
}
```
↓
```JavaScript
const foo = this.safeValue (params, 'foo');
if (foo !== undefined) {
}
```
Or:
```JavaScript
if ('foo' in params) {
}
```
#### Using Base Class Cryptography Methods For Authentication
Do not reinvent the wheel. Always use base-class methods for cryptography.
The CCXT library supports the following authentication algorithms and cryptography algorithms:
- HMAC
- JWT (JSON Web Token)
- RSA
- ECDSA Elliptic Curve Cryptography
- NIST P256
- secp256k1
- OTP 2FA (one-time password 2-factor authentication)
The base `Exchange` class offers several methods that are key to practically all cryptography in this lib. Derived exchange implementations must not use external dependencies for cryptography, everything should be done with base methods only.
- `hash (message, hash = 'md5', digest = 'hex')`
- `hmac (message, secret, hash = 'sha256', digest = 'hex')`
- `jwt (message, secret, hash = 'HS256')`
- `rsa (message, secret, alg = 'RS256')`
- `ecdsa (request, secret, algorithm = 'p256', hash = undefined)`
- `totp (secret)`
- `stringToBase64()`, `base64ToBinary()`, `binaryToBase64()`...
The `hash()` method supports the following `hash` algorithms:
- `'md5'`
- `'sha1'`
- `'sha3'`
- `'sha256'`
- `'sha384'`
- `'sha512'`
- `'keccak'`
The `digest` encoding argument accepts the following values:
- `'hex'`
- `'binary'`
The `hmac()` method also supports `'base64'` for the `digest` argument. This is for `hmac()` only, other implementations should use `'binary'` with `binaryToBase64()`.
#### Timestamps
**All timestamps throughout all unified structures within this library are integer timestamp _in milliseconds_!**
In order to convert to milliseconds timestamps, CCXT implements the following methods:
```JavaScript
const data = {
'unixTimestampInSeconds': 1565242530,
'unixTimestampInMilliseconds': 1565242530165,
'stringInSeconds': '1565242530',
};
// convert to integer if the underlying value is already in milliseconds
const timestamp = this.safeInteger (data, 'unixTimestampInMilliseconds'); // === 1565242530165
// convert to integer and multiply by a thousand if the value is a UNIX timestamp in seconds
const timestamp = this.safeTimestamp (data, 'unixTimestampInSeconds'); // === 1565242530000
// convert to integer and multiply by a thousand if the value is in seconds
const timestamp = this.safeTimestamp (data, 'stringInSeconds'); // === 1565242530000
```
#### Working With Array Lengths
In JavaScript the common syntax to get a length of a string or an array is to reference the `.length` property like shown here:
```JavaScript
someArray.length
// or
someString.length
```
And it works for both strings and arrays. In Python this is done in a similar way:
```Python
len(some_array)
# or
len(some_string)
```
So the length is accessible in the same way for both strings and arrays and both work fine.
However, with PHP this is different, so the syntax for string lengths and array lengths is different:
```PHP
count(some_array);
// or
strlen(some_string); // or mb_strlen
```
Because the transpiler works line-by-line and does no code introspection, it cannot tell arrays from strings and cannot properly transpile `.length` to PHP without additional hinting. It will always transpile JS `.length` to PHP `strlen` and will prefer string lengths over array lengths. In order to indicate an array length properly we have to do the following:
```JavaScript
const arrayLength = someArray.length;
// the above line ends with .length;
// that ending is a hint for the transpiler that will recognize someArray
// as an array variable in this place, rather than a string type variable
// now we can use arrayLength for the arithmetic
```
That `.length;` line ending does the trick. The only case when the array `.length` is preferred over the string `.length` is the `for` loop. In the header of the `for` loop, the `.length` always refers to array length (not string length).
#### Adding Numbers And Concatenating Strings
In JS the arithmetic addition `+` operator handles both strings and numbers. So, it can concatenate strings with `+` and can sum up numbers with `+` as well. The same is true with Python. With PHP this is different, so it has different operators for string concatenation (the "dot" operator `.`) and for arithmetic addition (the "plus" operator `+`). Once again, because the transpiler does no code introspection it cannot tell if you're adding up numbers or strings in JS. This works fine until you want to transpile this to other languages, be it PHP or whatever other language it is. In order to help the transpiler we have to use `this.sum` for arithmetic additions.
The rule of thumb is: **`+` is for string concatenation only (!)** and **`this.sum (a, b, c, ...)` is for arithmetic additions**. The same logic applies to operator `+=` vs operator `.=` – `this.sum()` has to be used in those cases as well.
#### Formatting Decimal Numbers To Precision
The `.toFixed ()` method has [known rounding bugs](https://www.google.com/search?q=toFixed+bug) in JavaScript, and so do the other rounding methods in the other languages as well. In order to work with number formatting consistently, we need to use the [`decimalToPrecision` method as described in the Manual](https://github.com/ccxt/ccxt/wiki/Manual#methods-for-formatting-decimals).
#### Escaped Control Characters
When using strings containing control characters like `"\n"`, `"\t"`, always enclose them in double quotes (`"`), not single quotes (`'`)! Single-quoted strings are not parsed for control characters and are treated as is in many languages apart from JavaScript. Therefore for tabs and newlines to work in PHP, we need to surround them with double quotes (especially in the `sign()` implementation).
Bad:
```JavaScript
const a = 'GET' + method.toLowerCase () + '\n' + path;
const b = 'api\nfoobar.com\n';
```
Good:
```JavaScript
const a = 'GET' + method.toLowerCase () + "\n" + path; // eslint-disable-line quotes
// eslint-disable-next-line quotes
const b = "api\nfoobar.com\n";
```
**↑ The `eslint-*` comments are mandatory!**
#### Using Ternary Conditionals
Do not use heavy ternary (`?:`) conditionals, **always use brackets in ternary operators!**
Despite that there is operator precedence in the programming languages themselves, the transpiler is regex-based and it does no code introspection, therefore it treats everything as plaintext.
The brackets are needed to hint the transpiler which part of the conditional is which. In the absence of brackets it's hard to understand the line and the intent of the developer.
Here are some examples of a badly-designed code that will break the transpiler:
```JavaScript
// this is an example of bad codestyle that will likely break the transpiler
const foo = {
'bar': 'a' + qux === 'baz' ? this.a () : this.b () + 'b',
};
```
```JavaScript
// this confuses the transpiler and a human developer as well
const foo = 'bar' + baz + qux ? 'a' : '' + this.c ();
```
Adding surrounding brackets to corresponding parts would be a more or less correct way to resolve it.
```JavaScript
const foo = {
'bar': (qux === 'baz') ? this.a () : this.b (), // much better now
};
```
The universally-working way to solve it is to just break the complex line into a few simpler lines, even at a cost of adding extra lines and conditionals:
```JavaScript
// before:
// const foo = {
// 'bar': 'a' + qux === 'baz' ? this.a () : this.b () + 'b',
// };
// ----------------------------------------------------------------------------
// after:
const bar = (qux === 'baz') ? this.a () : this.b ();
const foo = {
'bar': 'a' + bar + 'b',
};
```
Or even:
```JavaScript
// before:
// const foo = 'bar' + baz + qux ? 'a' : '' + this.c ();
// ----------------------------------------------------------------------------
// after:
let foo = 'bar' + baz;
if (qux) {
foo += 'a';
};
foo += this.c ();
```
---
### New Exchange Integrations
**REMEMBER:** The key reason why this library is used at all is **Unification**. When developing a new exchange file the goal is not to implement it somehow, but to implement it in a very pedantic, precise and exact way, just as the other exchanges are implemented. For that we have to copy bits of logic from other exchanges and make sure that the new exchange conforms to the Manual in the following aspects:
- market ids, trading pair symbols, currency ids, token codes, symbolic unification and `commonCurrencies` must be standardized in all parsing methods (`fetchMarkets`, `fetchCurrencies`, `parseTrade`, `parseOrder`, ...)
- all unified API method names and arguments are standard – can't add or change them freely
- all parser input must be `safe`-sanitized as [described above](#sanitizing-input-with-safe-methods)
- for bulk operations the base methods should be used (`parseTrades`, `parseOrders`, note the `s` plural ending)
- use as much of base functionality as you can, do not reinvent the wheel, nor the bicycle, nor the bicycle wheel
- respect default argument values in `fetch`-methods, check if `since` and `limit` are `undefined` and do not send them to the exchange, we intentionally use the exchanges' defaults in such cases
- when implementing a unified method that has some arguments – we can't ignore or miss any of those arguments
- all structures returned from the unified methods must conform to their specifications from the Manual
- all API endpoints have to be listed out with proper support for params substituted in the URLs
Please, see the following document for new integrations: https://github.com/ccxt/ccxt/wiki/Requirements
A quick merge of a Pull Request for a new exchange integration depends on consistency and compliance with the above unified rules and standards. Breaking one of those is the key reason for not merging a Pull Request.
**If you want to add (support for) another exchange, or implement a new method for a particular exchange, then the best way to make it a consistent improvement is to learn from example. Take a look at how same things are implemented in other exchanges (we recommend certified exchanges) and try to copy the code flow and style.**
The basic JSON-skeleton for a new exchange integration is as follows:
```
{
'id': 'example',
'name': 'Example Exchange',
'country': [ 'US', 'EU', 'CN', 'RU' ],
'rateLimit': 1000,
'version': '1',
'comment': 'This comment is optional',
'urls': {
'logo': 'https://example.com/image.jpg',
'api': 'https://api.example.com/api',
'www': 'https://www.example.com',
'doc': [
'https://www.example.com/docs/api',
'https://www.example.com/docs/howto',
'https://github.com/example/docs',
],
},
'api': {
'public': {
'get': [
'endpoint/example',
'orderbook/{pair}/full',
'{pair}/ticker',
],
},
'private': {
'post': [
'balance',
],
},
},
}
```
### Implicit API Methods
In the code for each exchange, you'll notice that the functions that make API requests aren't explicitly defined. This is because the `api` definition in the exchange description JSON is used to create *magic functions* (aka *partial functions* or *closures*) inside the exchange subclass. That implicit injection is done by the `defineRestApi/define_rest_api` base exchange method.
Each partial function takes a dictionary of `params` and returns the API response. In the example JSON above, the `'endpoint/example'` results in the injection of a `this.publicGetEndpointExample` function. Similarly, the `'orderbook/{pair}/full'` results in a `this.publicGetOrderbookPairFull` function, that takes a ``pair`` parameter (again, passed in the `params` argument).
Upon instantiation the base exchange class takes each URL from its list of endpoints, splits it into words, and then makes up a callable function name from those words by using a partial construct. That process is the same in JS, Python and PHP as well. It is also described here:
- https://github.com/ccxt/ccxt/wiki/Manual#api-methods--endpoints
- https://github.com/ccxt/ccxt/wiki/Manual#implicit-api-methods
- https://github.com/ccxt-dev/ccxt/wiki/Manual#api-method-naming-conventions
```UNDER CONSTRUCTION```
### Continuous Integration
Builds are automated with [Travis CI](https://travis-ci.org/ccxt/ccxt). The build steps for Travis CI are described in the [`.travis.yml`](https://github.com/ccxt/ccxt/blob/master/.travis.yml) file.
Windows builds are automated with [Appveyor](https://ci.appveyor.com/project/ccxt/ccxt). The build steps for Appveyor are in the [`appveyor.yml`](https://github.com/ccxt/ccxt/blob/master/appveyor.yml) file.
Incoming pull requests are automatically validated by the CI service. You can watch the build process online here: [travis-ci.org/ccxt/ccxt/builds](https://travis-ci.org/ccxt/ccxt/builds).
### How To Build & Run Tests On Your Local Machine
Before building for the first time, install Node dependencies (skip this step if you're running our Docker image):
```
npm install
```
The command below will build everything and generate PHP/Python versions from source JS files:
```
npm run build
```
The following command will test the built generated files (for all exchanges, symbols and languages):
```
node run-tests
```
You can restrict tests to a specific language, a particular exchange or symbol:
```
node run-tests [--php] [--js] [--python] [--python3] [exchange] [symbol]
```
For example, the first of the following lines will only test the source JS version of the library (`ccxt.js`). It does not require an `npm run build` before running it (can be useful if you need to verify quickly whether your changes break the code or not):
```shell
node run-tests --js # test master ccxt.js, all exchanges
# other examples require the 'npm run build' to run
node run-tests --python # test Python 2 version, all exchanges
node run-tests --php bitfinex # test Bitfinex with PHP
node run-tests --python3 kraken # test Kraken with Python 3, requires 'npm run build'
```
## Committing Changes To The Repository
The build process generates many changes in the transpiled exchange files, e.g. for Python and PHP. **You should NOT commit them to GitHub, commit only the base (JS) file changes please**.
You can hide the changes in the generated files by running this command (after that, the generated files are no longer marked as changed):
```
npm run git-ignore-generated-files
```
Previously we had that command implemented as a final build step, but it caused problems with subsequent `git pull` and also branch selection commands (when a conflict occurred in those files that have been marked as ignored). So if you experience an issue with that, you can un-ignore those files by executing:
```
npm run git-unignore-generated-files
```
## Financial Contributions
We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/ccxt).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
```
ETH 0x26a3CB49578F07000575405a57888681249c35Fd (ETH only!)
BTC 33RmVRfhK2WZVQR1R83h2e9yXoqRNDvJva
BCH 1GN9p233TvNcNQFthCgfiHUnj5JRKEc2Ze
LTC LbT8mkAqQBphc4yxLXEDgYDfEax74et3bP
```
## Credits
### Contributors
Thank you to all the people who have already contributed to ccxt!
<a href="graphs/contributors"><img src="https://opencollective.com/ccxt/contributors.svg?width=890" /></a>
### Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/ccxt#backer)]
<a href="https://opencollective.com/ccxt#backers" target="_blank"><img src="https://opencollective.com/ccxt/backers.svg?width=890"></a>
### Supporters
Support this project by becoming a supporter. Your avatar will show up here with a link to your website.
[[Become a supporter](https://opencollective.com/ccxt#supporter)]
<a href="https://opencollective.com/ccxt/tiers/supporter/0/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/0/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/1/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/1/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/2/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/2/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/3/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/3/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/4/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/4/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/5/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/5/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/6/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/6/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/7/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/7/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/8/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/8/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/9/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/9/avatar.svg"></a>
### Sponsors
Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/ccxt#sponsor))
[[Become a sponsor](https://opencollective.com/ccxt#sponsor)]
<a href="https://opencollective.com/ccxt/tiers/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/9/avatar.svg"></a>
@@ -0,0 +1,21 @@
MIT License
Copyright © 2017 Igor Kroitor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,551 @@
# CCXT – CryptoCurrency eXchange Trading Library
[![Build Status](https://travis-ci.com/ccxt/ccxt.svg?branch=master)](https://travis-ci.com/ccxt/ccxt) [![npm](https://img.shields.io/npm/v/ccxt.svg)](https://npmjs.com/package/ccxt) [![PyPI](https://img.shields.io/pypi/v/ccxt.svg)](https://pypi.python.org/pypi/ccxt) [![NPM Downloads](https://img.shields.io/npm/dm/ccxt.svg)](https://www.npmjs.com/package/ccxt) [![Discord](https://img.shields.io/discord/690203284119617602?logo=discord&logoColor=white)](https://discord.gg/dhzSKYU) [![Supported Exchanges](https://img.shields.io/badge/exchanges-123-blue.svg)](https://github.com/ccxt/ccxt/wiki/Exchange-Markets) [![Open Collective](https://opencollective.com/ccxt/backers/badge.svg)](https://opencollective.com/ccxt)
[![Twitter Follow](https://img.shields.io/twitter/follow/ccxt_official.svg?style=social&label=CCXT)](https://twitter.com/ccxt_official)
A JavaScript / Python / PHP library for cryptocurrency trading and e-commerce with support for many bitcoin/ether/altcoin exchange markets and merchant APIs.
### [Install](#install) · [Usage](#usage) · [Manual](https://github.com/ccxt/ccxt/wiki) · [FAQ](https://github.com/ccxt/ccxt/wiki/FAQ) · [Examples](https://github.com/ccxt/ccxt/tree/master/examples) · [Contributing](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md) · [Social](#social)
The **CCXT** library is used to connect and trade with cryptocurrency exchanges and payment processing services worldwide. It provides quick access to market data for storage, analysis, visualization, indicator development, algorithmic trading, strategy backtesting, bot programming, and related software engineering.
It is intended to be used by **coders, developers, technically-skilled traders, data-scientists and financial analysts** for building trading algorithms.
Current feature list:
- support for many cryptocurrency exchanges — more coming soon
- fully implemented public and private APIs
- optional normalized data for cross-exchange analytics and arbitrage
- an out of the box unified API that is extremely easy to integrate
- works in Node 7.6+, Python 3, PHP 5.4+, and web browsers
## Sponsored Promotion
[![CCXT Pro – A JavaScript / Python / PHP cryptocurrency exchange trading WebSocket API for professionals](https://user-images.githubusercontent.com/1294454/93346896-22dfb080-f83d-11ea-957f-6b8ae008f62a.png)](https://ccxt.pro) **[A JavaScript / Python / PHP cryptocurrency exchange trading WebSocket API for professionals](https://ccxt.pro)**
## See Also
- <sub>[![Quadency](https://user-images.githubusercontent.com/1294454/69334382-a0618100-0c6b-11ea-9ba9-40aa97440cfa.png)](https://quadency.com?utm_source=ccxt)</sub>&nbsp; **[Quadency](https://quadency.com?utm_source=ccxt)**&nbsp;&mdash;&nbsp;trading automation terminal for leading exchanges with streaming APIs + Python IDE.
- <sub>[![TabTrader](https://user-images.githubusercontent.com/1294454/66755907-9c3e8880-eea1-11e9-846e-0bff349ceb87.png)](https://tab-trader.com/?utm_source=ccxt)</sub>&nbsp; **[TabTrader](https://tab-trader.com/?utm_source=ccxt)**&nbsp;&mdash;&nbsp;trading on all exchanges in one app. Avaliable on [Android](https://play.google.com/store/apps/details?id=com.tabtrader.android&referrer=utm_source%3Dccxt) and [iOS](https://itunes.apple.com/app/apple-store/id1095716562?mt=8).
## Certified Cryptocurrency Exchanges
| logo | id | name | ver | doc | certified | pro |
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------|:---:|:----------------------------------------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [![binance](https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg)](https://www.binance.com/?ref=10205187) | binance | [Binance](https://www.binance.com/?ref=10205187) | * | [API](https://binance-docs.github.io/apidocs/spot/en) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitfinex](https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg)](https://www.bitfinex.com/?refcode=P61eYxFL) | bitfinex | [Bitfinex](https://www.bitfinex.com/?refcode=P61eYxFL) | 1 | [API](https://docs.bitfinex.com/v1/docs) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bittrex](https://user-images.githubusercontent.com/51840849/87153921-edf53180-c2c0-11ea-96b9-f2a9a95a455b.jpg)](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | bittrex | [Bittrex](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | 3 | [API](https://bittrex.github.io/api/v3) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitvavo](https://user-images.githubusercontent.com/1294454/83165440-2f1cf200-a116-11ea-9046-a255d09fb2ed.jpg)](https://bitvavo.com/?a=24F34952F7) | bitvavo | [Bitvavo](https://bitvavo.com/?a=24F34952F7) | 2 | [API](https://docs.bitvavo.com/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bytetrade](https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg)](https://www.byte-trade.com) | bytetrade | [ByteTrade](https://www.byte-trade.com) | * | [API](https://docs.byte-trade.com/#description) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![currencycom](https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg)](https://currency.com/trading/signup?c=362jaimv&pid=referral) | currencycom | [Currency.com](https://currency.com/trading/signup?c=362jaimv&pid=referral) | 1 | [API](https://currency.com/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![eterbase](https://user-images.githubusercontent.com/1294454/82067900-faeb0f80-96d9-11ea-9f22-0071cfcb9871.jpg)](https://eterbase.exchange/invite/1wjjh4Pe) | eterbase | [Eterbase](https://eterbase.exchange/invite/1wjjh4Pe) | 1 | [API](https://developers.eterbase.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![ftx](https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg)](https://ftx.com/#a=1623029) | ftx | [FTX](https://ftx.com/#a=1623029) | * | [API](https://github.com/ftexchange/ftx) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![idex](https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg)](https://idex.io) | idex | [IDEX](https://idex.io) | 2 | [API](https://docs.idex.io/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![kraken](https://user-images.githubusercontent.com/51840849/76173629-fc67fb00-61b1-11ea-84fe-f2de582f58a3.jpg)](https://www.kraken.com) | kraken | [Kraken](https://www.kraken.com) | 0 | [API](https://www.kraken.com/features/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![upbit](https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg)](https://upbit.com) | upbit | [Upbit](https://upbit.com) | 1 | [API](https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![wavesexchange](https://user-images.githubusercontent.com/1294454/84547058-5fb27d80-ad0b-11ea-8711-78ac8b3c7f31.jpg)](https://waves.exchange) | wavesexchange | [Waves.Exchange](https://waves.exchange) | * | [API](https://docs.waves.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![xena](https://user-images.githubusercontent.com/51840849/87489843-bb469280-c64c-11ea-91aa-69c6326506af.jpg)](https://xena.exchange) | xena | [Xena Exchange](https://xena.exchange) | * | [API](https://support.xena.exchange/support/solutions/44000808700) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
## Supported Cryptocurrency Exchange Markets
The CCXT library currently supports the following 123 cryptocurrency exchange markets and trading APIs:
| logo | id | name | ver | doc | certified | pro |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|-----------------------------------------------------------------------------------------|:---:|:-------------------------------------------------------------------------------------------:|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [![acx](https://user-images.githubusercontent.com/1294454/30247614-1fe61c74-9621-11e7-9e8c-f1a627afa279.jpg)](https://acx.io) | acx | [ACX](https://acx.io) | 2 | [API](https://acx.io/documents/api_v2) | | |
| [![aofex](https://user-images.githubusercontent.com/51840849/77670271-056d1080-6f97-11ea-9ac2-4268e9ed0c1f.jpg)](https://aofex.com/#/register?key=9763840) | aofex | [AOFEX](https://aofex.com/#/register?key=9763840) | * | [API](https://aofex.zendesk.com/hc/en-us/sections/360005576574-API) | | |
| [![bcex](https://user-images.githubusercontent.com/51840849/77231516-851c6900-6bac-11ea-8fd6-ee5c23eddbd4.jpg)](https://www.bcex.top/register?invite_code=758978&lang=en) | bcex | [BCEX](https://www.bcex.top/register?invite_code=758978&lang=en) | 1 | [API](https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface) | | |
| [![bequant](https://user-images.githubusercontent.com/1294454/55248342-a75dfe00-525a-11e9-8aa2-05e9dca943c6.jpg)](https://bequant.io) | bequant | [Bequant](https://bequant.io) | 2 | [API](https://api.bequant.io/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bibox](https://user-images.githubusercontent.com/51840849/77257418-3262b000-6c85-11ea-8fb8-20bdf20b3592.jpg)](https://w2.bibox365.com/login/register?invite_code=05Kj3I) | bibox | [Bibox](https://w2.bibox365.com/login/register?invite_code=05Kj3I) | 1 | [API](https://biboxcom.github.io/en/) | | |
| [![bigone](https://user-images.githubusercontent.com/1294454/69354403-1d532180-0c91-11ea-88ed-44c06cefdf87.jpg)](https://b1.run/users/new?code=D3LLBVFT) | bigone | [BigONE](https://b1.run/users/new?code=D3LLBVFT) | 3 | [API](https://open.big.one/docs/api.html) | | |
| [![binance](https://user-images.githubusercontent.com/1294454/29604020-d5483cdc-87ee-11e7-94c7-d1a8d9169293.jpg)](https://www.binance.com/?ref=10205187) | binance | [Binance](https://www.binance.com/?ref=10205187) | * | [API](https://binance-docs.github.io/apidocs/spot/en) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![binanceus](https://user-images.githubusercontent.com/1294454/65177307-217b7c80-da5f-11e9-876e-0b748ba0a358.jpg)](https://www.binance.us/?ref=35005074) | binanceus | [Binance US](https://www.binance.us/?ref=35005074) | * | [API](https://github.com/binance-us/binance-official-api-docs) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bit2c](https://user-images.githubusercontent.com/1294454/27766119-3593220e-5ece-11e7-8b3a-5a041f6bcc3f.jpg)](https://bit2c.co.il/Aff/63bfed10-e359-420c-ab5a-ad368dab0baf) | bit2c | [Bit2C](https://bit2c.co.il/Aff/63bfed10-e359-420c-ab5a-ad368dab0baf) | * | [API](https://www.bit2c.co.il/home/api) | | |
| [![bitbank](https://user-images.githubusercontent.com/1294454/37808081-b87f2d9c-2e59-11e8-894d-c1900b7584fe.jpg)](https://bitbank.cc/) | bitbank | [bitbank](https://bitbank.cc/) | 1 | [API](https://docs.bitbank.cc/) | | |
| [![bitbay](https://user-images.githubusercontent.com/1294454/27766132-978a7bd8-5ece-11e7-9540-bc96d1e9bbb8.jpg)](https://auth.bitbay.net/ref/jHlbB4mIkdS1) | bitbay | [BitBay](https://auth.bitbay.net/ref/jHlbB4mIkdS1) | * | [API](https://bitbay.net/public-api) | | |
| [![bitcoincom](https://user-images.githubusercontent.com/1294454/97296144-514fa300-1861-11eb-952b-3d55d492200b.jpg)](https://exchange.bitcoin.com/referral/da948b21d6c92d69) | bitcoincom | [bitcoin.com](https://exchange.bitcoin.com/referral/da948b21d6c92d69) | 2 | [API](https://api.exchange.bitcoin.com/api/2/explore) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitfinex](https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg)](https://www.bitfinex.com/?refcode=P61eYxFL) | bitfinex | [Bitfinex](https://www.bitfinex.com/?refcode=P61eYxFL) | 1 | [API](https://docs.bitfinex.com/v1/docs) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitfinex2](https://user-images.githubusercontent.com/1294454/27766244-e328a50c-5ed2-11e7-947b-041416579bb3.jpg)](https://www.bitfinex.com/?refcode=P61eYxFL) | bitfinex2 | [Bitfinex](https://www.bitfinex.com/?refcode=P61eYxFL) | 2 | [API](https://docs.bitfinex.com/v2/docs/) | | |
| [![bitflyer](https://user-images.githubusercontent.com/1294454/28051642-56154182-660e-11e7-9b0d-6042d1e6edd8.jpg)](https://bitflyer.com) | bitflyer | [bitFlyer](https://bitflyer.com) | 1 | [API](https://lightning.bitflyer.com/docs?lang=en) | | |
| [![bitforex](https://user-images.githubusercontent.com/51840849/87295553-1160ec00-c50e-11ea-8ea0-df79276a9646.jpg)](https://www.bitforex.com/en/invitationRegister?inviterId=1867438) | bitforex | [Bitforex](https://www.bitforex.com/en/invitationRegister?inviterId=1867438) | 1 | [API](https://github.com/githubdev2020/API_Doc_en/wiki) | | |
| [![bitget](https://user-images.githubusercontent.com/51840849/88317935-a8a21c80-cd22-11ea-8e2b-4b9fac5975eb.jpg)](https://www.bitget.com/expressly?languageType=0&channelCode=ccxt&vipCode=tg9j) | bitget | [Bitget](https://www.bitget.com/expressly?languageType=0&channelCode=ccxt&vipCode=tg9j) | 3 | [API](https://bitgetlimited.github.io/apidoc/en/swap) | | |
| [![bithumb](https://user-images.githubusercontent.com/1294454/30597177-ea800172-9d5e-11e7-804c-b9d4fa9b56b0.jpg)](https://www.bithumb.com) | bithumb | [Bithumb](https://www.bithumb.com) | * | [API](https://apidocs.bithumb.com) | | |
| [![bitkk](https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg)](https://www.bitkk.com) | bitkk | [bitkk](https://www.bitkk.com) | 1 | [API](https://www.bitkk.com/i/developer) | | |
| [![bitmart](https://user-images.githubusercontent.com/1294454/61835713-a2662f80-ae85-11e9-9d00-6442919701fd.jpg)](http://www.bitmart.com/?r=rQCFLh) | bitmart | [BitMart](http://www.bitmart.com/?r=rQCFLh) | 1 | [API](https://github.com/bitmartexchange/bitmart-official-api-docs) | | |
| [![bitmax](https://user-images.githubusercontent.com/1294454/66820319-19710880-ef49-11e9-8fbe-16be62a11992.jpg)](https://bitmax.io/#/register?inviteCode=EL6BXBQM) | bitmax | [BitMax](https://bitmax.io/#/register?inviteCode=EL6BXBQM) | 1 | [API](https://bitmax-exchange.github.io/bitmax-pro-api/#bitmax-pro-api-documentation) | | |
| [![bitmex](https://user-images.githubusercontent.com/1294454/27766319-f653c6e6-5ed4-11e7-933d-f0bc3699ae8f.jpg)](https://www.bitmex.com/register/upZpOX) | bitmex | [BitMEX](https://www.bitmex.com/register/upZpOX) | 1 | [API](https://www.bitmex.com/app/apiOverview) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitpanda](https://user-images.githubusercontent.com/51840849/87591171-9a377d80-c6f0-11ea-94ac-97a126eac3bc.jpg)](https://www.bitpanda.com/en/pro) | bitpanda | [Bitpanda Pro](https://www.bitpanda.com/en/pro) | 1 | [API](https://developers.bitpanda.com/exchange/) | | |
| [![bitso](https://user-images.githubusercontent.com/51840849/87295554-11f98280-c50e-11ea-80d6-15b3bafa8cbf.jpg)](https://bitso.com/?ref=itej) | bitso | [Bitso](https://bitso.com/?ref=itej) | 3 | [API](https://bitso.com/api_info) | | |
| [![bitstamp](https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg)](https://www.bitstamp.net) | bitstamp | [Bitstamp](https://www.bitstamp.net) | 2 | [API](https://www.bitstamp.net/api) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitstamp1](https://user-images.githubusercontent.com/1294454/27786377-8c8ab57e-5fe9-11e7-8ea4-2b05b6bcceec.jpg)](https://www.bitstamp.net) | bitstamp1 | [Bitstamp](https://www.bitstamp.net) | 1 | [API](https://www.bitstamp.net/api) | | |
| [![bittrex](https://user-images.githubusercontent.com/51840849/87153921-edf53180-c2c0-11ea-96b9-f2a9a95a455b.jpg)](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | bittrex | [Bittrex](https://bittrex.com/Account/Register?referralCode=1ZE-G0G-M3B) | 3 | [API](https://bittrex.github.io/api/v3) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitvavo](https://user-images.githubusercontent.com/1294454/83165440-2f1cf200-a116-11ea-9046-a255d09fb2ed.jpg)](https://bitvavo.com/?a=24F34952F7) | bitvavo | [Bitvavo](https://bitvavo.com/?a=24F34952F7) | 2 | [API](https://docs.bitvavo.com/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![bitz](https://user-images.githubusercontent.com/51840849/87443304-fec5e000-c5fd-11ea-98f8-ba8e67f7eaff.jpg)](https://u.bitz.com/register?invite_code=1429193) | bitz | [Bit-Z](https://u.bitz.com/register?invite_code=1429193) | 2 | [API](https://apidoc.bitz.com/en/) | | |
| [![bl3p](https://user-images.githubusercontent.com/1294454/28501752-60c21b82-6feb-11e7-818b-055ee6d0e754.jpg)](https://bl3p.eu) | bl3p | [BL3P](https://bl3p.eu) | 1 | [API](https://github.com/BitonicNL/bl3p-api/tree/master/docs) | | |
| [![bleutrade](https://user-images.githubusercontent.com/1294454/30303000-b602dbe6-976d-11e7-956d-36c5049c01e7.jpg)](https://bleutrade.com) | bleutrade | [Bleutrade](https://bleutrade.com) | * | [API](https://app.swaggerhub.com/apis-docs/bleu/white-label/3.0.0) | | |
| [![braziliex](https://user-images.githubusercontent.com/1294454/34703593-c4498674-f504-11e7-8d14-ff8e44fb78c1.jpg)](https://braziliex.com/?ref=5FE61AB6F6D67DA885BC98BA27223465) | braziliex | [Braziliex](https://braziliex.com/?ref=5FE61AB6F6D67DA885BC98BA27223465) | * | [API](https://braziliex.com/exchange/api.php) | | |
| [![btcalpha](https://user-images.githubusercontent.com/1294454/42625213-dabaa5da-85cf-11e8-8f99-aa8f8f7699f0.jpg)](https://btc-alpha.com/?r=123788) | btcalpha | [BTC-Alpha](https://btc-alpha.com/?r=123788) | 1 | [API](https://btc-alpha.github.io/api-docs) | | |
| [![btcbox](https://user-images.githubusercontent.com/51840849/87327317-98c55400-c53c-11ea-9a11-81f7d951cc74.jpg)](https://www.btcbox.co.jp/) | btcbox | [BtcBox](https://www.btcbox.co.jp/) | 1 | [API](https://blog.btcbox.jp/en/archives/8762) | | |
| [![btcmarkets](https://user-images.githubusercontent.com/51840849/89731817-b3fb8480-da52-11ea-817f-783b08aaf32b.jpg)](https://btcmarkets.net) | btcmarkets | [BTC Markets](https://btcmarkets.net) | 3 | [API](https://api.btcmarkets.net/doc/v3) | | |
| [![btctradeua](https://user-images.githubusercontent.com/1294454/27941483-79fc7350-62d9-11e7-9f61-ac47f28fcd96.jpg)](https://btc-trade.com.ua/registration/22689) | btctradeua | [BTC Trade UA](https://btc-trade.com.ua/registration/22689) | * | [API](https://docs.google.com/document/d/1ocYA0yMy_RXd561sfG3qEPZ80kyll36HUxvCRe5GbhE/edit) | | |
| [![btcturk](https://user-images.githubusercontent.com/51840849/87153926-efbef500-c2c0-11ea-9842-05b63612c4b9.jpg)](https://www.btcturk.com) | btcturk | [BTCTurk](https://www.btcturk.com) | * | [API](https://github.com/BTCTrader/broker-api-docs) | | |
| [![buda](https://user-images.githubusercontent.com/1294454/47380619-8a029200-d706-11e8-91e0-8a391fe48de3.jpg)](https://www.buda.com) | buda | [Buda](https://www.buda.com) | 2 | [API](https://api.buda.com) | | |
| [![bw](https://user-images.githubusercontent.com/1294454/69436317-31128c80-0d52-11ea-91d1-eb7bb5818812.jpg)](https://www.bw.com/regGetCommission/N3JuT1R3bWxKTE0) | bw | [BW](https://www.bw.com/regGetCommission/N3JuT1R3bWxKTE0) | 1 | [API](https://github.com/bw-exchange/api_docs_en/wiki) | | |
| [![bybit](https://user-images.githubusercontent.com/51840849/76547799-daff5b80-649e-11ea-87fb-3be9bac08954.jpg)](https://www.bybit.com/app/register?ref=X7Prm) | bybit | [Bybit](https://www.bybit.com/app/register?ref=X7Prm) | 2 | [API](https://bybit-exchange.github.io/docs/inverse/) | | |
| [![bytetrade](https://user-images.githubusercontent.com/1294454/67288762-2f04a600-f4e6-11e9-9fd6-c60641919491.jpg)](https://www.byte-trade.com) | bytetrade | [ByteTrade](https://www.byte-trade.com) | * | [API](https://docs.byte-trade.com/#description) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![cex](https://user-images.githubusercontent.com/1294454/27766442-8ddc33b0-5ed8-11e7-8b98-f786aef0f3c9.jpg)](https://cex.io/r/0/up105393824/0/) | cex | [CEX.IO](https://cex.io/r/0/up105393824/0/) | * | [API](https://cex.io/cex-api) | | |
| [![chilebit](https://user-images.githubusercontent.com/1294454/27991414-1298f0d8-647f-11e7-9c40-d56409266336.jpg)](https://chilebit.net) | chilebit | [ChileBit](https://chilebit.net) | 1 | [API](https://blinktrade.com/docs) | | |
| [![coinbase](https://user-images.githubusercontent.com/1294454/40811661-b6eceae2-653a-11e8-829e-10bfadb078cf.jpg)](https://www.coinbase.com/join/58cbe25a355148797479dbd2) | coinbase | [Coinbase](https://www.coinbase.com/join/58cbe25a355148797479dbd2) | 2 | [API](https://developers.coinbase.com/api/v2) | | |
| [![coinbaseprime](https://user-images.githubusercontent.com/1294454/44539184-29f26e00-a70c-11e8-868f-e907fc236a7c.jpg)](https://prime.coinbase.com) | coinbaseprime | [Coinbase Prime](https://prime.coinbase.com) | * | [API](https://docs.prime.coinbase.com) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![coinbasepro](https://user-images.githubusercontent.com/1294454/41764625-63b7ffde-760a-11e8-996d-a6328fa9347a.jpg)](https://pro.coinbase.com/) | coinbasepro | [Coinbase Pro](https://pro.coinbase.com/) | * | [API](https://docs.pro.coinbase.com) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![coincheck](https://user-images.githubusercontent.com/51840849/87182088-1d6d6380-c2ec-11ea-9c64-8ab9f9b289f5.jpg)](https://coincheck.com) | coincheck | [coincheck](https://coincheck.com) | * | [API](https://coincheck.com/documents/exchange/api) | | |
| [![coinegg](https://user-images.githubusercontent.com/1294454/36770310-adfa764e-1c5a-11e8-8e09-449daac3d2fb.jpg)](https://www.coinegg.com/user/register?invite=523218) | coinegg | [CoinEgg](https://www.coinegg.com/user/register?invite=523218) | * | [API](https://www.coinegg.com/explain.api.html) | | |
| [![coinex](https://user-images.githubusercontent.com/51840849/87182089-1e05fa00-c2ec-11ea-8da9-cc73b45abbbc.jpg)](https://www.coinex.com/register?refer_code=yw5fz) | coinex | [CoinEx](https://www.coinex.com/register?refer_code=yw5fz) | 1 | [API](https://github.com/coinexcom/coinex_exchange_api/wiki) | | |
| [![coinfalcon](https://user-images.githubusercontent.com/1294454/41822275-ed982188-77f5-11e8-92bb-496bcd14ca52.jpg)](https://coinfalcon.com/?ref=CFJSVGTUPASB) | coinfalcon | [CoinFalcon](https://coinfalcon.com/?ref=CFJSVGTUPASB) | 1 | [API](https://docs.coinfalcon.com) | | |
| [![coinfloor](https://user-images.githubusercontent.com/51840849/87153925-ef265e80-c2c0-11ea-91b5-020c804b90e0.jpg)](https://www.coinfloor.co.uk) | coinfloor | [coinfloor](https://www.coinfloor.co.uk) | * | [API](https://github.com/coinfloor/api) | | |
| [![coingi](https://user-images.githubusercontent.com/1294454/28619707-5c9232a8-7212-11e7-86d6-98fe5d15cc6e.jpg)](https://www.coingi.com/?r=XTPPMC) | coingi | [Coingi](https://www.coingi.com/?r=XTPPMC) | * | [API](https://coingi.docs.apiary.io) | | |
| [![coinmarketcap](https://user-images.githubusercontent.com/51840849/87182086-1cd4cd00-c2ec-11ea-9ec4-d0cf2a2abf62.jpg)](https://coinmarketcap.com) | coinmarketcap | [CoinMarketCap](https://coinmarketcap.com) | 1 | [API](https://coinmarketcap.com/api) | | |
| [![coinmate](https://user-images.githubusercontent.com/51840849/87460806-1c9f3f00-c616-11ea-8c46-a77018a8f3f4.jpg)](https://coinmate.io?referral=YTFkM1RsOWFObVpmY1ZjMGREQmpTRnBsWjJJNVp3PT0) | coinmate | [CoinMate](https://coinmate.io?referral=YTFkM1RsOWFObVpmY1ZjMGREQmpTRnBsWjJJNVp3PT0) | * | [API](https://coinmate.docs.apiary.io) | | |
| [![coinone](https://user-images.githubusercontent.com/1294454/38003300-adc12fba-323f-11e8-8525-725f53c4a659.jpg)](https://coinone.co.kr) | coinone | [CoinOne](https://coinone.co.kr) | 2 | [API](https://doc.coinone.co.kr) | | |
| [![coinspot](https://user-images.githubusercontent.com/1294454/28208429-3cacdf9a-6896-11e7-854e-4c79a772a30f.jpg)](https://www.coinspot.com.au/register?code=PJURCU) | coinspot | [CoinSpot](https://www.coinspot.com.au/register?code=PJURCU) | * | [API](https://www.coinspot.com.au/api) | | |
| [![crex24](https://user-images.githubusercontent.com/1294454/47813922-6f12cc00-dd5d-11e8-97c6-70f957712d47.jpg)](https://crex24.com/?refid=slxsjsjtil8xexl9hksr) | crex24 | [CREX24](https://crex24.com/?refid=slxsjsjtil8xexl9hksr) | 2 | [API](https://docs.crex24.com/trade-api/v2) | | |
| [![currencycom](https://user-images.githubusercontent.com/1294454/83718672-36745c00-a63e-11ea-81a9-677b1f789a4d.jpg)](https://currency.com/trading/signup?c=362jaimv&pid=referral) | currencycom | [Currency.com](https://currency.com/trading/signup?c=362jaimv&pid=referral) | 1 | [API](https://currency.com/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![deribit](https://user-images.githubusercontent.com/1294454/41933112-9e2dd65a-798b-11e8-8440-5bab2959fcb8.jpg)](https://www.deribit.com/reg-1189.4038) | deribit | [Deribit](https://www.deribit.com/reg-1189.4038) | 2 | [API](https://docs.deribit.com/v2) | | |
| [![digifinex](https://user-images.githubusercontent.com/51840849/87443315-01283a00-c5fe-11ea-8628-c2a0feaf07ac.jpg)](https://www.digifinex.com/en-ww/from/DhOzBg?channelCode=ljaUPp) | digifinex | [DigiFinex](https://www.digifinex.com/en-ww/from/DhOzBg?channelCode=ljaUPp) | 3 | [API](https://docs.digifinex.com) | | |
| [![dsx](https://user-images.githubusercontent.com/51840849/76909626-cb2bb100-68bc-11ea-99e0-28ba54f04792.jpg)](https://dsxglobal.com) | dsx | [DSX](https://dsxglobal.com) | 3 | [API](https://dsxglobal.com/developers/publicApi) | | |
| [![eterbase](https://user-images.githubusercontent.com/1294454/82067900-faeb0f80-96d9-11ea-9f22-0071cfcb9871.jpg)](https://eterbase.exchange/invite/1wjjh4Pe) | eterbase | [Eterbase](https://eterbase.exchange/invite/1wjjh4Pe) | 1 | [API](https://developers.eterbase.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![exmo](https://user-images.githubusercontent.com/1294454/27766491-1b0ea956-5eda-11e7-9225-40d67b481b8d.jpg)](https://exmo.me/?ref=131685) | exmo | [EXMO](https://exmo.me/?ref=131685) | 1.1 | [API](https://exmo.me/en/api_doc?ref=131685) | | |
| [![exx](https://user-images.githubusercontent.com/1294454/37770292-fbf613d0-2de4-11e8-9f79-f2dc451b8ccb.jpg)](https://www.exx.com/r/fde4260159e53ab8a58cc9186d35501f?recommQd=1) | exx | [EXX](https://www.exx.com/r/fde4260159e53ab8a58cc9186d35501f?recommQd=1) | * | [API](https://www.exx.com/help/restApi) | | |
| [![fcoin](https://user-images.githubusercontent.com/1294454/42244210-c8c42e1e-7f1c-11e8-8710-a5fb63b165c4.jpg)](https://www.fcoin.com/i/Z5P7V) | fcoin | [FCoin](https://www.fcoin.com/i/Z5P7V) | 2 | [API](https://developer.fcoin.com) | | |
| [![fcoinjp](https://user-images.githubusercontent.com/1294454/54219174-08b66b00-4500-11e9-862d-f522d0fe08c6.jpg)](https://www.fcoinjp.com) | fcoinjp | [FCoinJP](https://www.fcoinjp.com) | 2 | [API](https://developer.fcoin.com) | | |
| [![flowbtc](https://user-images.githubusercontent.com/51840849/87443317-01c0d080-c5fe-11ea-95c2-9ebe1a8fafd9.jpg)](https://www.flowbtc.com.br) | flowbtc | [flowBTC](https://www.flowbtc.com.br) | 1 | [API](https://www.flowbtc.com.br/api.html) | | |
| [![foxbit](https://user-images.githubusercontent.com/51840849/87443320-01c0d080-c5fe-11ea-92e2-4ef56d32b026.jpg)](https://foxbit.com.br/exchange) | foxbit | [FoxBit](https://foxbit.com.br/exchange) | 1 | [API](https://foxbit.com.br/api/) | | |
| [![ftx](https://user-images.githubusercontent.com/1294454/67149189-df896480-f2b0-11e9-8816-41593e17f9ec.jpg)](https://ftx.com/#a=1623029) | ftx | [FTX](https://ftx.com/#a=1623029) | * | [API](https://github.com/ftexchange/ftx) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![gateio](https://user-images.githubusercontent.com/1294454/31784029-0313c702-b509-11e7-9ccc-bc0da6a0e435.jpg)](https://www.gate.io/signup/2436035) | gateio | [Gate.io](https://www.gate.io/signup/2436035) | 2 | [API](https://gate.io/api2) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![gemini](https://user-images.githubusercontent.com/1294454/27816857-ce7be644-6096-11e7-82d6-3c257263229c.jpg)](https://gemini.com/) | gemini | [Gemini](https://gemini.com/) | 1 | [API](https://docs.gemini.com/rest-api) | | |
| [![hbtc](https://user-images.githubusercontent.com/51840849/80134449-70663300-85a7-11ea-8942-e204cdeaab5d.jpg)](https://www.hbtc.com/register/O2S8NS) | hbtc | [HBTC](https://www.hbtc.com/register/O2S8NS) | 1 | [API](https://github.com/bhexopen/BHEX-OpenApi/tree/master/doc) | | |
| [![hitbtc](https://user-images.githubusercontent.com/1294454/27766555-8eaec20e-5edc-11e7-9c5b-6dc69fc42f5e.jpg)](https://hitbtc.com/?ref_id=5a5d39a65d466) | hitbtc | [HitBTC](https://hitbtc.com/?ref_id=5a5d39a65d466) | 2 | [API](https://api.hitbtc.com) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![hollaex](https://user-images.githubusercontent.com/1294454/75841031-ca375180-5ddd-11ea-8417-b975674c23cb.jpg)](https://pro.hollaex.com/signup?affiliation_code=QSWA6G) | hollaex | [HollaEx](https://pro.hollaex.com/signup?affiliation_code=QSWA6G) | 1 | [API](https://apidocs.hollaex.com) | | |
| [![huobijp](https://user-images.githubusercontent.com/1294454/85734211-85755480-b705-11ea-8b35-0b7f1db33a2f.jpg)](https://www.huobi.co.jp/register/?invite_code=znnq3) | huobijp | [Huobi Japan](https://www.huobi.co.jp/register/?invite_code=znnq3) | 1 | [API](https://api-doc.huobi.co.jp) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![huobipro](https://user-images.githubusercontent.com/1294454/76137448-22748a80-604e-11ea-8069-6e389271911d.jpg)](https://www.huobi.com/en-us/topic/invited/?invite_code=rwrd3) | huobipro | [Huobi Pro](https://www.huobi.com/en-us/topic/invited/?invite_code=rwrd3) | 1 | [API](https://huobiapi.github.io/docs/spot/v1/cn/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![huobiru](https://user-images.githubusercontent.com/1294454/52978816-e8552e00-33e3-11e9-98ed-845acfece834.jpg)](https://www.huobi.com.ru/invite?invite_code=esc74) | huobiru | [Huobi Russia](https://www.huobi.com.ru/invite?invite_code=esc74) | 1 | [API](https://github.com/cloudapidoc/API_Docs_en) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![ice3x](https://user-images.githubusercontent.com/51840849/87460809-1dd06c00-c616-11ea-98ad-7d5e1cb7fcdd.jpg)](https://ice3x.com?ref=14341802) | ice3x | [ICE3X](https://ice3x.com?ref=14341802) | 1 | [API](https://ice3x.co.za/ice-cubed-bitcoin-exchange-api-documentation-1-june-2017) | | |
| [![idex](https://user-images.githubusercontent.com/51840849/94481303-2f222100-01e0-11eb-97dd-bc14c5943a86.jpg)](https://idex.io) | idex | [IDEX](https://idex.io) | 2 | [API](https://docs.idex.io/) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![independentreserve](https://user-images.githubusercontent.com/51840849/87182090-1e9e9080-c2ec-11ea-8e49-563db9a38f37.jpg)](https://www.independentreserve.com) | independentreserve | [Independent Reserve](https://www.independentreserve.com) | * | [API](https://www.independentreserve.com/API) | | |
| [![indodax](https://user-images.githubusercontent.com/51840849/87070508-9358c880-c221-11ea-8dc5-5391afbbb422.jpg)](https://indodax.com/ref/testbitcoincoid/1) | indodax | [INDODAX](https://indodax.com/ref/testbitcoincoid/1) | 2.0 | [API](https://github.com/btcid/indodax-official-api-docs) | | |
| [![itbit](https://user-images.githubusercontent.com/1294454/27822159-66153620-60ad-11e7-89e7-005f6d7f3de0.jpg)](https://www.itbit.com) | itbit | [itBit](https://www.itbit.com) | 1 | [API](https://api.itbit.com/docs) | | |
| [![kraken](https://user-images.githubusercontent.com/51840849/76173629-fc67fb00-61b1-11ea-84fe-f2de582f58a3.jpg)](https://www.kraken.com) | kraken | [Kraken](https://www.kraken.com) | 0 | [API](https://www.kraken.com/features/api) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![kucoin](https://user-images.githubusercontent.com/51840849/87295558-132aaf80-c50e-11ea-9801-a2fb0c57c799.jpg)](https://www.kucoin.com/?rcode=E5wkqe) | kucoin | [KuCoin](https://www.kucoin.com/?rcode=E5wkqe) | 2 | [API](https://docs.kucoin.com) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![kuna](https://user-images.githubusercontent.com/51840849/87153927-f0578b80-c2c0-11ea-84b6-74612568e9e1.jpg)](https://kuna.io?r=kunaid-gvfihe8az7o4) | kuna | [Kuna](https://kuna.io?r=kunaid-gvfihe8az7o4) | 2 | [API](https://kuna.io/documents/api) | | |
| [![lakebtc](https://user-images.githubusercontent.com/1294454/28074120-72b7c38a-6660-11e7-92d9-d9027502281d.jpg)](https://www.lakebtc.com) | lakebtc | [LakeBTC](https://www.lakebtc.com) | 2 | [API](https://www.lakebtc.com/s/api_v2) | | |
| [![latoken](https://user-images.githubusercontent.com/1294454/61511972-24c39f00-aa01-11e9-9f7c-471f1d6e5214.jpg)](https://latoken.com) | latoken | [Latoken](https://latoken.com) | 1 | [API](https://api.latoken.com) | | |
| [![lbank](https://user-images.githubusercontent.com/1294454/38063602-9605e28a-3302-11e8-81be-64b1e53c4cfb.jpg)](https://www.lbex.io/invite?icode=7QCY) | lbank | [LBank](https://www.lbex.io/invite?icode=7QCY) | 1 | [API](https://github.com/LBank-exchange/lbank-official-api-docs) | | |
| [![liquid](https://user-images.githubusercontent.com/1294454/45798859-1a872600-bcb4-11e8-8746-69291ce87b04.jpg)](https://www.liquid.com/sign-up/?affiliate=SbzC62lt30976) | liquid | [Liquid](https://www.liquid.com/sign-up/?affiliate=SbzC62lt30976) | 2 | [API](https://developers.liquid.com) | | |
| [![livecoin](https://user-images.githubusercontent.com/1294454/27980768-f22fc424-638a-11e7-89c9-6010a54ff9be.jpg)](https://livecoin.net/?from=Livecoin-CQ1hfx44) | livecoin | [LiveCoin](https://livecoin.net/?from=Livecoin-CQ1hfx44) | * | [API](https://www.livecoin.net/api?lang=en) | | |
| [![luno](https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg)](https://www.luno.com/invite/44893A) | luno | [luno](https://www.luno.com/invite/44893A) | 1 | [API](https://www.luno.com/en/api) | | |
| [![lykke](https://user-images.githubusercontent.com/1294454/34487620-3139a7b0-efe6-11e7-90f5-e520cef74451.jpg)](https://www.lykke.com) | lykke | [Lykke](https://www.lykke.com) | 1 | [API](https://hft-api.lykke.com/swagger/ui/) | | |
| [![mercado](https://user-images.githubusercontent.com/1294454/27837060-e7c58714-60ea-11e7-9192-f05e86adb83f.jpg)](https://www.mercadobitcoin.com.br) | mercado | [Mercado Bitcoin](https://www.mercadobitcoin.com.br) | 3 | [API](https://www.mercadobitcoin.com.br/api-doc) | | |
| [![mixcoins](https://user-images.githubusercontent.com/51840849/87460810-1dd06c00-c616-11ea-9276-956f400d6ffa.jpg)](https://mixcoins.com) | mixcoins | [MixCoins](https://mixcoins.com) | 1 | [API](https://mixcoins.com/help/api/) | | |
| [![novadax](https://user-images.githubusercontent.com/1294454/92337550-2b085500-f0b3-11ea-98e7-5794fb07dd3b.jpg)](https://www.novadax.com.br/?s=ccxt) | novadax | [NovaDAX](https://www.novadax.com.br/?s=ccxt) | 1 | [API](https://doc.novadax.com/pt-BR/) | | |
| [![oceanex](https://user-images.githubusercontent.com/1294454/58385970-794e2d80-8001-11e9-889c-0567cd79b78e.jpg)](https://oceanex.pro/signup?referral=VE24QX) | oceanex | [OceanEx](https://oceanex.pro/signup?referral=VE24QX) | 1 | [API](https://api.oceanex.pro/doc/v1) | | |
| [![okcoin](https://user-images.githubusercontent.com/51840849/87295551-102fbf00-c50e-11ea-90a9-462eebba5829.jpg)](https://www.okcoin.com/account/register?flag=activity&channelId=600001513) | okcoin | [OKCoin](https://www.okcoin.com/account/register?flag=activity&channelId=600001513) | 3 | [API](https://www.okcoin.com/docs/en/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![okex](https://user-images.githubusercontent.com/1294454/32552768-0d6dd3c6-c4a6-11e7-90f8-c043b64756a7.jpg)](https://www.okex.com/join/1888677) | okex | [OKEX](https://www.okex.com/join/1888677) | 3 | [API](https://www.okex.com/docs/en/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![paymium](https://user-images.githubusercontent.com/51840849/87153930-f0f02200-c2c0-11ea-9c0a-40337375ae89.jpg)](https://www.paymium.com/page/sign-up?referral=eDAzPoRQFMvaAB8sf-qj) | paymium | [Paymium](https://www.paymium.com/page/sign-up?referral=eDAzPoRQFMvaAB8sf-qj) | 1 | [API](https://github.com/Paymium/api-documentation) | | |
| [![phemex](https://user-images.githubusercontent.com/1294454/85225056-221eb600-b3d7-11ea-930d-564d2690e3f6.jpg)](https://phemex.com/register?referralCode=EDNVJ) | phemex | [Phemex](https://phemex.com/register?referralCode=EDNVJ) | 1 | [API](https://github.com/phemex/phemex-api-docs) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![poloniex](https://user-images.githubusercontent.com/1294454/27766817-e9456312-5ee6-11e7-9b3c-b628ca5626a5.jpg)](https://poloniex.com/signup?c=UBFZJRPJ) | poloniex | [Poloniex](https://poloniex.com/signup?c=UBFZJRPJ) | * | [API](https://docs.poloniex.com) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![probit](https://user-images.githubusercontent.com/51840849/79268032-c4379480-7ea2-11ea-80b3-dd96bb29fd0d.jpg)](https://www.probit.com/r/34608773) | probit | [ProBit](https://www.probit.com/r/34608773) | 1 | [API](https://docs-en.probit.com) | | |
| [![qtrade](https://user-images.githubusercontent.com/51840849/80491487-74a99c00-896b-11ea-821e-d307e832f13e.jpg)](https://qtrade.io/?ref=BKOQWVFGRH2C) | qtrade | [qTrade](https://qtrade.io/?ref=BKOQWVFGRH2C) | 1 | [API](https://qtrade-exchange.github.io/qtrade-docs) | | |
| [![rightbtc](https://user-images.githubusercontent.com/51840849/87182092-1f372700-c2ec-11ea-8f9e-01b4d3ff8941.jpg)](https://www.rightbtc.com) | rightbtc | [RightBTC](https://www.rightbtc.com) | * | [API](https://docs.rightbtc.com/api/) | | |
| [![ripio](https://user-images.githubusercontent.com/1294454/94507548-a83d6a80-0218-11eb-9998-28b9cec54165.jpg)](https://exchange.ripio.com) | ripio | [Ripio](https://exchange.ripio.com) | 1 | [API](https://exchange.ripio.com/en/api/) | | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![southxchange](https://user-images.githubusercontent.com/1294454/27838912-4f94ec8a-60f6-11e7-9e5d-bbf9bd50a559.jpg)](https://www.southxchange.com) | southxchange | [SouthXchange](https://www.southxchange.com) | * | [API](https://www.southxchange.com/Home/Api) | | |
| [![stex](https://user-images.githubusercontent.com/1294454/69680782-03fd0b80-10bd-11ea-909e-7f603500e9cc.jpg)](https://app.stex.com?ref=36416021) | stex | [STEX](https://app.stex.com?ref=36416021) | 3 | [API](https://help.stex.com/en/collections/1593608-api-v3-documentation) | | |
| [![surbitcoin](https://user-images.githubusercontent.com/1294454/27991511-f0a50194-6481-11e7-99b5-8f02932424cc.jpg)](https://surbitcoin.com) | surbitcoin | [SurBitcoin](https://surbitcoin.com) | 1 | [API](https://blinktrade.com/docs) | | |
| [![therock](https://user-images.githubusercontent.com/1294454/27766869-75057fa2-5ee9-11e7-9a6f-13e641fa4707.jpg)](https://therocktrading.com) | therock | [TheRockTrading](https://therocktrading.com) | 1 | [API](https://api.therocktrading.com/doc/v1/index.html) | | |
| [![tidebit](https://user-images.githubusercontent.com/51840849/87460811-1e690280-c616-11ea-8652-69f187305add.jpg)](http://bit.ly/2IX0LrM) | tidebit | [TideBit](http://bit.ly/2IX0LrM) | 2 | [API](https://www.tidebit.com/documents/api/guide) | | |
| [![tidex](https://user-images.githubusercontent.com/1294454/30781780-03149dc4-a12e-11e7-82bb-313b269d24d4.jpg)](https://tidex.com/exchange/?ref=57f5638d9cd7) | tidex | [Tidex](https://tidex.com/exchange/?ref=57f5638d9cd7) | 3 | [API](https://tidex.com/exchange/public-api) | | |
| [![timex](https://user-images.githubusercontent.com/1294454/70423869-6839ab00-1a7f-11ea-8f94-13ae72c31115.jpg)](https://timex.io/?refcode=1x27vNkTbP1uwkCck) | timex | [TimeX](https://timex.io/?refcode=1x27vNkTbP1uwkCck) | 1 | [API](https://docs.timex.io) | | |
| [![upbit](https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg)](https://upbit.com) | upbit | [Upbit](https://upbit.com) | 1 | [API](https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | [![CCXT Pro](https://img.shields.io/badge/CCXT-Pro-black)](https://ccxt.pro) |
| [![vaultoro](https://user-images.githubusercontent.com/1294454/27766880-f205e870-5ee9-11e7-8fe2-0d5b15880752.jpg)](https://www.vaultoro.com) | vaultoro | [Vaultoro](https://www.vaultoro.com) | 1 | [API](https://api.vaultoro.com) | | |
| [![vbtc](https://user-images.githubusercontent.com/1294454/27991481-1f53d1d8-6481-11e7-884e-21d17e7939db.jpg)](https://vbtc.exchange) | vbtc | [VBTC](https://vbtc.exchange) | 1 | [API](https://blinktrade.com/docs) | | |
| [![vcc](https://user-images.githubusercontent.com/1294454/100545356-8427f500-326c-11eb-9539-7d338242d61b.jpg)](https://vcc.exchange?ref=l4xhrH) | vcc | [VCC Exchange](https://vcc.exchange?ref=l4xhrH) | 3 | [API](https://vcc.exchange/api) | | |
| [![wavesexchange](https://user-images.githubusercontent.com/1294454/84547058-5fb27d80-ad0b-11ea-8711-78ac8b3c7f31.jpg)](https://waves.exchange) | wavesexchange | [Waves.Exchange](https://waves.exchange) | * | [API](https://docs.waves.exchange) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![whitebit](https://user-images.githubusercontent.com/1294454/66732963-8eb7dd00-ee66-11e9-849b-10d9282bb9e0.jpg)](https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963) | whitebit | [WhiteBit](https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963) | 2 | [API](https://documenter.getpostman.com/view/7473075/Szzj8dgv?version=latest) | | |
| [![xbtce](https://user-images.githubusercontent.com/1294454/28059414-e235970c-662c-11e7-8c3a-08e31f78684b.jpg)](https://xbtce.com/?agent=XX97BTCXXXG687021000B) | xbtce | [xBTCe](https://xbtce.com/?agent=XX97BTCXXXG687021000B) | 1 | [API](https://www.xbtce.com/tradeapi) | | |
| [![xena](https://user-images.githubusercontent.com/51840849/87489843-bb469280-c64c-11ea-91aa-69c6326506af.jpg)](https://xena.exchange) | xena | [Xena Exchange](https://xena.exchange) | * | [API](https://support.xena.exchange/support/solutions/44000808700) | [![CCXT Certified](https://img.shields.io/badge/CCXT-Certified-green.svg)](https://github.com/ccxt/ccxt/wiki/Certification) | |
| [![yobit](https://user-images.githubusercontent.com/1294454/27766910-cdcbfdae-5eea-11e7-9859-03fea873272d.jpg)](https://www.yobit.net) | yobit | [YoBit](https://www.yobit.net) | 3 | [API](https://www.yobit.net/en/api/) | | |
| [![zaif](https://user-images.githubusercontent.com/1294454/27766927-39ca2ada-5eeb-11e7-972f-1b4199518ca6.jpg)](https://zaif.jp) | zaif | [Zaif](https://zaif.jp) | 1 | [API](https://techbureau-api-document.readthedocs.io/ja/latest/index.html) | | |
| [![zb](https://user-images.githubusercontent.com/1294454/32859187-cd5214f0-ca5e-11e7-967d-96568e2e2bd1.jpg)](https://www.zb.com) | zb | [ZB](https://www.zb.com) | 1 | [API](https://www.zb.com/i/developer) | | |
The list above is updated frequently, new crypto markets, exchanges, bug fixes, and API endpoints are introduced on a regular basis. See the [Manual](https://github.com/ccxt/ccxt/wiki) for more details. If you can't find a cryptocurrency exchange in the list above and want it to be added, post a link to it by opening an issue here on GitHub or send us an email.
The library is under [MIT license](https://github.com/ccxt/ccxt/blob/master/LICENSE.txt), that means it's absolutely free for any developer to build commercial and opensource software on top of it, but use it at your own risk with no warranties, as is.
---
## Install
The easiest way to install the CCXT library is to use a package manager:
- [ccxt in **NPM**](https://www.npmjs.com/package/ccxt) (JavaScript / Node v7.6+)
- [ccxt in **PyPI**](https://pypi.python.org/pypi/ccxt) (Python 3.5.3+)
- [ccxt in **Packagist/Composer**](https://packagist.org/packages/ccxt/ccxt) (PHP 5.4+)
This library is shipped as an all-in-one module implementation with minimalistic dependencies and requirements:
- [`js/`](https://github.com/ccxt/ccxt/blob/master/js/) in JavaScript
- [`python/`](https://github.com/ccxt/ccxt/blob/master/python/) in Python (generated from JS)
- [`php/`](https://github.com/ccxt/ccxt/blob/master/php/) in PHP (generated from JS)
You can also clone it into your project directory from [ccxt GitHub repository](https://github.com/ccxt/ccxt):
```shell
git clone https://github.com/ccxt/ccxt.git
```
### JavaScript (NPM)
JavaScript version of CCXT works in both Node and web browsers. Requires ES6 and `async/await` syntax support (Node 7.6.0+). When compiling with Webpack and Babel, make sure it is [not excluded](https://github.com/ccxt/ccxt/issues/225#issuecomment-331905178) in your `babel-loader` config.
[ccxt in **NPM**](https://www.npmjs.com/package/ccxt)
```shell
npm install ccxt
```
```JavaScript
var ccxt = require ('ccxt')
console.log (ccxt.exchanges) // print all available exchanges
```
### JavaScript (for use with the `<script>` tag):
All-in-one browser bundle (dependencies included), served from a CDN of your choice:
* jsDelivr: https://cdn.jsdelivr.net/npm/ccxt@1.38.76/dist/ccxt.browser.js
* unpkg: https://unpkg.com/ccxt@1.38.76/dist/ccxt.browser.js
CDNs are not updated in real-time and may have delays. Defaulting to the most recent version without specifying the version number is not recommended. Please, keep in mind that we are not responsible for the correct operation of those CDN servers.
```HTML
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ccxt@1.38.76/dist/ccxt.browser.js"></script>
```
Creates a global `ccxt` object:
```JavaScript
console.log (ccxt.exchanges) // print all available exchanges
```
### Python
[ccxt in **PyPI**](https://pypi.python.org/pypi/ccxt)
```shell
pip install ccxt
```
```Python
import ccxt
print(ccxt.exchanges) # print a list of all available exchange classes
```
The library supports concurrent asynchronous mode with asyncio and async/await in Python 3.5.3+
```Python
import ccxt.async_support as ccxt # link against the asynchronous version of ccxt
```
### PHP
[ccxt in PHP with **Packagist/Composer**](https://packagist.org/packages/ccxt/ccxt) (PHP 5.4+)
It requires common PHP modules:
- cURL
- mbstring (using UTF-8 is highly recommended)
- PCRE
- iconv
- gmp (this is a built-in extension as of PHP 7.2+)
```PHP
include "ccxt.php";
var_dump (\ccxt\Exchange::$exchanges); // print a list of all available exchange classes
```
### Docker
You can get CCXT installed in a container along with all the supported languages and dependencies. This may be useful if you want to contribute to CCXT (e.g. run the build scripts and tests — please see the [Contributing](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md) document for the details on that).
Using `docker-compose` (in the cloned CCXT repository):
```shell
docker-compose run --rm ccxt
```
You don't need the Docker image if you're not going to develop CCXT. If you just want to use CCXT – just install it as a regular package into your project.
---
## Documentation
Read the [Manual](https://github.com/ccxt/ccxt/wiki) for more details.
## Usage
### Intro
The CCXT library consists of a public part and a private part. Anyone can use the public part immediately after installation. Public APIs provide unrestricted access to public information for all exchange markets without the need to register a user account or have an API key.
Public APIs include the following:
- market data
- instruments/trading pairs
- price feeds (exchange rates)
- order books
- trade history
- tickers
- OHLC(V) for charting
- other public endpoints
In order to trade with private APIs you need to obtain API keys from an exchange's website. It usually means signing up to the exchange and creating API keys for your account. Some exchanges require personal info or identification. Sometimes verification may be necessary as well. In this case you will need to register yourself, this library will not create accounts or API keys for you. Some exchanges expose API endpoints for registering an account, but most exchanges don't. You will have to sign up and create API keys on their websites.
Private APIs allow the following:
- manage personal account info
- query account balances
- trade by making market and limit orders
- deposit and withdraw fiat and crypto funds
- query personal orders
- get ledger history
- transfer funds between accounts
- use merchant services
This library implements full public and private REST APIs for all exchanges. WebSocket and FIX implementations in JavaScript, PHP, Python are available in [CCXT Pro](https://ccxt.pro), which is a professional addon to CCXT with support for WebSocket streams.
The CCXT library supports both camelcase notation (preferred in JavaScript) and underscore notation (preferred in Python and PHP), therefore all methods can be called in either notation or coding style in any language.
```JavaScript
// both of these notations work in JavaScript/Python/PHP
exchange.methodName () // camelcase pseudocode
exchange.method_name () // underscore pseudocode
```
Read the [Manual](https://github.com/ccxt/ccxt/wiki) for more details.
### JavaScript
```JavaScript
'use strict';
const ccxt = require ('ccxt');
(async function () {
let kraken = new ccxt.kraken ()
let bitfinex = new ccxt.bitfinex ({ verbose: true })
let huobipro = new ccxt.huobipro ()
let okcoinusd = new ccxt.okcoinusd ({
apiKey: 'YOUR_PUBLIC_API_KEY',
secret: 'YOUR_SECRET_PRIVATE_KEY',
})
const exchangeId = 'binance'
, exchangeClass = ccxt[exchangeId]
, exchange = new exchangeClass ({
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_SECRET',
'timeout': 30000,
'enableRateLimit': true,
})
console.log (kraken.id, await kraken.loadMarkets ())
console.log (bitfinex.id, await bitfinex.loadMarkets ())
console.log (huobipro.id, await huobipro.loadMarkets ())
console.log (kraken.id, await kraken.fetchOrderBook (kraken.symbols[0]))
console.log (bitfinex.id, await bitfinex.fetchTicker ('BTC/USD'))
console.log (huobipro.id, await huobipro.fetchTrades ('ETH/USDT'))
console.log (okcoinusd.id, await okcoinusd.fetchBalance ())
// sell 1 BTC/USD for market price, sell a bitcoin for dollars immediately
console.log (okcoinusd.id, await okcoinusd.createMarketSellOrder ('BTC/USD', 1))
// buy 1 BTC/USD for $2500, you pay $2500 and receive ฿1 when the order is closed
console.log (okcoinusd.id, await okcoinusd.createLimitBuyOrder ('BTC/USD', 1, 2500.00))
// pass/redefine custom exchange-specific order params: type, amount, price or whatever
// use a custom order type
bitfinex.createLimitSellOrder ('BTC/USD', 1, 10, { 'type': 'trailing-stop' })
}) ();
```
### Python
```Python
# coding=utf-8
import ccxt
hitbtc = ccxt.hitbtc({'verbose': True})
bitmex = ccxt.bitmex()
huobipro = ccxt.huobipro()
exmo = ccxt.exmo({
'apiKey': 'YOUR_PUBLIC_API_KEY',
'secret': 'YOUR_SECRET_PRIVATE_KEY',
})
kraken = ccxt.kraken({
'apiKey': 'YOUR_PUBLIC_API_KEY',
'secret': 'YOUR_SECRET_PRIVATE_KEY',
})
exchange_id = 'binance'
exchange_class = getattr(ccxt, exchange_id)
exchange = exchange_class({
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_SECRET',
'timeout': 30000,
'enableRateLimit': True,
})
hitbtc_markets = hitbtc.load_markets()
print(hitbtc.id, hitbtc_markets)
print(bitmex.id, bitmex.load_markets())
print(huobipro.id, huobipro.load_markets())
print(hitbtc.fetch_order_book(hitbtc.symbols[0]))
print(bitmex.fetch_ticker('BTC/USD'))
print(huobipro.fetch_trades('LTC/USDT'))
print(exmo.fetch_balance())
# sell one ฿ for market price and receive $ right now
print(exmo.id, exmo.create_market_sell_order('BTC/USD', 1))
# limit buy BTC/EUR, you pay €2500 and receive ฿1 when the order is closed
print(exmo.id, exmo.create_limit_buy_order('BTC/EUR', 1, 2500.00))
# pass/redefine custom exchange-specific order params: type, amount, price, flags, etc...
kraken.create_market_buy_order('BTC/USD', 1, {'trading_agreement': 'agree'})
```
### PHP
```PHP
include 'ccxt.php';
$poloniex = new \ccxt\poloniex ();
$bittrex = new \ccxt\bittrex (array ('verbose' => true));
$quoinex = new \ccxt\quoinex ();
$zaif = new \ccxt\zaif (array (
'apiKey' => 'YOUR_PUBLIC_API_KEY',
'secret' => 'YOUR_SECRET_PRIVATE_KEY',
));
$hitbtc = new \ccxt\hitbtc (array (
'apiKey' => 'YOUR_PUBLIC_API_KEY',
'secret' => 'YOUR_SECRET_PRIVATE_KEY',
));
$exchange_id = 'binance';
$exchange_class = "\\ccxt\\$exchange_id";
$exchange = new $exchange_class (array (
'apiKey' => 'YOUR_API_KEY',
'secret' => 'YOUR_SECRET',
'timeout' => 30000,
'enableRateLimit' => true,
));
$poloniex_markets = $poloniex->load_markets ();
var_dump ($poloniex_markets);
var_dump ($bittrex->load_markets ());
var_dump ($quoinex->load_markets ());
var_dump ($poloniex->fetch_order_book ($poloniex->symbols[0]));
var_dump ($bittrex->fetch_trades ('BTC/USD'));
var_dump ($quoinex->fetch_ticker ('ETH/EUR'));
var_dump ($zaif->fetch_ticker ('BTC/JPY'));
var_dump ($zaif->fetch_balance ());
// sell 1 BTC/JPY for market price, you pay ¥ and receive ฿ immediately
var_dump ($zaif->id, $zaif->create_market_sell_order ('BTC/JPY', 1));
// buy BTC/JPY, you receive ฿1 for ¥285000 when the order closes
var_dump ($zaif->id, $zaif->create_limit_buy_order ('BTC/JPY', 1, 285000));
// set a custom user-defined id to your order
$hitbtc->create_order ('BTC/USD', 'limit', 'buy', 1, 3000, array ('clientOrderId' => '123'));
```
## Contributing
Please read the [CONTRIBUTING](https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md) document before making changes that you would like adopted in the code. Also, read the [Manual](https://github.com/ccxt/ccxt/wiki) for more details.
## Support Developer Team
We are investing a significant amount of time into the development of this library. If CCXT made your life easier and you want to help us improve it further, or if you want to speed up development of new features and exchanges, please support us with a tip. We appreciate all contributions!
### Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
[[Become a sponsor](https://opencollective.com/ccxt#sponsor)]
<a href="https://opencollective.com/ccxt/tiers/sponsor/0/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/1/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/2/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/3/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/4/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/5/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/6/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/7/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/8/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/sponsor/9/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/sponsor/9/avatar.svg"></a>
### Supporters
Support this project by becoming a supporter. Your avatar will show up here with a link to your website.
[[Become a supporter](https://opencollective.com/ccxt#supporter)]
<a href="https://opencollective.com/ccxt/tiers/supporter/0/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/0/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/1/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/1/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/2/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/2/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/3/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/3/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/4/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/4/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/5/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/5/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/6/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/6/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/7/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/7/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/8/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/8/avatar.svg"></a>
<a href="https://opencollective.com/ccxt/tiers/supporter/9/website" target="_blank"><img src="https://opencollective.com/ccxt/tiers/supporter/9/avatar.svg"></a>
### Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/ccxt#backer)]
<a href="https://opencollective.com/ccxt#backers" target="_blank"><img src="https://opencollective.com/ccxt/tiers/backer.svg?width=890"></a>
### Crypto
```
ETH 0x26a3CB49578F07000575405a57888681249c35Fd (ETH only)
BTC 33RmVRfhK2WZVQR1R83h2e9yXoqRNDvJva
BCH 1GN9p233TvNcNQFthCgfiHUnj5JRKEc2Ze
LTC LgM5hkvPzytEFfQGGDFGKYkE743jwUA7qv
```
Thank you!
## Social
- [Follow us on Twitter](https://twitter.com/ccxt_official)
- [Read our blog on Medium](https://medium.com/@ccxt)
- <sub><sub>[![Discord](https://img.shields.io/discord/690203284119617602?logo=discord&logoColor=white)](https://discord.gg/dhzSKYU)</sub></sub>
## Contact Us
For business inquiries: info@ccxt.trade
Loaded 100 of 930 files, more files were not shown because too many files have changed in this diff. Show more