Merge pull request #4 from AAVikings/master

Superalgos node v1
This commit is contained in:
benme authored and GitHub committed 2019-06-22 19:26:15 -03:00
commit 187c58c0ef
28 files changed
+5616 -231

No files matched your search

+16 -1
View File
@@ -1,6 +1,21 @@
NODE_ENV=production
PORT=4011
PORT=4004
# LOG_LEVEL=info
MONGO_URL=mongodb://mongo/saweb
GATEWAY_ENDPOINT=http://master-app-api:4100/graphql
PRESHARED_GATEWAY_KEY=saweb_key
# The NODE_ENDPOINT is used at team creation and is where other components will search for the team
NODE_ENDPOINT=http://docker.for.win.localhost:4100/
# Storage Configuration
STORAGE_PROVIDER=Minio
# Minio
MINIO_END_POINT=minio
MINIO_PORT=9000
MINIO_USE_SSL=false
MINIO_ACCESS_KEY=minio
MINIO_SECRET_KEY=minio123
+1101 -25
View File
File diff suppressed because it is too large. Load diff
+7 -3
View File
@@ -13,19 +13,23 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"express": "^4.16.4"
"express": "^4.16.4",
"chai": "^4.2.0",
"mocha": "^6.1.4"
},
"dependencies": {
"@azure/storage-blob": "^10.3.0",
"apollo-server-express": "2.1.0",
"axios": "0.18.0",
"axios": "0.19.0",
"azure-storage": "^2.10.3",
"dotenv": "6.0.0",
"dotenv": "8.0.0",
"esm": "^3.2.22",
"express": "4.16.3",
"graphql": "0.13.2",
"json-beautify": "^1.1.0",
"minio": "^7.0.8",
"mongoose": "5.3.4",
"redis": "^2.8.0",
"winston": "3.2.1"
}
}
+4
View File
@@ -5,9 +5,13 @@ import schema from './schema';
import wrongPreshared from './errors/notAllowed.json';
import db from './db';
import logger from './utils/logger'
import bodyParser from 'body-parser'
const app = express();
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.post('/graphql', (req, res, next) => {
if (req.headers.preshared === process.env.PRESHARED_GATEWAY_KEY) {
next();
+12 -12
View File
@@ -5,10 +5,10 @@
"codeName": "AAMasters",
"displayName": "Masters",
"host": {
"url": "https://platform-api-dev.superalgos.org/",
"storage": "https://aadevelop.blob.core.windows.net",
"url": "https://demo-api-dev.superalgos.org/",
"storage": "localStorage",
"container": "aamasters",
"accessKey": "?st=2019-05-21T12%3A00%3A00Z&se=2020-05-22T12%3A00%3A00Z&sp=rl&sv=2018-03-28&sr=c&sig=PsGM67klPfLjdfjymziEQEQcD5Wzy3SMcZWsTCQrhd8%3D"
"accessKey": ""
},
"bots": [
{
@@ -725,10 +725,10 @@
"codeName": "AAVikings",
"displayName": "Vikings",
"host": {
"url": "https://platform-api-dev.superalgos.org/",
"url": "https://demo-api-dev.superalgos.org/",
"container": "aavikings",
"storage": "https://aadevelop.blob.core.windows.net",
"accessKey": "?st=2019-05-20T12%3A00%3A00Z&se=2020-05-21T12%3A00%3A00Z&sp=rl&sv=2018-03-28&sr=c&sig=hJuV9aEP4KVZo5z54FEF1BQtkbksfwkTJFv1FYkHFRg%3D"
"storage": "localStorage",
"accessKey": ""
},
"bots": [
{
@@ -832,10 +832,10 @@
"codeName": "AAMasters",
"displayName": "AA Arena",
"host": {
"url": "https://platform-api-dev.superalgos.org/",
"storage": "https://aadevelop.blob.core.windows.net",
"url": "https://demo-api-dev.superalgos.org/",
"storage": "localStorage",
"container": "aaplatform",
"accessKey": "?sv=2018-03-28&ss=b&srt=co&sp=rw&se=2020-01-04T00:00:00Z&st=2019-01-08T11:25:04Z&spr=https&sig=xZU1PO%2BdtVilqlZp%2BVRWW55LYl8oGZU7x4JnP%2BhMc%2FY%3D"
"accessKey": ""
},
"competitions": [
{
@@ -849,10 +849,10 @@
"devTeam": "AAMasters",
"codeName": "PlottersROI",
"host": {
"url": "https://platform-api-dev.superalgos.org/",
"storage": "https://aadevelop.blob.core.windows.net",
"url": "https://demo-api-dev.superalgos.org/",
"storage": "localStorage",
"container": "aamasters",
"accessKey": "?st=2019-05-21T12%3A00%3A00Z&se=2020-05-22T12%3A00%3A00Z&sp=rl&sv=2018-03-28&sr=c&sig=PsGM67klPfLjdfjymziEQEQcD5Wzy3SMcZWsTCQrhd8%3D"
"accessKey": ""
},
"moduleName": "CombinedProfits",
"repo": "Plotters-ROI"
+3 -3
View File
@@ -30,7 +30,7 @@ const ecosystemSchema = new Schema({
},
accessKey: {
type: String,
required: true
required: false
}
},
bots: [{
@@ -200,7 +200,7 @@ const ecosystemSchema = new Schema({
},
accessKey: {
type: String,
required: true
required: false
}
},
competitions: [{
@@ -252,7 +252,7 @@ const ecosystemSchema = new Schema({
},
accessKey: {
type: String,
required: true
required: false
}
},
repo: {
+14 -13
View File
@@ -3,7 +3,9 @@ import { AuthenticationError, WrongArgumentsError } from '../../errors'
import { TeamType } from '../types'
import { Ecosystem } from '../../models'
import { TeamInputType } from '../types/input'
import { copyBots } from '../../storage/CopyBots'
import { copyBot } from '../../storage/CopyBot'
import { copySimulator } from '../../storage/CopySimulator'
import { createContainer, writeFileContent } from '../../storage/providers/MinioStorage'
export const args = { team: { type: TeamInputType } }
@@ -34,23 +36,22 @@ const resolve = async (parent, { team }, context) => {
}
}
let storage = require ('../../storage/providers/AzureStorage') // Default value
if (process.env.STORAGE_PROVIDER === 'Minio') {
storage = require ('../../storage/providers/MinioStorage')
}
// Teams will be created on a local minio container
let botsConfigurations = []
await createContainer(team.codeName)
let tradingBot = await copyBot(team.codeName, team.bot.codeName, team.bot.displayName, writeFileContent)
botsConfigurations.push(tradingBot)
let simulatorBot = await copySimulator(team.codeName, team.bot.codeName, team.bot.displayName, writeFileContent)
botsConfigurations.push(simulatorBot)
let containerName = team.codeName
await storage.createContainer(containerName)
let accessKey = storage.createPrivateAccessKey(containerName, "WRITE", 365)
let botsConfigurations = await copyBots(storage, team)
team.bots = botsConfigurations
if (!team.host) {
let nodeEndpoint = {
url: process.env.SUPERALGOS_NODE_ENDPOINT,
storage: process.env.STORAGE_URL,
url: process.env.NODE_ENDPOINT,
storage: 'localStorage',
container: team.codeName,
accessKey: accessKey,
accessKey: '',
}
team.host = nodeEndpoint
}
+28
View File
@@ -0,0 +1,28 @@
import logger from '../../utils/logger'
import { FileInputType } from '../types/input'
import { GraphQLString } from 'graphql'
import { writeFileContent } from '../../storage/providers/MinioStorage'
export const args = { file: { type: FileInputType } }
const resolve = async (parent, { file }, context) => {
logger.debug('createFile -> Entering Function.')
try {
//TODO Add permissions
await writeFileContent(file.container, file.filePath, file.fileContent)
return 'File created.'
} catch (err) {
logger.error('createFile -> Error: %s', err.message)
throw err
}
}
const mutation = {
createFile: {
type: GraphQLString,
args,
resolve,
}
}
export default mutation;
+2 -2
View File
@@ -3,7 +3,7 @@ import { AuthenticationError, WrongArgumentsError } from '../../errors'
import { TeamType } from '../types'
import { Ecosystem } from '../../models'
import { DeleteTeamInputType } from '../types/input'
import { deleteTeam } from '../../storage/DeleteTeam'
import { deleteContainer } from '../../storage/providers/MinioStorage'
export const args = { team: { type: DeleteTeamInputType } }
@@ -30,7 +30,7 @@ const resolve = async (parent, { team }, context) => {
if (auxTeam.codeName === team.codeName) {
logger.debug('deleteTeam -> Team found, removing.')
teamFound = true
await deleteTeam(team)
await deleteContainer(team.codeName)
userEcosystem.devTeams.splice(i, 1)
await userEcosystem.save()
break
+9 -7
View File
@@ -1,16 +1,18 @@
import {
GraphQLObjectType,
} from 'graphql';
} from 'graphql'
import AddTeamMutation from './AddTeam';
import DeleteTeamMutation from './DeleteTeam';
import AddTeamMutation from './AddTeam'
import DeleteTeamMutation from './DeleteTeam'
import CreateFileMutation from './CreateFile'
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: Object.assign(
AddTeamMutation,
DeleteTeamMutation
),
});
DeleteTeamMutation,
CreateFileMutation
)
})
export default Mutation;
export default Mutation
+9 -20
View File
@@ -1,6 +1,8 @@
import logger from '../../utils/logger'
import { FileInputType } from '../types/input'
import { GraphQLString } from 'graphql'
import { getFileContent } from '../../storage/providers/MinioStorage'
import { getFileContentRemote } from '../../storage/providers/AzureStorage'
export const args = { file: { type: FileInputType } }
@@ -9,31 +11,18 @@ const resolve = async (parent, { file }, context) => {
try {
let storage = require('../../storage/providers/AzureStorage') // Default value
if (process.env.STORAGE_PROVIDER === 'Minio') {
storage = require('../storage/providers/MinioStorage')
let fileContent
if (file.storage === 'localStorage') {
//TODO Add permissions
fileContent = await getFileContent(file.container, file.filePath)
} else {
fileContent = await getFileContentRemote(file.container, file.filePath, file.storage, file.accessKey)
}
//Temporary fix for plotter and plotter pannels
if (file.storage === '' && file.accessKey === ''){
let defaultEcosystem = require('../../config/ecosystem.json')
for (let index = 0; index < defaultEcosystem.devTeams.length; index++) {
const devTeam = defaultEcosystem.devTeams[index];
if(devTeam.codeName.toLowerCase() === file.container){
file.storage = devTeam.host.storage
file.accessKey = devTeam.host.accessKey
}
}
}
if (file.storage === '' || file.accessKey === ''){
throw "Wrong parameters."
}
let fileContent = await storage.getFileContentRemote(file.container, file.filePath, file.storage, file.accessKey)
return fileContent
} catch (err) {
logger.error('getFileContent -> Error: %s', err.stack)
logger.error('getFileContent -> Error: %s', err.message)
throw err
}
}
+53
View File
@@ -0,0 +1,53 @@
import logger from '../../utils/logger'
import { FileInputType } from '../types/input'
import { GraphQLString } from 'graphql'
import { getFileContent } from '../../storage/providers/MinioStorage'
import { getFileContentRemote } from '../../storage/providers/AzureStorage'
export const args = { file: { type: FileInputType } }
const resolve = async (parent, { file }, context) => {
logger.debug('getPlotterCode -> Entering Function: ' + file.filePath)
try {
let fileContent
if (file.storage === '' || file.storage === undefined) {
//TODO Add permissions
fileContent = await getFileContent(file.container, file.filePath)
} else {
// TODO Temporary fix for plotter and plotter pannels
if (file.storage === '' && file.accessKey === '') {
let defaultEcosystem = require('../../config/ecosystem.json')
for (let index = 0; index < defaultEcosystem.devTeams.length; index++) {
const devTeam = defaultEcosystem.devTeams[index];
if (devTeam.codeName.toLowerCase() === file.container) {
file.storage = devTeam.host.storage
file.accessKey = devTeam.host.accessKey
}
}
}
if (file.storage === '' || file.accessKey === '') {
throw "Wrong parameters."
}
fileContent = await getFileContentRemote(file.container, file.filePath, file.storage, file.accessKey)
}
return fileContent
} catch (err) {
logger.error('getPlotterCode -> Error: %s', err)
logger.error('getPlotterCode -> Stack: %s', err.stack)
throw err
}
}
const query = {
plotterCode: {
type: GraphQLString,
args,
resolve,
}
}
export default query
+3 -1
View File
@@ -4,12 +4,14 @@ import {
import EcosystemQuery from './Ecosystem';
import FileContentQuery from './FileContent';
import PlotterCodeQuery from './PlotterCode';
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: Object.assign(
EcosystemQuery,
FileContentQuery
FileContentQuery,
PlotterCodeQuery
),
});
+1 -1
View File
@@ -11,7 +11,7 @@ const Type = new GraphQLInputObjectType({
container: { type: new GraphQLNonNull(GraphQLString) },
filePath: { type: new GraphQLNonNull(GraphQLString) },
storage: { type: new GraphQLNonNull(GraphQLString) },
accessKey: { type: new GraphQLNonNull(GraphQLString) },
accessKey: { type: GraphQLString },
fileContent: { type: GraphQLString }
}),
});
+8 -27
View File
@@ -1,14 +1,13 @@
import beautify from 'json-beautify'
import logger from '../utils/logger'
import readFile from '../utils/readFile'
export const copyBot = async (storage, teamCodeName, botCodeName, botDisplayName) => {
export const copyBot = async (teamCodeName, botCodeName, botDisplayName, writeFileContent) => {
try {
logger.debug('copyBot started for bot %s', botCodeName)
let containerName = 'aavikings'
let templatePath = 'AAVikings/bots/blumblebee-Trading-Bot'
let config = await storage.getFileContent(containerName, templatePath + '/this.bot.config.json')
let code = await storage.getFileContent(containerName, templatePath + '/Trading-Process/User.Bot.js')
// File imports
const botCode = await readFile('./src/storage/templates/TradingBot/User.Bot.js')
const config = await readFile('./src/storage/templates/TradingBot/this.bot.config.json')
// Change config to adapt the new indicator
let parsedConfig = JSON.parse(config)
@@ -29,32 +28,14 @@ export const copyBot = async (storage, teamCodeName, botCodeName, botDisplayName
parsedConfig.processes[0].dataDependencies[2].devTeam = teamCodeName
parsedConfig.processes[0].dataDependencies[2].bot = 'simulator-' + botCodeName
// Write the new files
// Creating a new trading bot
let newBotPath = teamCodeName + '/bots/' + botCodeName + '-Trading-Bot'
await storage.writeFileContent(teamCodeName, newBotPath + '/this.bot.config.json', beautify(parsedConfig, null, 2, 80))
await storage.writeFileContent(teamCodeName, newBotPath + '/Trading-Bot/User.Bot.js', code)
await writeFileContent(teamCodeName, newBotPath + '/Trading-Process/User.Bot.js', botCode)
await writeFileContent(teamCodeName, newBotPath + '/this.bot.config.json', beautify(parsedConfig, null, 2, 80))
logger.debug('copyBot completed for bot %s', botCodeName)
return parsedConfig
} catch (err) {
logger.error('copyBot error for bot: %s. %s', botCodeName, err)
throw err
}
}
export const removeBot = async (storage, teamCodeName, botCodeName) => {
try {
logger.debug('removeBot started for bot %s', botCodeName)
// Delete bot code
let newBotPath = teamCodeName + '/bots/' + 'bot-' + botCodeName
await storage.deleteBlob(teamCodeName, newBotPath + '/this.bot.config.json')
await storage.deleteBlob(teamCodeName, newBotPath + '/Trading-Bot/User.Bot.js')
logger.debug('removeBot completed for bot %s', botCodeName)
} catch (err) {
logger.error('copyBot error for bot %s: %s', botCodeName, err)
throw err
}
}
-22
View File
@@ -1,22 +0,0 @@
import logger from '../utils/logger'
import { copyBot } from './CopyBot'
import { copySimulator } from './CopySimulator'
export const copyBots = async (storage, team) => {
try {
logger.debug('copyBots started for team %s', team.codeName)
let bots = []
let tradingBot = await copyBot(storage, team.codeName, team.bot.codeName, team.bot.displayName)
bots.push(tradingBot)
let simulatorBot = await copySimulator(storage, team.codeName, team.bot.codeName, team.bot.displayName)
bots.push(simulatorBot)
return bots
} catch (err) {
logger.error('copyBots error for teamCodeName: %s, error: %s', team.bot.codeName, err.stack)
throw err
}
}
+12 -12
View File
@@ -1,16 +1,16 @@
import beautify from 'json-beautify'
import logger from '../utils/logger'
import readFile from '../utils/readFile'
export const copySimulator = async (storage, teamCodeName, botCodeName, botDisplayName) => {
export const copySimulator = async (teamCodeName, botCodeName, botDisplayName, writeFileContent) => {
try {
logger.debug('copySimulator started for bot %s', 'simulator-' + botCodeName)
let containerName = 'aamasters'
let templatePath = 'AAMasters/bots/AAJason-Indicator-Bot'
let commons = await storage.getFileContent(containerName, templatePath + '/Commons.js')
let config = await storage.getFileContent(containerName, templatePath + '/this.bot.config.json')
let codeDaily = await storage.getFileContent(containerName, templatePath + '/Multi-Period-Daily/User.Bot.js')
let codeMarket = await storage.getFileContent(containerName, templatePath + '/Multi-Period-Market/User.Bot.js')
// File imports
const commons = await readFile('./src/storage/templates/Simulator/Commons.js')
const config = await readFile('./src/storage/templates/Simulator/this.bot.config.json')
const codeDaily = await readFile('./src/storage/templates/Simulator/User.Bot.Daily.js')
const codeMarket = await readFile('./src/storage/templates/Simulator/User.Bot.Market.js')
// Change config to adapt the new indicator
let newBotCodeName = 'simulator-' + botCodeName
@@ -47,12 +47,12 @@ export const copySimulator = async (storage, teamCodeName, botCodeName, botDispl
parsedConfig.products[3].dataSets[1].dataRange.filePath = teamCodeName + '/' + parsedConfig.codeName + '.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Trades/Multi-Period-Daily'
// Write the new files
let newSimulatorName = 'simulator-' + newBotCodeName + '-Indicator-Bot'
let newSimulatorName = newBotCodeName + '-Indicator-Bot'
let newSimulatorPath = teamCodeName + '/bots/' + newSimulatorName
await storage.writeFileContent(teamCodeName, newSimulatorPath + '/Commons.js', commons)
await storage.writeFileContent(teamCodeName, newSimulatorPath + '/this.bot.config.json', beautify(parsedConfig, null, 2, 80))
await storage.writeFileContent(teamCodeName, newSimulatorPath + '/Multi-Period-Daily/User.Bot.js', codeDaily)
await storage.writeFileContent(teamCodeName, newSimulatorPath + '/Multi-Period-Market/User.Bot.js', codeMarket)
await writeFileContent(teamCodeName, newSimulatorPath + '/Commons.js', commons)
await writeFileContent(teamCodeName, newSimulatorPath + '/this.bot.config.json', beautify(parsedConfig, null, 2, 80))
await writeFileContent(teamCodeName, newSimulatorPath + '/Multi-Period-Daily/User.Bot.js', codeDaily)
await writeFileContent(teamCodeName, newSimulatorPath + '/Multi-Period-Market/User.Bot.js', codeMarket)
logger.debug('copySimulator completed for bot %s', newBotCodeName)
return parsedConfig
-18
View File
@@ -1,18 +0,0 @@
import logger from '../utils/logger'
export const deleteTeam = async (team) => {
try {
logger.debug('deleteTeam started for team %s', team.codeName)
let storage = require ('./providers/AzureStorage') // Default value
if (process.env.STORAGE_PROVIDER === 'Minio') {
storage = require ('./providers/MinioStorage')
}
await storage.deleteContainer(team.codeName)
} catch (err) {
logger.error('deleteTeam error for teamCodeName: %s, error: %s', team.codeName, err.stack)
throw err
}
}
+16 -64
View File
@@ -1,4 +1,3 @@
import logger from '../../utils/logger'
import {
Aborter,
BlobURL,
@@ -10,9 +9,9 @@ import {
AnonymousCredential
} from '@azure/storage-blob'
const AzureServiceURL = () => {
const getService = () => {
// Use SharedKeyCredential with storage account and account key
const sharedKeyCredential = new SharedKeyCredential(process.env.STORAGE_BASE_URL, process.env.STORAGE_CONNECTION_STRING);
const sharedKeyCredential = new SharedKeyCredential(process.env.AZURE_BASE_URL, process.env.AZURE_CONNECTION_STRING);
// Use sharedKeyCredential, tokenCredential or anonymousCredential to create a pipeline
const pipeline = StorageURL.newPipeline(sharedKeyCredential, {
@@ -22,18 +21,16 @@ const AzureServiceURL = () => {
// List containers
const serviceURL = new ServiceURL(
// When using AnonymousCredential, following url should include a valid SAS or support public access
`https://${process.env.STORAGE_BASE_URL}.blob.core.windows.net`,
`https://${process.env.AZURE_BASE_URL}.blob.core.windows.net`,
pipeline
)
return serviceURL
}
const serviceURL = AzureServiceURL()
export const getFileContentRemote = async (containerName, blobName, storage, accessKey) => {
const pipeline = StorageURL.newPipeline(new AnonymousCredential(), {
retryOptions: { maxTries: 10 }
});
})
const serviceURLWithAccessKey = new ServiceURL(
`${storage}${accessKey}`,
pipeline
@@ -45,14 +42,14 @@ export const getFileContentRemote = async (containerName, blobName, storage, acc
}
export const getFileContent = async (containerName, blobName) => {
const containerURL = ContainerURL.fromServiceURL(serviceURL, containerName)
const containerURL = ContainerURL.fromServiceURL(getService(), containerName)
const blockBlobURL = BlockBlobURL.fromContainerURL(containerURL, blobName)
let downloadResponse = await blockBlobURL.download(Aborter.none, 0)
return await streamToString(downloadResponse.readableStreamBody)
}
export const writeFileContent = async (containerName, blobName, content) => {
const containerURL = ContainerURL.fromServiceURL(serviceURL, containerName)
const containerURL = ContainerURL.fromServiceURL(getService(), containerName)
const blobURL = BlobURL.fromContainerURL(containerURL, blobName)
const blockBlobURL = BlockBlobURL.fromBlobURL(blobURL)
await blockBlobURL.upload(
@@ -63,77 +60,32 @@ export const writeFileContent = async (containerName, blobName, content) => {
}
export const deleteBlob = async (containerName, blobName) => {
try {
const containerURL = ContainerURL.fromServiceURL(serviceURL, containerName)
const blockBlobURL = BlockBlobURL.fromContainerURL(containerURL, blobName)
await blockBlobURL.delete(Aborter.none)
} catch (err) {
logger.warn('Error deleting the file: %s%s', containerName, blobName)
}
const containerURL = ContainerURL.fromServiceURL(getService(), containerName)
const blockBlobURL = BlockBlobURL.fromContainerURL(containerURL, blobName)
await blockBlobURL.delete(Aborter.none)
}
export const deleteContainer = async (containerName) => {
const containerURL = ContainerURL.fromServiceURL(serviceURL, containerName)
const containerURL = ContainerURL.fromServiceURL(getService(), containerName)
await containerURL.delete(Aborter.none)
logger.debug('deleteContainer -> Successful.')
}
export const createContainer = async (containerName) => {
const containerURL = ContainerURL.fromServiceURL(serviceURL, containerName)
const containerURL = ContainerURL.fromServiceURL(getService(), containerName)
await containerURL.create(Aborter.none)
logger.debug('createContainer -> Successful.')
}
// A helper method used to read a Node.js readable stream into string
async function streamToString(readableStream) {
return new Promise((resolve, reject) => {
const chunks = [];
const chunks = []
readableStream.on('data', data => {
chunks.push(data.toString());
});
})
readableStream.on('end', () => {
resolve(chunks.join(''));
});
resolve(chunks.join(''))
})
readableStream.on('error', reject)
});
})
}
export const createPrivateAccessKey = (container, type, days) => {
let azure = require('azure-storage');
let blobService = azure.createBlobService(process.env.AZURE_STORAGE_CONNECTION_STRING);
let startDate = new Date();
startDate.setDate(startDate.getDate() - 1);
let expiryDate = new Date(startDate);
expiryDate.setDate(startDate.getDate() + days);
let azurePermissions;
switch (type) {
case 'READ': {
azurePermissions = azure.BlobUtilities.SharedAccessPermissions.READ;
break;
}
case 'WRITE': {
azurePermissions = azure.BlobUtilities.SharedAccessPermissions.WRITE;
break;
}
case 'DELETE': {
azurePermissions = azure.BlobUtilities.SharedAccessPermissions.DELETE;
break;
}
}
let blobName = undefined;
let sharedAccessPolicy = {
AccessPolicy: {
Permissions: azurePermissions,
Start: startDate,
Expiry: expiryDate
}
};
return blobService.generateSharedAccessSignature(container, blobName, sharedAccessPolicy);
}
+99
View File
@@ -0,0 +1,99 @@
import { Client } from 'minio'
const getService = () => {
let service = new Client({
endPoint: process.env.MINIO_END_POINT,
port: JSON.parse(process.env.MINIO_PORT),
useSSL: JSON.parse(process.env.MINIO_USE_SSL),
accessKey: process.env.MINIO_ACCESS_KEY,
secretKey: process.env.MINIO_SECRET_KEY
})
return service
}
export const getFileContent = async (containerName, blobName) => {
let service = getService()
let chunks = []
let fileContent = await new Promise(function (resolve, reject) {
service.getObject(containerName, blobName, function (error, dataStream) {
if (error) {
reject(error)
}
if (dataStream != undefined) {
dataStream.on('data', function (chunk) {
chunks.push(chunk.toString())
})
dataStream.on('end', function () {
resolve(chunks.join(''))
})
dataStream.on('error', function (e) {
reject(e)
})
} else {
resolve('')
}
})
})
return fileContent
}
export const writeFileContent = async (containerName, blobName, content) => {
let service = getService()
await new Promise(function (resolve, reject) {
service.putObject(containerName, blobName, content, 'text/plain', function (error) {
if (error) {
reject(error)
}
resolve()
})
})
}
export const createContainer = async (containerName) => {
let service = getService()
await new Promise(function (resolve, reject) {
service.makeBucket(containerName, '', function (error) {
if (error) {
reject(error)
}
resolve()
})
})
}
export const deleteContainer = async (containerName) => {
let service = getService()
let objectsStream = service.listObjects(containerName, '', true)
let totalObjects = 0
let removed = 0
objectsStream.on('data', async function (obj) {
totalObjects++
await new Promise(function (resolve, reject) {
service.removeObject(containerName, obj.name, function (e) {
if (e) reject(e)
removed++
resolve()
})
})
})
objectsStream.on('error', function (e) {
throw e
})
objectsStream.on('end', async function (e) {
await removeBucket()
})
function removeBucket() {
if (totalObjects === removed) {
new Promise(function (resolve, reject) {
service.removeBucket(containerName, function (e) {
if (e) reject(e)
resolve()
})
})
} else {
setTimeout(removeBucket, 2000)
}
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,472 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const MODULE_NAME = "User Bot";
const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation";
const CONDITIONS_FOLDER_NAME = "Simulation-Conditions";
const STRATEGIES_FOLDER_NAME = "Simulation-Strategies";
const TRADES_FOLDER_NAME = "Simulation-Trades";
const commons = COMMONS.newCommons(bot, logger, UTILITIES);
thisObject = {
initialize: initialize,
start: start
};
let utilities = UTILITIES.newCloudUtilities(bot, logger);
let dataDependencies;
return thisObject;
function initialize(pDataDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
dataDependencies = pDataDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function start(dataFiles, timePeriod, outputPeriodLabel, currentDay, startDate, endDate, interExecutionMemory, callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = global.MARKET;
let dataFile;
let recordsArray = [];
let conditionsArray = [];
let strategiesArray = [];
let tradesArray = [];
let tradingSystem = {};
let lastObjectsArray = [];
commons.initializeData();
for (let i = 0; i < dataDependencies.config.length; i++) {
let dependency = dataDependencies.config[i];
dataFile = dataFiles[i];
switch (i) {
case 0: {
commons.buildLRC(dataFile, callBackFunction);
break;
}
case 1: {
commons.buildPercentageBandwidthMap(dataFile, callBackFunction);
break;
}
case 2: {
commons.buildBollingerBandsMap(dataFile, callBackFunction);
break;
}
case 3: {
commons.buildBollingerChannelsArray(dataFile, callBackFunction);
break;
}
case 4: {
commons.buildBollingerSubChannelsArray(dataFile, callBackFunction);
break;
}
case 5: {
commons.buildCandles(dataFile, callBackFunction);
break;
}
}
}
commons.runSimulation(
recordsArray,
conditionsArray,
strategiesArray,
tradesArray,
lastObjectsArray,
timePeriod,
currentDay,
startDate,
endDate,
interExecutionMemory,
writeFiles)
function writeFiles(pTradingSystem) {
tradingSystem = pTradingSystem
writeRecordsFile()
}
function writeRecordsFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < recordsArray.length; i++) {
let record = recordsArray[i];
/* Will only add to the file the records of the current day */
if (record.begin < currentDay.valueOf()) { continue; }
fileContent = fileContent + separator + '[' +
record.begin + "," +
record.end + "," +
record.type + "," +
record.marketRate + "," +
record.amount + "," +
record.balanceA + "," +
record.balanceB + "," +
record.profit + "," +
record.lastProfit + "," +
record.stopLoss + "," +
record.roundtrips + "," +
record.hits + "," +
record.fails + "," +
record.hitRatio + "," +
record.ROI + "," +
record.periods + "," +
record.days + "," +
record.anualizedRateOfReturn + "," +
record.positionRate + "," +
record.lastProfitPercent + "," +
record.strategy + "," +
record.strategyStageNumber + "," +
record.takeProfit + "," +
record.stopLossPhase + "," +
record.takeProfitPhase + "," +
JSON.stringify(record.orderRecord) + "," +
record.positionSize + "," +
record.initialBalanceA + "," +
record.minimunBalanceA + "," +
record.maximunBalanceA + "," +
record.initialBalanceB + "," +
record.minimunBalanceB + "," +
record.maximunBalanceB + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
writeConditionsFile();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeConditionsFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < conditionsArray.length; i++) {
let record = conditionsArray[i];
/* Will only add to the file the records of the current day */
if (record.begin < currentDay.valueOf()) { continue; }
let conditions = "";
let conditionsSeparator = "";
/* Will only add to the file the records of the current day */
if (record.begin < currentDay.valueOf()) { continue; }
for (let j = 0; j < record.length - 3; j++) {
conditions = conditions + conditionsSeparator + record[j];
if (conditionsSeparator === "") { conditionsSeparator = ","; }
}
conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values.
conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors.
conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues.
fileContent = fileContent + separator + '[' + conditions + ']';
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
fileContent = "[" + JSON.stringify(tradingSystem) + "," + JSON.stringify(lastObjectsArray) + "," + fileContent + "]";
let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
writeStrategiesFile();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStrategiesFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < strategiesArray.length; i++) {
let record = strategiesArray[i];
/* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends
at the current date. */
if (record.end < currentDay.valueOf()) { continue; }
fileContent = fileContent + separator + '[' +
record.begin + "," +
record.end + "," +
record.status + "," +
record.number + "," +
record.beginRate + "," +
record.endRate + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
writeTradesFile();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeTradesFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < tradesArray.length; i++) {
let record = tradesArray[i];
/* Will only add to the file the records of the current day. In this case since objects can span more than one day, we add all of the objects that ends
at the current date. */
if (record.end < currentDay.valueOf()) { continue; }
fileContent = fileContent + separator + '[' +
record.begin + "," +
record.end + "," +
record.status + "," +
record.lastProfitPercent + "," +
record.beginRate + "," +
record.endRate + "," +
record.exitType + "," +
record.stopRate + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let dateForPath = currentDay.getUTCFullYear() + '/' + utilities.pad(currentDay.getUTCMonth() + 1, 2) + '/' + utilities.pad(currentDay.getUTCDate(), 2);
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Daily" + "/" + outputPeriodLabel + "/" + dateForPath;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -0,0 +1,442 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileStorage) {
const FULL_LOG = true;
const LOG_FILE_CONTENT = false;
const MODULE_NAME = "User Bot";
const SIMULATED_RECORDS_FOLDER_NAME = "Trading-Simulation";
const CONDITIONS_FOLDER_NAME = "Simulation-Conditions";
const STRATEGIES_FOLDER_NAME = "Simulation-Strategies";
const TRADES_FOLDER_NAME = "Simulation-Trades";
const commons = COMMONS.newCommons(bot, logger, UTILITIES);
thisObject = {
initialize: initialize,
start: start
};
let dataDependencies;
return thisObject;
function initialize(pDataDependencies, callBackFunction) {
try {
logger.fileName = MODULE_NAME;
logger.initialize();
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); }
dataDependencies = pDataDependencies;
callBackFunction(global.DEFAULT_OK_RESPONSE);
} catch (err) {
logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function start(dataFiles, timePeriod, outputPeriodLabel, startDate, endDate, callBackFunction) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> Entering function."); }
let market = global.MARKET;
let dataFile;
let recordsArray = [];
let conditionsArray = [];
let strategiesArray = [];
let tradesArray = [];
let lastObjectsArray = [];
let tradingSystem = {};
let interExecutionMemory = {};
let currentDay = bot.processDatetime;
commons.initializeData();
for (let i = 0; i < dataDependencies.config.length; i++) {
let dependency = dataDependencies.config[i];
dataFile = dataFiles[i];
switch (i) {
case 0: {
commons.buildLRC(dataFile, callBackFunction);
break;
}
case 1: {
commons.buildPercentageBandwidthMap(dataFile, callBackFunction);
break;
}
case 2: {
commons.buildBollingerBandsMap(dataFile, callBackFunction);
break;
}
case 3: {
commons.buildBollingerChannelsArray(dataFile, callBackFunction);
break;
}
case 4: {
commons.buildBollingerSubChannelsArray(dataFile, callBackFunction);
break;
}
case 5: {
commons.buildCandles(dataFile, callBackFunction);
break;
}
}
}
commons.runSimulation(
recordsArray,
conditionsArray,
strategiesArray,
tradesArray,
lastObjectsArray,
timePeriod,
currentDay,
startDate,
endDate,
interExecutionMemory,
writeFiles)
function writeFiles(pTradingSystem) {
tradingSystem = pTradingSystem
writeRecordsFile()
}
function writeRecordsFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < recordsArray.length; i++) {
let record = recordsArray[i];
fileContent = fileContent + separator + '[' +
record.begin + "," +
record.end + "," +
record.type + "," +
record.marketRate + "," +
record.amount + "," +
record.balanceA + "," +
record.balanceB + "," +
record.profit + "," +
record.lastProfit + "," +
record.stopLoss + "," +
record.roundtrips + "," +
record.hits + "," +
record.fails + "," +
record.hitRatio + "," +
record.ROI + "," +
record.periods + "," +
record.days + "," +
record.anualizedRateOfReturn + "," +
record.positionRate + "," +
record.lastProfitPercent + "," +
record.strategy + "," +
record.strategyStageNumber + "," +
record.takeProfit + "," +
record.stopLossPhase + "," +
record.takeProfitPhase + "," +
JSON.stringify(record.orderRecord) + "," +
record.positionSize + "," +
record.initialBalanceA + "," +
record.minimunBalanceA + "," +
record.maximunBalanceA + "," +
record.initialBalanceB + "," +
record.minimunBalanceB + "," +
record.maximunBalanceB + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + SIMULATED_RECORDS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeRecordsFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
writeConditionsFile();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeRecordsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeConditionsFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < conditionsArray.length; i++) {
let record = conditionsArray[i];
let conditions = "";
let conditionsSeparator = "";
for (let j = 0; j < record.length - 3; j++) {
conditions = conditions + conditionsSeparator + record[j];
if (conditionsSeparator === "") { conditionsSeparator = ","; }
}
conditions = conditions + conditionsSeparator + '[' + record[record.length - 3] + ']'; // The last item contains an Array of condition values.
conditions = conditions + conditionsSeparator + '[' + record[record.length - 2] + ']'; // The last item contains an Array of formulaErrors.
conditions = conditions + conditionsSeparator + '[' + record[record.length - 1] + ']'; // The last item contains an Array of formulaValues.
fileContent = fileContent + separator + '[' + conditions + ']';
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
fileContent = "[" + JSON.stringify(tradingSystem) + "," + JSON.stringify(lastObjectsArray) + "," + fileContent + "]";
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + CONDITIONS_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeConditionsFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
writeStrategiesFile();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeConditionsFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeStrategiesFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < strategiesArray.length; i++) {
let record = strategiesArray[i];
fileContent = fileContent + separator + '[' +
record.begin + "," +
record.end + "," +
record.status + "," +
record.number + "," +
record.beginRate + "," +
record.endRate + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + STRATEGIES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeStrategiesFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
writeTradesFile();
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeStrategiesFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
function writeTradesFile() {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> Entering function."); }
let separator = "";
let fileRecordCounter = 0;
let fileContent = "";
for (let i = 0; i < tradesArray.length; i++) {
let record = tradesArray[i];
fileContent = fileContent + separator + '[' +
record.begin + "," +
record.end + "," +
record.status + "," +
record.lastProfitPercent + "," +
record.beginRate + "," +
record.endRate + "," +
record.exitType + "," +
record.stopRate + "]";
if (separator === "") { separator = ","; }
fileRecordCounter++;
}
fileContent = "[" + fileContent + "]";
let fileName = '' + market.assetA + '_' + market.assetB + '.json';
let filePathRoot = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + global.CLONE_EXECUTOR.codeName + "." + global.CLONE_EXECUTOR.version + "/" + global.EXCHANGE_NAME + "/" + bot.dataSetVersion;
let filePath = filePathRoot + "/Output/" + TRADES_FOLDER_NAME + "/" + "Multi-Period-Market" + "/" + outputPeriodLabel;
filePath += '/' + fileName
fileStorage.createTextFile(bot.devTeam, filePath, fileContent + '\n', onFileCreated);
function onFileCreated(err) {
try {
if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> Entering function."); }
if (LOG_FILE_CONTENT === true) { logger.write(MODULE_NAME, "[INFO] start -> writeTradesFile -> onFileCreated -> fileContent = " + fileContent); }
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.stack);
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> filePath = " + filePath);
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> market = " + market.assetA + "_" + market.assetB);
callBackFunction(err);
return;
}
callBackFunction(global.DEFAULT_OK_RESPONSE);
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> onFileCreated -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> writeTradesFile -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
}
catch (err) {
logger.write(MODULE_NAME, "[ERROR] start -> err = " + err.stack);
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
}
}
};
@@ -0,0 +1,391 @@
{
"displayName": "Jason",
"codeName": "AAJason",
"type": "Indicator",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"devTeam": "AAMasters",
"profilePicture": "Jason.png",
"dataSetVersion": "dataSet.V1",
"processes": [
{
"name": "Multi-Period-Market",
"description": "Produces a single file for each time period covering all the market.",
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"framework": {
"name": "Multi-Period-Market",
"startDate": {
"fixedDate": "2018-07-01"
},
"endDate": {
}
},
"statusDependencies": [
{
"devTeam": "AAMasters",
"bot": "AAJason",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Market",
"dataSetVersion": "dataSet.V1"
}
],
"dataDependencies": [
{
"devTeam": "AAVikings",
"bot": "AAGauss",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "LRC-Points",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAChris",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Percentage-Bandwidth",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAChris",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Bollinger-Bands",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAPaula",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Bollinger-Channels",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAPaula",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Bollinger-Sub-Channels",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Candles",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
}
]
},
{
"name": "Multi-Period-Daily",
"description": "Produces a Daily File at each time period.",
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"framework": {
"name": "Multi-Period-Daily",
"validPeriods": [ "15-min", "10-min", "05-min"],
"startDate": {
"fixedDate": "2018-07-01"
},
"endDate": {
"takeItFromStatusDependency": 1
}
},
"statusDependencies": [
{
"devTeam": "AAMasters",
"bot": "AACharly",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Historic-Trades",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Daily",
"dataSetVersion": "dataSet.V1"
},
{
"devTeam": "AAMasters",
"bot": "AAJason",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Daily",
"dataSetVersion": "dataSet.V1"
}
],
"dataDependencies": [
{
"devTeam": "AAVikings",
"bot": "AAGauss",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "LRC-Points",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAChris",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Percentage-Bandwidth",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAChris",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Bollinger-Bands",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAPaula",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Bollinger-Channels",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAPaula",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Bollinger-Sub-Channels",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAOlivia",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Candles",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
}
]
}
],
"products": [
{
"codeName": "Trading-Simulation",
"displayName": "Trading Simulation",
"description": "Trading Simulation",
"storageAccount": "aajason",
"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": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Trading-Simulation/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "15-min", "10-min", "05-min" ],
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Trading-Simulation/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Trading-Simulation/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTradingSimulation",
"moduleName": "TradingSimulation"
},
"relatedToTradingEngine": true
},
{
"codeName": "Simulation-Conditions",
"displayName": "Simulation Conditions",
"description": "Simulation Conditions",
"storageAccount": "aajason",
"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": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Conditions/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "15-min", "10-min", "05-min" ],
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Conditions/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Conditions/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTradingSimulation",
"moduleName": "Conditions"
},
"relatedToTradingEngine": true
},
{
"codeName": "Simulation-Strategies",
"displayName": "Simulation Strategies",
"description": "Simulation Strategies",
"storageAccount": "aajason",
"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": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Strategies/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "15-min", "10-min", "05-min" ],
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Strategies/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Strategies/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTradingSimulation",
"moduleName": "Strategies"
},
"relatedToTradingEngine": true
},
{
"codeName": "Simulation-Trades",
"displayName": "Simulation Trades",
"description": "Simulation Trades",
"storageAccount": "aajason",
"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": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Trades/Multi-Period-Market/@Period",
"fileName": "@AssetA_@AssetB.json"
},
{
"codeName": "Multi-Period-Daily",
"type": "Daily Files",
"validPeriods": [ "15-min", "10-min", "05-min" ],
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Trades/Multi-Period-Daily/@Period/@Year/@Month/@Day",
"fileName": "@AssetA_@AssetB.json",
"dataRange": {
"filePath": "AAMasters/AAJason.1.0/AACloud.1.1/@Exchange/dataSet.V1/Output/Simulation-Trades/Multi-Period-Daily",
"fileName": "Data.Range.@AssetA_@AssetB.json"
}
}
],
"exchangeList": [
{
"name": "Poloniex"
},
{
"name": "Coss"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTradingSimulation",
"moduleName": "Trades"
},
"relatedToTradingEngine": true
}
]
}
@@ -0,0 +1,764 @@
exports.newUserBot = function newUserBot(bot, logger, COMMONS_MODULE) {
// Variable used for logs, which will be passed to the logger instance
const MODULE_NAME = 'User Bot'
const LOG_INFO = true
let assistant, fileStorage
let autopilotControl = false
const orderMessage = require("@superalgos/mqservice/orderMessage/orderMessage")
const {
MESSAGE_ENTITY, MESSAGE_TYPE, ORDER_CREATOR, ORDER_TYPE, ORDER_OWNER,
ORDER_DIRECTION, ORDER_STATUS, ORDER_EXIT_OUTCOME, ORDER_MARGIN_ENABLED,
getMessage, createMessageFromObject
} = orderMessage.newOrderMessage()
const axios = require('axios')
const util = require('util')
/*
This objects returns two public functions that will be used to integrate with the platform.
*/
return {
initialize: initialize,
start: start,
setAutopilot: setAutopilot
}
function initialize(pAssistant, pGenes, callBackFunction) {
try {
logInfo('initialize -> Entering function.')
// Integration with platform logger, when running on cloud
logger.fileName = MODULE_NAME
assistant = pAssistant
if (bot.timePeriodFileStorage === undefined) {
throw new Error("Execution Parameter Time Period not defined.")
}
if (bot.dataSet === undefined) {
throw new Error("Execution Parameter Data Set not defined.")
}
let key = bot.devTeam + '-simulator-' + bot.codeName + '-Trading-Simulation-' + bot.dataSet + '-dataSet.V1'
fileStorage = assistant.dataDependencies.dataSets.get(key)
if (fileStorage !== undefined){
callBackFunction(global.DEFAULT_OK_RESPONSE)
}else{
logError('initialize -> Failed to initialize storage. Key not found:' + key)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
} catch (error) {
logError('initialize -> onDone -> err = ' + error.message)
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
}
}
function logInfo(message) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] ' + message) }
}
function logWarn(message) {
if (LOG_INFO === true) { logger.write(MODULE_NAME, '[WARN] ' + message) }
}
function logError(message) {
logger.write(MODULE_NAME, '[ERROR] ' + message)
}
function setAutopilot(autopilot) {
autopilotControl = autopilot
}
/*
The start function is called by the platform for executing the bot every 1 minute
*/
function start(callBackFunction) {
logInfo('start -> Entering function.')
const callback = util.callbackify(executorLogic)
callback((err, ret) => {
if (err) {
callBackFunction(err)
} else {
callBackFunction(global.DEFAULT_OK_RESPONSE)
}
})
}
async function executorLogic() {
try {
let indicatorFileContent = await getIndicatorFile()
let simulatorEngineMessage = getSimulatorEngineMessageFromFile(indicatorFileContent)
let autopilotResponse = await getAutopilot()
if (simulatorEngineMessage === undefined) {
logWarn('start -> Indicator message not found.')
throw global.DEFAULT_RETRY_RESPONSE
}
logInfo('Processing indicator message: ' + JSON.stringify(simulatorEngineMessage))
// Checking Stop Loss
let assetABalance = assistant.getAvailableBalance().assetA
let currentRate = assistant.getMarketRate()
if (simulatorEngineMessage !== undefined && assetABalance > 0 && currentRate >= simulatorEngineMessage.order.stop) {
logInfo('Closing trade with Stop Loss.')
try {
let position = await createBuyPosition(currentRate)
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = position.id
simulatorExecutorMessage.order.id = position.id
simulatorExecutorMessage.order.rate = currentRate
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy
simulatorExecutorMessage.order.size = assetABalance
simulatorExecutorMessage.order.status = ORDER_STATUS.Placed
simulatorExecutorMessage.order.exitOutcome = ORDER_EXIT_OUTCOME.StopLoss
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
} catch (error) {
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant
simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.order.rate = position.rate
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy
simulatorExecutorMessage.order.size = assetABalance
simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
} finally {
return
}
}
// Checking Take Profits
if (simulatorEngineMessage !== undefined && assetABalance > 0 && currentRate <= simulatorEngineMessage.order.takeProfit) {
logInfo('Closing trade with Take Profits.')
try {
let position = await createBuyPosition(currentRate, assetABalance)
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = position.id
simulatorExecutorMessage.order.id = position.id
simulatorExecutorMessage.order.rate = currentRate
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy
simulatorExecutorMessage.order.size = assetABalance
simulatorExecutorMessage.order.status = ORDER_STATUS.Placed
simulatorExecutorMessage.order.exitOutcome = ORDER_EXIT_OUTCOME.TakeProfit
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
} catch (error) {
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant
simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.order.rate = currentRate
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Buy
simulatorExecutorMessage.order.size = assetABalance
simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
} finally {
return
}
}
if (autopilotResponse.autopilot || autopilotControl) {
await manageCloneInAutopilotOn(simulatorEngineMessage)
} else {
await manageCloneInAutopilotOff(simulatorEngineMessage)
}
} catch (error) {
if (error.stack) logError(JSON.stringify(error.stack))
throw error
}
}
async function manageCloneInAutopilotOn(simulatorEngineMessage) {
let assetBBalance = assistant.getAvailableBalance().assetB
let currentRate = assistant.getMarketRate()
if (simulatorEngineMessage.messageType === MESSAGE_TYPE.Order
&& simulatorEngineMessage.order.direction === ORDER_DIRECTION.Sell) {
try {
let position = await createSellPosition(currentRate, assetBBalance)
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = position.id
simulatorExecutorMessage.order.id = position.id
simulatorExecutorMessage.order.rate = currentRate
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Sell
simulatorExecutorMessage.order.size = assetBBalance
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
} catch (error) {
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingAssistant
simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.messageType = MESSAGE_TYPE.Order
simulatorExecutorMessage.order.rate = currentRate
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Sell
simulatorExecutorMessage.order.size = assetBBalance
simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
}
} else {
logInfo("manageCloneInAutopilotOn -> Nothing to do, there isn't a buy or sell opportunity.")
}
}
async function manageCloneInAutopilotOff(simulatorEngineMessage) {
/**
* If there is a ManualAuthorized order on the cockpit we will proceed to:
* 1) Save an audit containing what we received from the cockpit
* 2) Execute the order in the market as received
* 3) Save the audit of the order placed
* 4) Update the cockpit with the result
*/
let acceptedSignals = await getSignalsByCloneId(ORDER_STATUS.ManualAuthorized)
if (acceptedSignals !== undefined && acceptedSignals.length > 0) {
for (let acceptedSignal of acceptedSignals) {
try {
logInfo('manageCloneInAutopilotOff -> Executing manual authorized signal.')
// 1) Save audit containing what we received from the cockpit
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = 0
simulatorExecutorMessage.from = MESSAGE_ENTITY.TradingCokpit
simulatorExecutorMessage.to = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.order = acceptedSignal.orderData // we save the order as received
simulatorExecutorMessage.order.status = ORDER_STATUS.ManualAuthorized
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
// 2) Execute the order in the market as received
let position = await createSellPosition(acceptedSignal.orderData.rate, acceptedSignal.orderData.size)
// 3) Save the audit of the order placed
simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = position.id
simulatorExecutorMessage.from = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingAssistant
simulatorExecutorMessage.dateTime = position.date
simulatorExecutorMessage.order = acceptedSignal.orderData
simulatorExecutorMessage.order.id = position.id
simulatorExecutorMessage.order.dateTime = position.date
simulatorExecutorMessage.order.rate = position.rate
simulatorExecutorMessage.order.size = position.amountB
simulatorExecutorMessage.order.status = ORDER_STATUS.Placed
simulatorExecutorMessage.order.sizeFilled = 0
// 4) Update the cockpit with the result
await updateSignal(acceptedSignal.id, simulatorExecutorMessage)
message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
return
} catch (error) {
// Update the cockpit with the error
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = 0
simulatorExecutorMessage.from = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingCokpit
simulatorExecutorMessage.messageType = MESSAGE_TYPE.Order
simulatorExecutorMessage.dateTime = bot.processDatetime.valueOf()
simulatorExecutorMessage.order.status = ORDER_STATUS.Rejected
simulatorExecutorMessage.order = acceptedSignal.order
await updateSignal(acceptedSignal.id, simulatorExecutorMessage)
// Update the audit with the error
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
return
}
}
}
/**
* If there is a Placed order on the cockpit we will proceed to:
* 1) Check order status on the market
* 2) Save the audit of the status
* 3) Update the cockpit with the results
*/
let inProcessSignals = await getSignalsByCloneId(ORDER_STATUS.Placed)
if (inProcessSignals !== undefined && inProcessSignals.length > 0) {
for (let inProcessSignal of inProcessSignals) {
logInfo('manageCloneInAutopilotOff -> Checking in process signal.')
// 1) Check order status on the market
let position = checkSignalCompletion()
if (position || position === undefined) {
// 2) Save the audit of the status
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = inProcessSignal.orderData.id
simulatorExecutorMessage.from = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingCokpit
simulatorExecutorMessage.messageType = MESSAGE_TYPE.OrderUpdate
simulatorExecutorMessage.order = inProcessSignal.orderData
simulatorExecutorMessage.order.status = ORDER_STATUS.Filled
simulatorExecutorMessage.order.sizeFilled = -1
// 3) Update the cockpit with the results
await updateSignal(inProcessSignal.id, simulatorExecutorMessage)
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
} else {
// Otherwise: Save the audit of the order status, wichs is currently saved by the trading process
}
return
}
}
/**
* If there is any signal not yet authorized:
* 1) Update the signal with the new indicator value on the cockpit
* 3) Save the audit of the message sent
*/
let assetBBalance = assistant.getAvailableBalance().assetB
let currentRate = assistant.getMarketRate()
let signals = await getSignalsByCloneId(ORDER_STATUS.Signaled)
if (signals !== undefined && signals.length > 0) {
for (let inProcessSignal of signals) {
logInfo('manageCloneInAutopilotOff -> Signal waiting for approval found, updating it with latest market data.')
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingCokpit
simulatorExecutorMessage.messageType = MESSAGE_TYPE.OrderUpdate
simulatorExecutorMessage.order.id = 0
simulatorExecutorMessage.order.rate = currentRate
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.direction = simulatorEngineMessage.order.direction
simulatorExecutorMessage.order.size = assetBBalance
simulatorExecutorMessage.order.status = ORDER_STATUS.Signaled
await updateSignal(inProcessSignal.id, simulatorExecutorMessage)
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
return
}
}
/**
* Finally:
* 1) Check if it's needed to create a new signal based on the indicator
* 2) Save the audit message for the received info from Trading Simulator
* 3) Create the signal on the cockpit
*/
if (simulatorEngineMessage !== undefined && simulatorEngineMessage.messageType === MESSAGE_TYPE.Order
&& simulatorEngineMessage.order.direction === ORDER_DIRECTION.Sell) {
logInfo('Creating new signal.')
// 2) Save the audit message for the received info from Trading Simulator
let simulatorExecutorMessage = buildBasicSimulatorExecutorMessage()
simulatorExecutorMessage.id = 0
simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingCokpit
simulatorExecutorMessage.messageType = MESSAGE_TYPE.Order
simulatorExecutorMessage.order.id = 0
simulatorExecutorMessage.order.status = ORDER_STATUS.Signaled
simulatorExecutorMessage.order.rate = currentRate
simulatorExecutorMessage.order.direction = ORDER_DIRECTION.Sell
simulatorExecutorMessage.order.takeProfit = simulatorEngineMessage.order.takeProfit
simulatorExecutorMessage.order.stop = simulatorEngineMessage.order.stop
simulatorExecutorMessage.order.size = assetBBalance
let message = createMessageFromObject(simulatorExecutorMessage)
assistant.addExtraData(message)
// 3) Create the signal on the cockpit
await createSignal(simulatorExecutorMessage)
} else {
logWarn("manageCloneInAutopilotOff -> Nothing to do, there isn't a buy or sell opportunity.")
}
}
async function createBuyPosition(currentRate, amountToBuy) {
if (!amountToBuy) amountToBuy = assistant.getAvailableBalance().assetA
let amountB = Number((amountToBuy / currentRate).toFixed(8))
let positions = assistant.getPositions()
let assetABalance = assistant.getAvailableBalance().assetA
if (positions.length > 0 && positions[0].type === 'buy' && positions[0].status !== 'executed') {
logInfo('createBuyPosition -> Moving an existing BUY position to a new price: $' + Number(currentRate).toLocaleString())
assistant.movePosition[util.promisify.custom] = n => new Promise((resolve, reject) => {
assistant.movePosition(positions[0], currentRate, (result, position) => {
if (result !== global.DEFAULT_OK_RESPONSE) {
reject(result)
} else {
resolve(position)
}
})
})
let movePosition = util.promisify(assistant.movePosition)
await movePosition(positions[0], currentRate)
return positions[0]
} else if (assetABalance > 0) {
logInfo('createBuyPosition -> Put a new BUY position at price: $' + Number(currentRate).toLocaleString())
assistant.putPosition[util.promisify.custom] = n => new Promise((resolve, reject) => {
assistant.putPosition('buy', currentRate, amountToBuy, amountB, (result, position) => {
if (result !== global.DEFAULT_OK_RESPONSE) {
reject(result)
} else {
resolve(position)
}
})
})
let putPosition = util.promisify(assistant.putPosition)
await putPosition('buy', currentRate, amountToBuy, amountB)
return positions[0]
} else {
logInfo('createBuyPosition -> Not enough available balance to buy.')
throw new Error('There is not enough available balance to buy.')
}
}
async function createSellPosition(currentRate, amountToSell) {
if (!amountToSell) amountToSell = assistant.getAvailableBalance().assetB
let amountA = amountToSell * currentRate
let positions = assistant.getPositions()
if (positions.length > 0 && positions[0].type === 'sell' && positions[0].status !== 'executed') {
logInfo('createSellPosition -> Moving an existing SELL position to a new price: $' + Number(currentRate).toLocaleString())
assistant.movePosition[util.promisify.custom] = n => new Promise((resolve, reject) => {
assistant.movePosition(positions[0], currentRate, (result, position) => {
if (result !== global.DEFAULT_OK_RESPONSE) {
reject(result)
} else {
resolve(position)
}
})
})
let movePosition = util.promisify(assistant.movePosition)
await movePosition(positions[0], currentRate)
return positions[0]
} else if (assistant.getAvailableBalance().assetB > 0) {
logInfo('createSellPosition -> Put a new SELL position at price: $' + Number(currentRate).toLocaleString())
assistant.putPosition[util.promisify.custom] = n => new Promise((resolve, reject) => {
assistant.putPosition('sell', currentRate, amountA, amountToSell, (result, position) => {
if (result !== global.DEFAULT_OK_RESPONSE) {
reject(result)
} else {
resolve(position)
}
})
})
let putPosition = util.promisify(assistant.putPosition)
let position = await putPosition('sell', currentRate, amountA, amountToSell)
return position
} else {
logInfo('createSellPosition -> There is not enough available balance to sell.')
throw new Error('There is not enough available balance to sell.')
}
}
function checkSignalCompletion() {
let positions = assistant.getPositions()
if ((positions.length === 0) || (positions.length > 0 && positions[0].status === 'executed')) {
logInfo('checkSignalCompletion -> The order was executed.')
return positions[0]
} else {
return false
}
}
// Storage functions
async function getIndicatorFile() {
logInfo('getIndicatorFile -> Entering function.')
let filePath
if (bot.processes[0].timePeriod > 2700000) {
// Market Files
filePath = 'Trading-Simulation/' + bot.dataSet + '/' + bot.timePeriodFileStorage
} else {
// Daily Files
let dateTime = bot.processDatetime
let datePath = dateTime.getUTCFullYear() + '/' + pad(dateTime.getUTCMonth() + 1, 2) + '/' + pad(dateTime.getUTCDate(), 2)
filePath = 'Trading-Simulation/' + bot.dataSet + '/' + bot.timePeriodFileStorage + '/' + datePath
}
/*
bot.botCache: allows us to keep a map (key value pairs) between executions,
so we don't need to go to the storage to retrieve this value.
If the value already exist on the cache we will get it from there,
otherwise it will be retrieved from the bot storage.
*/
if (bot.startMode === 'Backtest' && bot.botCache.has(filePath)) {
logInfo('getIndicatorFile -> Getting the file from local cache.')
return bot.botCache.get(filePath)
} else {
let indicatorFileContent = await getFileContent(filePath, 'USDT_BTC.json')
bot.botCache.set(filePath, JSON.parse(indicatorFileContent))
return JSON.parse(indicatorFileContent)
}
}
function getSimulatorEngineMessageFromFile(indicatorFileContent) {
try {
let lastIndexIndicatorFile = indicatorFileContent.length - 1
let lastAvailableDateTime = indicatorFileContent[lastIndexIndicatorFile][0]
if (bot.processDatetime.valueOf() <= lastAvailableDateTime || !isExecutionToday()) {
for (let i = 0; i < indicatorFileContent.length; i++) {
if (bot.processDatetime.valueOf() >= indicatorFileContent[i][0] && bot.processDatetime.valueOf() < indicatorFileContent[i][1]) {
return getMessage(indicatorFileContent[i][25])
}
}
logWarn('getSimulatorEngineMessageFromFile -> The indicator message was not found at time: ' + bot.processDatetime.valueOf())
} else {
// Running live we will process last available Indicator Message only if it's delayed 25 minutes top
let maxTolerance = 120 * 60 * 1000
if (bot.processDatetime.valueOf() <= (lastAvailableDateTime + maxTolerance)) {
return getMessage(indicatorFileContent[lastIndexIndicatorFile][25])
} else {
logWarn('getSimulatorEngineMessageFromFile -> Last available indicator older than 2 hours. Skipping execution.')
}
}
} catch (error) {
logError('getSimulatorEngineMessageFromFile -> error = ' + error.message)
throw global.DEFAULT_RETRY_RESPONSE
}
}
// Helper Functions
function isExecutionToday() {
let localDate = new Date()
let today = new Date(Date.UTC(
localDate.getUTCFullYear(),
localDate.getUTCMonth(),
localDate.getUTCDate()))
return (today.getUTCFullYear() === bot.processDatetime.getUTCFullYear()
&& today.getUTCMonth() === bot.processDatetime.getUTCMonth()
&& today.getUTCDate() === bot.processDatetime.getUTCDate())
}
// Modules queries
async function getSignalsByCloneId(orderStatus) {
try {
const cockPit = await axios({
url: process.env.GATEWAY_ENDPOINT,
method: 'post',
data: {
query: `
query($cloneId: String!, $orderStatus: String){
cockpit_SignalsByCloneId(cloneId: $cloneId, orderStatus:$orderStatus){
id
cloneId
orderId
orderCreator
orderStatus
orderData
}
}
`,
variables: {
cloneId: process.env.CLONE_ID,
orderStatus: orderStatus
},
},
headers: {
authorization: 'Bearer ' + global.ACCESS_TOKEN
}
})
logInfo("Retrieved signals in status: " + orderStatus)
return cockPit.data.data.cockpit_SignalsByCloneId
} catch (error) {
logError('getSignalsByCloneId error: ' + error.response.data.errors[0])
throw global.DEFAULT_RETRY_RESPONSE
}
}
async function updateSignal(signalId, message) {
try {
const cockPit = await axios({
url: process.env.GATEWAY_ENDPOINT,
method: 'post',
data: {
query: `
mutation ($signalId: ID!, $message:cockpit_JSON!){
cockpit_UpdateSignal(
id: $signalId
message: $message
) {
id
cloneId
orderStatus
}
}
`,
variables: {
signalId: signalId,
message: message
},
},
headers: {
authorization: 'Bearer ' + global.ACCESS_TOKEN
}
})
logInfo('updateSignal ok.')
return cockPit.data.data.cockpit_UpdateSignal
} catch (error) {
logError('updateSignal error: ' + error.response.data.errors[0])
throw global.DEFAULT_RETRY_RESPONSE
}
}
async function createSignal(message) {
try {
const cockPit = await axios({
url: process.env.GATEWAY_ENDPOINT,
method: 'post',
data: {
query: `
mutation ($cloneId: String!, $message: cockpit_JSON!){
cockpit_CreateSignal(
cloneId: $cloneId
message: $message
) {
id
cloneId
orderStatus
}
}
`,
variables: {
cloneId: process.env.CLONE_ID,
message: message
},
},
headers: {
authorization: 'Bearer ' + global.ACCESS_TOKEN
}
})
logInfo('createSignal ok.')
return cockPit.data.data.cockpit_CreateSignal
} catch (error) {
logInfo("createSignal error: " + error.response.data.errors[0])
throw global.DEFAULT_RETRY_RESPONSE
}
}
async function getAutopilot() {
try {
const cockPit = await axios({
url: process.env.GATEWAY_ENDPOINT,
method: 'post',
data: {
query: `
query($cloneId: String!){
cockpit_CloneSettingsByCloneId(cloneId: $cloneId){
id
cloneId
autopilot
}
}
`,
variables: {
cloneId: process.env.CLONE_ID
},
},
headers: {
authorization: 'Bearer ' + global.ACCESS_TOKEN
}
})
if (cockPit.data.errors) {
throw new Error(cockPit.data.errors[0].message)
} else {
logInfo('getAutopilot -> Ok.')
return cockPit.data.data.cockpit_CloneSettingsByCloneId
}
} catch (error) {
logInfo('getAutopilot error: ' + error)
throw global.DEFAULT_RETRY_RESPONSE
}
}
async function getFileContent(containerName, blobName) {
fileStorage.getTextFile[util.promisify.custom] = n => new Promise((resolve, reject) => {
fileStorage.getTextFile(containerName, blobName, (result, fileContent) => {
if (result !== global.DEFAULT_OK_RESPONSE) {
console.log('[WARN] getFileContent -> The indicator dependency is not ready. Will retry later.')
reject(global.DEFAULT_RETRY_RESPONSE)
} else {
resolve(fileContent)
}
})
})
let getTextFile = util.promisify(fileStorage.getTextFile)
let fileContent = await getTextFile(containerName, blobName)
return fileContent
}
function pad(str, max) {
str = str.toString()
return str.length < max ? pad('0' + str, max) : str
}
function buildBasicSimulatorExecutorMessage() {
var simulatorExecutorMessage = {}
simulatorExecutorMessage.id = 0
simulatorExecutorMessage.from = MESSAGE_ENTITY.SimulationExecutor
simulatorExecutorMessage.to = MESSAGE_ENTITY.TradingAssistant
simulatorExecutorMessage.messageType = MESSAGE_TYPE.Order
simulatorExecutorMessage.dateTime = bot.processDatetime.valueOf()
simulatorExecutorMessage.order = {}
simulatorExecutorMessage.order.dateTime = bot.processDatetime.valueOf()
simulatorExecutorMessage.order.creator = ORDER_CREATOR.SimulationEngine
simulatorExecutorMessage.order.owner = ORDER_OWNER.User
simulatorExecutorMessage.order.exchange = global.EXCHANGE_NAME
simulatorExecutorMessage.order.market = global.MARKET.name
simulatorExecutorMessage.order.marginEnabled = ORDER_MARGIN_ENABLED.False
simulatorExecutorMessage.order.type = ORDER_TYPE.Limit
simulatorExecutorMessage.order.status = ORDER_STATUS.Placed
simulatorExecutorMessage.order.sizeFilled = 0
simulatorExecutorMessage.order.exitOutcome = ''
return simulatorExecutorMessage
}
}
@@ -0,0 +1,248 @@
{
"displayName": "Bumblebee",
"codeName": "bumblebee",
"type": "Trading",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"devTeam": "aavikings",
"profilePicture": "bumblebee.png",
"dataSetVersion": "dataSet.V1",
"processes": [
{
"name": "Trading-Process",
"description": "Trading strategy to be used as a template.",
"normalWaitTime": 60000,
"retryWaitTime": 10000,
"sleepWaitTime": 3600000,
"comaWaitTime": 86400000,
"timePeriod": 3600000,
"statusDependencies": [
{
"devTeam": "aavikings",
"bot": "bumblebee",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Trading-Process",
"dataSetVersion": "dataSet.V1",
"type": "Trading"
},
{
"devTeam": "AAMasters",
"bot": "AAJason",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Market",
"dataSetVersion": "dataSet.V1",
"type": "Indicator"
},
{
"devTeam": "AAMasters",
"bot": "AAJason",
"botVersion": {
"major": 1,
"minor": 0
},
"process": "Multi-Period-Daily",
"dataSetVersion": "dataSet.V1",
"type": "Indicator"
}
],
"dataDependencies": [
{
"devTeam": "AAMasters",
"bot": "AABruce",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Candles",
"dataSetVersion": "dataSet.V1",
"dataSet": "One-Min"
},
{
"devTeam": "AAMasters",
"bot": "AAJason",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Trading-Simulation",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Market"
},
{
"devTeam": "AAMasters",
"bot": "AAJason",
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Trading-Simulation",
"dataSetVersion": "dataSet.V1",
"dataSet": "Multi-Period-Daily"
}
]
}
],
"products": [
{
"codeName": "Live Trading History",
"displayName": "Live Trading History",
"shortDisplayName": "Live",
"description": "General information about trading history.",
"dataSets": [
{
"codeName": "Oficial History",
"type": "File Sequence",
"validPeriods": [
"24-hs",
"12-hs",
"08-hs",
"06-hs",
"04-hs",
"03-hs",
"02-hs",
"01-hs",
"45-min",
"40-min",
"30-min",
"20-min",
"15-min",
"10-min",
"05-min",
"04-min",
"03-min",
"02-min",
"01-min"
],
"filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process",
"fileName": "Execution.History.Live.@Sequence.json"
}
],
"exchangeList": [
{
"name": "Poloniex"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTradingSimulation",
"moduleName": "SimulationExecution"
}
},
{
"codeName": "Backtest Trading History",
"displayName": "Backtest Trading History",
"shortDisplayName": "Backtest",
"description": "General information about backtest trading history.",
"dataSets": [
{
"codeName": "Backtest History",
"type": "File Sequence",
"validPeriods": [
"24-hs",
"12-hs",
"08-hs",
"06-hs",
"04-hs",
"03-hs",
"02-hs",
"01-hs",
"45-min",
"40-min",
"30-min",
"20-min",
"15-min",
"10-min",
"05-min",
"04-min",
"03-min",
"02-min",
"01-min"
],
"filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process",
"fileName": "Execution.History.Backtest.@Sequence.json"
}
],
"exchangeList": [
{
"name": "Poloniex"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTradingSimulation",
"moduleName": "SimulationExecution"
}
},
{
"codeName": "Competition Trading History",
"displayName": "Competition Trading History",
"shortDisplayName": "Competition",
"description": "General information about competition trading history.",
"dataSets": [
{
"codeName": "Competition History",
"type": "File Sequence",
"validPeriods": [
"24-hs",
"12-hs",
"08-hs",
"06-hs",
"04-hs",
"03-hs",
"02-hs",
"01-hs",
"45-min",
"40-min",
"30-min",
"20-min",
"15-min",
"10-min",
"05-min",
"04-min",
"03-min",
"02-min",
"01-min"
],
"filePath": "@DevTeam/@Bot.1.0/AACloud.1.1/Poloniex/dataSet.V1/Output/Trading-Process",
"fileName": "Execution.History.Competition.@Sequence.json"
}
],
"exchangeList": [
{
"name": "Poloniex"
}
],
"plotter": {
"devTeam": "AAMasters",
"codeName": "PlottersTradingSimulation",
"moduleName": "SimulationExecution"
}
}
],
"marketRateProvider": {
"devTeam": "AAMasters",
"bot": "AABruce",
"host": {
"url": "https://demo-dev.superalgos.org/",
"storage": "localStorage",
"container": "aamasters",
"accessKey": ""
},
"botVersion": {
"major": 1,
"minor": 0
},
"product": "Candles",
"dataSet": "One-Min",
"dataSetVersion": "dataSet.V1"
}
}
+14
View File
@@ -0,0 +1,14 @@
import fs from 'fs'
export default function readFile(fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, 'utf8', (err, file) => {
if (err) {
reject(err)
} else {
resolve(file)
}
})
})
}
+46
View File
@@ -0,0 +1,46 @@
import { assert } from 'chai'
import {
getFileContent,
createContainer,
deleteContainer,
writeFileContent
} from '../src/storage/providers/MinioStorage'
import 'dotenv/config'
describe('Minio Storage Tests', function () {
it('Create Container', async function () {
let containerName = 'test-container'
await createContainer(containerName)
assert.isOk('everything', 'everything is ok')
})
it('Save File', async function () {
let containerName = 'test-container'
let blobName = 'test-folder/test_data_file.json'
let content = '[0,0,0,0]'
await writeFileContent(containerName, blobName, content)
// for (let index = 0; index < 1000; index++) {
// let blobName = 'test-folder/test_data_file_' + index + '.json'
// await writeFileContent(containerName, blobName, content)
// }
assert.isOk('everything', 'everything is ok')
})
it('Retrieve File', async function () {
let containerName = 'test-container'
let blobName = 'test-folder/test_data_file_1.json'
let fileContent = await getFileContent(containerName, blobName)
let expected = '[0,0,0,0]'
assert.equal(fileContent, expected)
})
it('Delete Container', async function () {
let containerName = 'test-container'
console.log('start: '+ new Date().toISOString())
await deleteContainer(containerName)
console.log('end: '+ new Date().toISOString())
assert.isOk('everything', 'everything is ok')
})
})