diff --git a/CanvasApp/CockpitSpace/AssetBalances.js b/CanvasApp/CockpitSpace/AssetBalances.js index d9fa9ff85..6cb267ef4 100644 --- a/CanvasApp/CockpitSpace/AssetBalances.js +++ b/CanvasApp/CockpitSpace/AssetBalances.js @@ -37,9 +37,6 @@ function newQuotedAssetalances () { for (let i = 0; i < 2; i++) { let speedometer = newSpeedometer() - speedometer.container.frame.position.x = thisObject.container.frame.width / 2 - speedometer.container.frame.width / 2 - speedometer.container.frame.position.y = thisObject.container.frame.height / 2 - speedometer.container.frame.height / 2 - speedometer.initialize() thisObject.speedometers.push(speedometer) } @@ -109,7 +106,7 @@ function newQuotedAssetalances () { label = 'ASSET BALANCES' xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO - yOffset = -45 + yOffset = -45 + 9 labelPoint = { x: 0 - xOffset + (thisObject.paramsArray[1].LEFT_OFFSET - thisObject.paramsArray[0].LEFT_OFFSET) / 2 + thisObject.paramsArray[0].LEFT_OFFSET - 20, @@ -120,3 +117,4 @@ function newQuotedAssetalances () { browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) } } + diff --git a/CanvasApp/CockpitSpace/Speedometer.js b/CanvasApp/CockpitSpace/Speedometer.js index 93178ab38..7a2f0a857 100644 --- a/CanvasApp/CockpitSpace/Speedometer.js +++ b/CanvasApp/CockpitSpace/Speedometer.js @@ -51,13 +51,13 @@ function newSpeedometer () { } function drawAssetCircle () { - const RED_LINE_HIGHT = 5 + const RED_LINE_HIGHT = 2 const RADIUS = 50 const OPACITY = 1 let centerPoint = { x: thisObject.params.LEFT_OFFSET, - y: COCKPIT_SPACE_POSITION - COCKPIT_SPACE_HEIGHT / 2 + y: COCKPIT_SPACE_POSITION - COCKPIT_SPACE_HEIGHT / 2 - 9 } browserCanvasContext.beginPath() diff --git a/CanvasApp/DesignerSpace/Workspace/FunctionLibraries/SessionFunctions.js b/CanvasApp/DesignerSpace/Workspace/FunctionLibraries/SessionFunctions.js index fe40011be..5b967b29d 100644 --- a/CanvasApp/DesignerSpace/Workspace/FunctionLibraries/SessionFunctions.js +++ b/CanvasApp/DesignerSpace/Workspace/FunctionLibraries/SessionFunctions.js @@ -34,6 +34,7 @@ function newSessionFunctions () { 'Trading System->' + 'Parameters->' + 'Base Asset->Time Range->Time Frame->Slippage->Fee Structure->' + + 'Exchange Account Asset->Asset->' + 'Strategy->' + 'Trigger Stage->Trigger On Event->Trigger Off Event->Take Position Event->' + 'Announcement->Telegram Bot->' + @@ -44,14 +45,16 @@ function newSessionFunctions () { 'Stop->Take Profit->' + 'Phase->Formula->Next Phase Event->' + 'Situation->Condition->Javascript Code->' + - 'Announcement->Telegram Bot->' + 'Announcement->Telegram Bot->' + + 'Close Stage->Close Execution->' let tradingSystem = functionLibraryProtocolNode.getProtocolNode(node.payload.referenceParent, false, true, true, false, false, lightingPath) lightingPath = '' + 'Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->' + 'Parameters->' + - 'Base Asset->Time Range->Time Frame->Slippage->Fee Structure->' + 'Base Asset->Time Range->Time Frame->Slippage->Fee Structure->' + + 'Exchange Account Asset->Asset->' let session = functionLibraryProtocolNode.getProtocolNode(node, false, true, true, false, false, lightingPath) @@ -62,6 +65,11 @@ function newSessionFunctions () { } systemEventHandler.raiseEvent(key, 'Run Session', event) + + if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) { + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } } function stopSession (node, functionLibraryProtocolNode, callBackFunction) { diff --git a/CanvasApp/FloatingSpace/CircularMenuItem.js b/CanvasApp/FloatingSpace/CircularMenuItem.js index d088385e6..4a66f3679 100644 --- a/CanvasApp/FloatingSpace/CircularMenuItem.js +++ b/CanvasApp/FloatingSpace/CircularMenuItem.js @@ -320,7 +320,7 @@ function newCircularMenuItem () { } } else { if (thisObject.workFailedLabel != undefined) { - setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 500, STATUS_PRIMARY_WORK_FAILED) + setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 250, STATUS_PRIMARY_WORK_FAILED) } } } else { @@ -331,7 +331,7 @@ function newCircularMenuItem () { } } else { if (thisObject.workFailedLabel != undefined) { - setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 500, STATUS_PRIMARY_WORK_FAILED) + setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 250, STATUS_PRIMARY_WORK_FAILED) } } } @@ -343,7 +343,7 @@ function newCircularMenuItem () { } } else { if (thisObject.secondaryWorkFailedLabel != undefined) { - setStatus(thisObject.secondaryWorkFailedLabel, UI_COLOR.TITANIUM_YELLOW, 500, STATUS_SECONDARY_WORK_FAILED) + setStatus(thisObject.secondaryWorkFailedLabel, UI_COLOR.TITANIUM_YELLOW, 250, STATUS_SECONDARY_WORK_FAILED) } } } diff --git a/CanvasApp/Spaces/DesignerSpace.js b/CanvasApp/Spaces/DesignerSpace.js index 1618ab0b1..bbf20057f 100644 --- a/CanvasApp/Spaces/DesignerSpace.js +++ b/CanvasApp/Spaces/DesignerSpace.js @@ -232,7 +232,13 @@ function newDesignerSpace () { 'angle-to-parent-180', 'angle-to-parent-090', 'angle-to-parent-045', - 'angle-to-parent-000' + 'angle-to-parent-000', + 'data-mining', + 'initial-definition', + 'next-phase-event', + 'testing-environment', + 'production-environment', + 'task-manager' ] for (let i = 0; i < iconsNames.length; i++) { diff --git a/Inter-Process-Files/AppSchema.json b/Inter-Process-Files/AppSchema.json index 840d8fdb8..787e8936f 100644 --- a/Inter-Process-Files/AppSchema.json +++ b/Inter-Process-Files/AppSchema.json @@ -976,7 +976,7 @@ "chainAttachesTo": { "compatibleTypes": "->Data Mining->Testing Environment->Production Environment->" }, - "icon": "task", + "icon": "task-manager", "properties": [ { "name": "tasks", @@ -3523,7 +3523,7 @@ "chainAttachesTo": { "compatibleTypes": "->Open Stage->" }, - "icon": "task", + "icon": "initial-definition", "properties": [ { "name": "stopLoss", @@ -3713,7 +3713,7 @@ "chainAttachesTo": { "compatibleTypes": "->Phase->" }, - "icon": "pantone", + "icon": "next-phase-event", "properties": [ { "name": "situations", @@ -5293,7 +5293,7 @@ ], "addLeftIcons": true, "level": 2, - "icon": "data-storage", + "icon": "data-mining", "chainAttachesTo": { "compatibleTypes": "->Network Node->" }, @@ -5328,7 +5328,7 @@ ], "addLeftIcons": true, "level": 2, - "icon": "data-storage", + "icon": "testing-environment", "chainAttachesTo": { "compatibleTypes": "->Network Node->" }, @@ -5363,7 +5363,7 @@ ], "addLeftIcons": true, "level": 2, - "icon": "data-storage", + "icon": "production-environment", "chainAttachesTo": { "compatibleTypes": "->Network Node->" }, diff --git a/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Historic-Trades/User.Bot.js b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Historic-Trades/User.Bot.js index 333539f29..b89352ef4 100644 --- a/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Historic-Trades/User.Bot.js +++ b/Legacy-Code/AAMasters/bots/AACharly-Sensor-Bot/Historic-Trades/User.Bot.js @@ -27,6 +27,14 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileSt let initialProcessTimestamp let beginingOfMarket let lastFileSaved + let exchangeId + let options = {} + let fetchType = "by Time" + let lastId + let firstId + let exchange + + const limit = 1000 return thisObject; @@ -37,27 +45,8 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileSt logger.initialize(); statusDependencies = pStatusDependencies; - callBackFunction(global.DEFAULT_OK_RESPONSE); - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function start(callBackFunction) { - try { - - if (global.STOP_TASK_GRACEFULLY === true) { - callBackFunction(global.DEFAULT_OK_RESPONSE); - return - } - - let exchangeId = bot.exchange.toLowerCase() - let options = {} - let fetchType = "by Time" - let lastId - let firstId + exchangeId = bot.exchange.toLowerCase() /* Applying the parameters defined by the user at the Exchange node */ if (bot.exchangeNode.code.API !== undefined) { @@ -87,7 +76,7 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileSt if (key === "undefined") { key = undefined } if (secret === "undefined") { secret = undefined } - const limit = 1000 + const exchangeClass = ccxt[exchangeId] const exchangeConstructorParams = { 'apiKey': key, @@ -98,7 +87,23 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileSt options: options } - const exchange = new exchangeClass(exchangeConstructorParams) + exchange = new exchangeClass(exchangeConstructorParams) + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function start(callBackFunction) { + try { + + if (global.STOP_TASK_GRACEFULLY === true) { + callBackFunction(global.DEFAULT_OK_RESPONSE); + return + } begin() @@ -219,7 +224,7 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileSt if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Trades Fetched to " + endDate) } } - if (trades.length > 1 && allTrades.length < MAX_TRADES_PER_EXECUTION) { + if (trades.length > 1) { previousSince = since since = trades[trades.length - 1]['timestamp'] if (since === previousSince) { @@ -240,20 +245,20 @@ exports.newUserBot = function newUserBot(bot, logger, COMMONS, UTILITIES, fileSt if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] start -> getTrades -> Fetching Trades @ " + processingDate + "-> exchange = " + bot.exchange + " -> symbol = " + symbol + " -> since = " + since + " -> limit = " + limit) } bot.processHeartBeat("Fetching " + allTrades.length.toFixed(0) + " trades from " + bot.exchange + " " + symbol + " @ " + processingDate) // tell the world we are alive and doing well - } else { - break } - if (global.STOP_TASK_GRACEFULLY === true) { + + if ( + trades.length < limit - 1 || + global.STOP_TASK_GRACEFULLY === true || + allTrades.length >= MAX_TRADES_PER_EXECUTION + ) { break } } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] start -> getTrades -> err = " + err.stack); - if (err.message.indexOf('exchange is down or offline') >= 0) { - callBackFunction(global.DEFAULT_RETRY_RESPONSE); - } else { - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } + logger.write(MODULE_NAME, "[ERROR] start -> getTrades -> Retrying Later -> err = " + err.stack); + callBackFunction(global.DEFAULT_RETRY_RESPONSE); + } } diff --git a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulation.js b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulation.js index 21d966750..856a22f61 100644 --- a/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulation.js +++ b/Legacy-Code/AAMasters/plotters/Plotters-Trading-Simulation/TradingSimulation.js @@ -668,7 +668,7 @@ let imagePosition = { x: recordPoint4.x + imageOffset.x, - y: recordPoint4.y + imageOffset.y + y: 0 } imagePosition = canvas.chartSpace.viewport.fitIntoVisibleArea(imagePosition) diff --git a/TaskServer/Assistant.js b/TaskServer/Assistant.js index 00d3176bb..9a98b1cbd 100644 --- a/TaskServer/Assistant.js +++ b/TaskServer/Assistant.js @@ -11,8 +11,7 @@ let thisObject = { dataDependencies: undefined, initialize: initialize, - putPosition: putPosition, - movePosition: movePosition, + createOrder: createOrder, getPositions: getPositions, getBalance: getBalance, getAvailableBalance: getAvailableBalance, @@ -32,7 +31,7 @@ let utilities = UTILITIES.newCloudUtilities(logger); - let exchangePositions = []; // These are the open positions at the exchange at the account the bot is authorized to use. + let openOrders = []; // These are the open positions at the exchange at the account the bot is authorized to use. let openPositions = []; // These are the open positions the bot knows it made by itself. let context; @@ -63,16 +62,10 @@ } case 'Backtest': { - // getMarketRateFromIndicator(); NOTE: This path is disabled since for now we do not allow ticker in simulation. Also it allow us to ship a dataset without bruce validateExchangeSyncronicity(); break; } - case 'Competition': { - getMarketRateFromExchange(); - break; - } - default: { logger.write(MODULE_NAME, "[ERROR] initialize -> Unexpected bot.startMode."); logger.write(MODULE_NAME, "[ERROR] initialize -> bot.startMode = " + bot.startMode); @@ -124,99 +117,6 @@ } } - function getMarketRateFromIndicator() { - - try { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromIndicator -> Entering function."); } - - /* Procedure to get the current market rate. */ - - let key = - bot.marketRateProvider.dataMine + "-" + - bot.marketRateProvider.bot + "-" + - bot.marketRateProvider.product + "-" + - bot.marketRateProvider.dataSet + "-" + - bot.marketRateProvider.dataSetVersion; - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromIndicator -> key = " + key); } - - let dataSet = thisObject.dataDependencies.dataSets.get(key); - - let dateForPath = bot.processDatetime.getUTCFullYear() + '/' + utilities.pad(bot.processDatetime.getUTCMonth() + 1, 2) + '/' + utilities.pad(bot.processDatetime.getUTCDate(), 2); - let fileName = '' + bot.market.baseAsset + '_' + bot.market.quotedAsset + '.json'; - let filePath = bot.marketRateProvider.product + "/" + bot.marketRateProvider.dataSet + "/" + dateForPath; - - dataSet.getTextFile(filePath, fileName, onFileReceived); - - function onFileReceived(err, text) { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromIndicator -> onFileReceived -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] initialize -> getMarketRateFromIndicator -> onFileReceived -> text = " + text); } - - let candleArray; - - if (err.result === global.CUSTOM_FAIL_RESPONSE.result || err.code === "The specified key does not exist." || err.message === "File does not exist.") { // Just past midnight, this file will not exist for a couple of minutes. - - logger.write(MODULE_NAME, "[WARN] initialize -> getMarketRateFromIndicator -> onFileReceived -> err = " + JSON.stringify(err)); - logger.write(MODULE_NAME, "[WARN] initialize -> getMarketRateFromIndicator -> This could happen when there are still holes on trades and the process needs to catch up. "); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - - } - - if (err.result === global.DEFAULT_OK_RESPONSE.result) { - try { - candleArray = JSON.parse(text); - - /* We need to find the candle at which the process is currently running. */ - - for (let i = 0; i < candleArray.length; i++) { - - let candle = { - open: candleArray[i][2], - close: candleArray[i][3], - min: candleArray[i][0], - max: candleArray[i][1], - begin: candleArray[i][4], - end: candleArray[i][5] - }; - - // TODO condition added for when there is no market rate available. - if ((bot.processDatetime.valueOf() >= candle.begin && bot.processDatetime.valueOf() < candle.end) - || (i + 1) === candleArray.length) { - - marketRate = (candle.open + candle.close) / 2; - marketRate = thisObject.truncDecimals(marketRate); - context.newHistoryRecord.marketRate = marketRate; - - ticker = { - bid: marketRate, - ask: marketRate, - last: marketRate - }; - - validateExchangeSyncronicity(); - return; - } - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromIndicator -> onFileReceived -> err = " + JSON.stringify(err)); - logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromIndicator -> onFileReceived -> Asuming this is a temporary situation. Requesting a Retry."); - callBackFunction(global.DEFAULT_RETRY_RESPONSE); - } - } else { - logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromIndicator -> onFileReceived -> err = " + JSON.stringify(err)); - callBackFunction(err); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] initialize -> getMarketRateFromIndicator -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - function validateExchangeSyncronicity() { try { @@ -345,22 +245,15 @@ case "Live": { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Live Mode Detected."); } - exchangeAPI.getOpenPositions(bot.market, onResponse); + exchangeAPI.getOpenOrders(bot.market, onResponse); break; } case "Backtest": { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Backtest Mode Detected."); } - let exchangePositions = []; // We simulate all positions were executed. - onResponse(global.DEFAULT_OK_RESPONSE, exchangePositions); - break; - } - - case "Competition": { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> Competition Mode Detected."); } - exchangeAPI.getOpenPositions(bot.market, onResponse); + let openOrders = []; // We simulate all positions were executed. + onResponse(global.DEFAULT_OK_RESPONSE, openOrders); break; } @@ -372,15 +265,15 @@ } } - function onResponse(err, pExchangePositions) { + function onResponse(err, pOpenOrders) { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> pExchangePositions = " + JSON.stringify(pExchangePositions)); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> pOpenOrders = " + JSON.stringify(pOpenOrders)); } switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] getPositionsAtExchange -> onResponse -> Execution finished well."); } - exchangePositions = pExchangePositions; + openOrders = pOpenOrders; ordersExecutionCheck(callBack); return; } @@ -471,11 +364,11 @@ let position = context.executionContext.positions[i]; let exchangePosition; - for (let j = 0; j < exchangePositions.length; j++) { + for (let j = 0; j < openOrders.length; j++) { - if (position.id === exchangePositions[j].id) { + if (position.id === openOrders[j].id) { - exchangePosition = exchangePositions[j]; + exchangePosition = openOrders[j]; positionFound(); return; } @@ -525,12 +418,12 @@ } - function getPositionTradesAtExchange(pPositionId, innerCallBack) { + function getPositionTradesAtExchange(pOrderId, innerCallBack) { try { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> pPositionId = " + pPositionId); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> pOrderId = " + pOrderId); } /* @@ -543,7 +436,7 @@ case "Live": { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Live Mode Detected."); } - exchangeAPI.getExecutedTrades(pPositionId, onResponse); + exchangeAPI.getOrder(pOrderId, bot.market, onResponse); return; } @@ -558,25 +451,28 @@ for (let i = 0; i < context.executionContext.positions.length; i++) { - let thisPosition = context.executionContext.positions[i]; + let thisOrder = context.executionContext.positions[i]; - if (thisPosition.id === pPositionId) { + if (thisOrder.id === pOrderId) { let feeRate = 0.002; // Default backtesting fee simulation let trade = { id: Math.trunc(Math.random(1) * 1000000), - type: thisPosition.type, - rate: thisPosition.rate.toString(), - amountA: thisObject.truncDecimals(thisPosition.amountA).toString(), - amountB: thisObject.truncDecimals(thisPosition.amountB).toString(), + type: thisOrder.type, + rate: thisOrder.rate.toString(), + amountA: thisObject.truncDecimals(thisOrder.amountA).toString(), + amountB: thisObject.truncDecimals(thisOrder.amountB).toString(), fee: thisObject.truncDecimals(feeRate).toString(), date: (new Date()).valueOf() } trades.push(trade); - onResponse(global.DEFAULT_OK_RESPONSE, trades); + let order = { + trades: trades + } + onResponse(global.DEFAULT_OK_RESPONSE, order); return; } @@ -587,13 +483,6 @@ return; } - case "Competition": { - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Competition Mode Detected."); } - exchangeAPI.getExecutedTrades(pPositionId, onResponse); - return; - } - default: { logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> Unexpected bot.startMode."); logger.write(MODULE_NAME, "[ERROR] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> bot.startMode = " + bot.startMode); @@ -602,7 +491,7 @@ } } - function onResponse(err, pTrades) { + function onResponse(err, order) { try { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Entering function."); } @@ -610,7 +499,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> getPositionTradesAtExchange -> onResponse -> Execution finished well."); } - innerCallBack(pTrades); + innerCallBack(order); } break; case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -637,13 +526,14 @@ } } - function confirmOrderWasExecuted(pTrades) { + function confirmOrderWasExecuted(order) { try { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> pTrades = " + JSON.stringify(pTrades)); } + if (global.LOG_CONTROL[MODULE_NAME].logContent === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasExecuted -> trades = " + JSON.stringify(trades)); } + let trades = order.trades /* To confirm everything is ok, we will add all the amounts on trades asociated to the order and @@ -656,8 +546,8 @@ let sumQuotedAsset = 0; let sumRate = 0; - for (let k = 0; k < pTrades.length; k++) { - let trade = pTrades[k]; + for (let k = 0; k < trades.length; k++) { + let trade = trades[k]; sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(trade.amountA); sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(trade.amountB); sumRate = sumRate + thisObject.truncDecimals(trade.rate); @@ -686,12 +576,12 @@ position.status = "executed"; if (position.type === "sell") { - context.newHistoryRecord.sellExecRate = sumRate / pTrades.length; + context.newHistoryRecord.sellExecRate = sumRate / trades.length; } else { - context.newHistoryRecord.buyExecRate = sumRate / pTrades.length; + context.newHistoryRecord.buyExecRate = sumRate / trades.length; } - applyTradesToContext(pTrades); + applyTradesToContext(trades); let newTransaction = { type: position.type + " executed", @@ -711,11 +601,13 @@ } } - function confirmOrderWasPartiallyExecuted(pTrades) { + function confirmOrderWasPartiallyExecuted(trades) { try { if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] ordersExecutionCheck -> loopBody -> confirmOrderWasPartiallyExecuted -> Entering function."); } + let trades = order.trades + /* To confirm everything is ok, we will add all the amounts on trades plus the remaining amounts @@ -727,8 +619,8 @@ let sumBaseAsset = 0; let sumQuotedAsset = 0; - for (let k = 0; k < pTrades.length; k++) { - let trade = pTrades[k]; + for (let k = 0; k < trades.length; k++) { + let trade = trades[k]; sumBaseAsset = sumBaseAsset + thisObject.truncDecimals(trade.amountA); sumQuotedAsset = sumQuotedAsset + thisObject.truncDecimals(trade.amountB); } @@ -777,7 +669,7 @@ position.amountB = exchangePosition.amountB; position.date = (new Date(exchangePosition.date)).valueOf(); - applyTradesToContext(pTrades); + applyTradesToContext(trades); let newTransaction = { type: position.type + " partially executed", @@ -797,7 +689,7 @@ } } - function applyTradesToContext(pTrades) { + function applyTradesToContext(trades) { try { @@ -805,9 +697,9 @@ /* Here we apply the trades that already happened at the exchange to the balance and available balance of the bot. We also calculate its profits. */ - for (k = 0; k < pTrades.length; k++) { + for (k = 0; k < trades.length; k++) { - let trade = pTrades[k]; + let trade = trades[k]; position.trades.push(trade); @@ -917,10 +809,10 @@ } } - function putPosition(pType, pRate, pAmountA, pAmountB, callBackFunction) { + function createOrder(pType, pRate, pAmountA, pAmountB, callBackFunction) { try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> Entering function."); } /* Removing extra decimals. */ @@ -928,10 +820,10 @@ pAmountA = thisObject.truncDecimals(pAmountA); pAmountB = thisObject.truncDecimals(pAmountB); - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pType = " + pType); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pRate = " + pRate); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountA = " + pAmountA); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> pAmountB = " + pAmountB); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pType = " + pType); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pRate = " + pRate); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pAmountA = " + pAmountA); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> pAmountB = " + pAmountB); } /* Validations that the limits are not surpassed. */ @@ -939,9 +831,9 @@ if (pAmountA > thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset)) { - logger.write(MODULE_NAME, "[ERROR] putPosition -> Input Validations -> pAmountA is grater than the Available Balance."); - logger.write(MODULE_NAME, "[ERROR] putPosition -> Input Validations -> pAmountA = " + pAmountA); - logger.write(MODULE_NAME, "[ERROR] putPosition -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset)); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountA is grater than the Available Balance."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountA = " + pAmountA); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.baseAsset)); let err = { result: global.DEFAULT_FAIL_RESPONSE.result, @@ -957,9 +849,9 @@ if (pAmountB > thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset)) { - logger.write(MODULE_NAME, "[ERROR] putPosition -> Input Validations -> pAmountB is grater than the Available Balance."); - logger.write(MODULE_NAME, "[ERROR] putPosition -> Input Validations -> pAmountB = " + pAmountB); - logger.write(MODULE_NAME, "[ERROR] putPosition -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset)); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountB is grater than the Available Balance."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> pAmountB = " + pAmountB); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Input Validations -> Available Balance = " + thisObject.truncDecimals(context.executionContext.availableBalance.quotedAsset)); let err = { result: global.DEFAULT_FAIL_RESPONSE.result, @@ -977,7 +869,7 @@ case "Live": { - exchangeAPI.putPosition(bot.market, pType, pRate, pAmountA, pAmountB, onResponse); + exchangeAPI.createOrder(bot.market, pType, pRate, pAmountA, pAmountB, onResponse); return; } @@ -985,43 +877,37 @@ if (pRate !== marketRate) { - logger.write(MODULE_NAME, "[WARNING] putPosition -> Input Validations -> putPosition Rate can is different to marketRate while in Backtesting Mode. "); + logger.write(MODULE_NAME, "[WARNING] createOrder -> Input Validations -> createOrder Rate can is different to marketRate while in Backtesting Mode. "); //onResponse(global.DEFAULT_FAIL_RESPONSE, positionId); } let positionId = Math.trunc(Math.random(1) * 1000000); - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> Simulating Exchange Response -> orderId = " + positionId); } onResponse(global.DEFAULT_OK_RESPONSE, positionId); return; } - case "Competition": { - - exchangeAPI.putPosition(bot.market, pType, pRate, pAmountA, pAmountB, onResponse); - return; - } - default: { - logger.write(MODULE_NAME, "[ERROR] putPosition -> Unexpected bot.startMode."); - logger.write(MODULE_NAME, "[ERROR] putPosition -> bot.startMode = " + bot.startMode); + logger.write(MODULE_NAME, "[ERROR] createOrder -> Unexpected bot.startMode."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> bot.startMode = " + bot.startMode); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return; } } - function onResponse(err, pPositionId) { + function onResponse(err, order) { try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> onResponse -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> onResponse -> pPositionId = " + pPositionId); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> Entering function."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> order.id = " + order.id); } switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> onResponse -> Execution finished well."); } + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] createOrder -> onResponse -> Execution finished well."); } let position = { - id: pPositionId, + id: order.id, type: pType, rate: pRate, amountA: pAmountA, @@ -1063,149 +949,25 @@ } break; case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[ERROR] putPosition -> onResponse -> Retry Later. Requesting Execution Retry."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> Retry Later. Requesting Execution Retry."); callBackFunction(global.DEFAULT_RETRY_RESPONSE); return; } break; case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] putPosition -> onResponse -> Operation Failed. Aborting the process."); + logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> Operation Failed. Aborting the process."); callBackFunction(err); return; } break; } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] putPosition -> onResponse -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] createOrder -> onResponse -> err = " + err.stack); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } } catch (err) { - logger.write(MODULE_NAME, "[ERROR] putPosition -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - - function movePosition(pPosition, pNewRate, callBackFunction) { - - try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> pPosition = " + JSON.stringify(pPosition)); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> pNewRate = " + pNewRate); } - - /* Removing extra decimals. */ - pNewRate = thisObject.truncDecimals(pNewRate); - - let newAmountB; - if (pPosition.type === "buy") { - newAmountB = thisObject.truncDecimals(pPosition.amountA / pNewRate); - } else { - newAmountB = pPosition.amountB; - } - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> newAmountB = " + newAmountB); } - - switch (bot.startMode) { - - case "Live": { - - exchangeAPI.movePosition(pPosition, pNewRate, newAmountB, onResponse); - return; - } - - case "Backtest": { - - let positionId = Math.trunc(Math.random(1) * 1000000); - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] putPosition -> Simulating Exchange Response -> orderId = " + positionId); } - onResponse(global.DEFAULT_OK_RESPONSE, positionId); - return; - } - - case "Competition": { - - exchangeAPI.movePosition(pPosition, pNewRate, newAmountB, onResponse); - return; - } - - default: { - logger.write(MODULE_NAME, "[ERROR] movePosition -> Unexpected bot.startMode."); - logger.write(MODULE_NAME, "[ERROR] movePosition -> bot.startMode = " + bot.startMode); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - function onResponse(err, pPositionId) { - - try { - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onResponse -> Entering function."); } - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onResponse -> pPositionId = " + pPositionId); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { // Everything went well, we have the information requested. - - if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { logger.write(MODULE_NAME, "[INFO] movePosition -> onResponse -> Execution finished well."); } - - let newPosition = { - id: pPositionId, - type: pPosition.type, - rate: pNewRate, - amountA: pPosition.amountA, - amountB: newAmountB, - date: (bot.processDatetime.valueOf()), - status: "open", - trades: [] - }; - - let oldPosition = JSON.parse(JSON.stringify(pPosition)); - - /* We need to update the position we have on file. */ - - for (let i = 0; i < context.executionContext.positions.length; i++) { - - if (context.executionContext.positions[i].id === pPosition.id) { - - context.executionContext.positions[i] = newPosition; - - break; - } - } - - let newTransaction = { - type: "movePosition", - oldPosition: oldPosition, - newPosition: newPosition - }; - - context.executionContext.transactions.push(newTransaction); - - context.newHistoryRecord.movedPositions++; - - recalculateRateAverages(); - - callBackFunction(global.DEFAULT_OK_RESPONSE, newPosition); - } - break; - case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[ERROR] movePosition -> onResponse -> Retry Later. Requesting Execution Retry."); - callBackFunction(global.DEFAULT_RETRY_RESPONSE); - return; - } - break; - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - logger.write(MODULE_NAME, "[ERROR] movePosition -> onResponse -> Operation Failed. Aborting the process."); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - break; - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] movePosition -> onResponse -> err = " + err.stack); - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - } - } - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] movePosition -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] createOrder -> err = " + err.stack); callBackFunction(global.DEFAULT_FAIL_RESPONSE); } } diff --git a/TaskServer/Commons.js b/TaskServer/Commons.js index aaf3d3dcf..5ad2fb509 100644 --- a/TaskServer/Commons.js +++ b/TaskServer/Commons.js @@ -71,7 +71,7 @@ return } if (outputDatasetNode.referenceParent.parentNode.dataBuilding === undefined) { - logger.write(MODULE_NAME, "[WARN] start -> Product Definition without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); + logger.write(MODULE_NAME, "[WARN] start -> Product Definition " + outputDatasetNode.referenceParent.parentNode.name + " without a Data Building Procedure. Product Definition = " + JSON.stringify(outputDatasetNode.referenceParent.parentNode)); } if (outputDatasetNode.referenceParent.code.codeName === undefined) { logger.write(MODULE_NAME, "[ERROR] start -> Dataset witn no codeName defined. Product Dataset = " + JSON.stringify(outputDatasetNode.referenceParent)); diff --git a/TaskServer/ExchangeAPI.js b/TaskServer/ExchangeAPI.js new file mode 100644 index 000000000..54c3c52e7 --- /dev/null +++ b/TaskServer/ExchangeAPI.js @@ -0,0 +1,160 @@ +exports.newExchangeAPI = function newExchangeAPI(logger, BOT) { + + let MODULE_NAME = "Exchange API"; + let LOG_INFO = true; + let apiClient; + let bot = BOT + + let thisObject = { + initialize: initialize, + getTicker: getTicker, + getOpenOrders: getOpenOrders, + getOrder: getOrder, + createOrder: createOrder, + getMaxDecimalPositions: getMaxDecimalPositions + }; + + let exchangeId + let options = {} + let exchange + + const ccxt = require('ccxt') + + return thisObject; + + function initialize(callBackFunction) { + try { + exchangeId = bot.exchange.toLowerCase() + + let key = process.env.KEY + let secret = process.env.SECRET + + if (key === "undefined") { key = undefined } + if (secret === "undefined") { secret = undefined } + + const exchangeClass = ccxt[exchangeId] + const exchangeConstructorParams = { + 'apiKey': key, + 'secret': secret, + 'timeout': 30000, + 'enableRateLimit': true, + verbose: false, + options: options + } + + exchange = new exchangeClass(exchangeConstructorParams) + + callBackFunction(global.DEFAULT_OK_RESPONSE) + } catch (err) { + logError("initialize -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getMaxDecimalPositions() { + return 8 + } + + async function getTicker(market, callBackFunction) { + try { + logInfo("getTicker -> Entering function."); + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + let ticker + + if (exchange.has['fetchTicker']) { + ticker = await (exchange.fetchTicker(symbol)) + callBackFunction(global.DEFAULT_OK_RESPONSE, ticker) + } else { + logError("getTicker -> Exchange does not support fetchTicker command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + } catch (err) { + logError("getTicker -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + async function getOpenOrders(market, callBackFunction) { + try { + logInfo("getOpenOrders -> Entering function. market = " + JSON.stringify(market)); + + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + let openOrders + + if (exchange.has['fetchOpenOrders']) { + openOrders = await(exchange.fetchOpenOrders (symbol)) + callBackFunction(global.DEFAULT_OK_RESPONSE, openOrders) + } else { + logError("getOpenOrders -> Exchange does not support fetchOpenOrders command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + } catch (err) { + logError("getOpenOrders -> Error = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + async function getOrder(orderId, market, callBackFunction) { + try { + logInfo("getOrder -> Entering function. orderId = " + orderId); + + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + let order + + if (exchange.has['fetchOrder']) { + order = await (exchange.fetchOrder(orderId, symbol)) + callBackFunction(global.DEFAULT_OK_RESPONSE, order) + } else { + logError("getOrder -> Exchange does not support fetchOrder command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } catch (err) { + logError("getOrder -> Error = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + async function createOrder(market, side, price, cost, amount, callBackFunction) { + try { + logInfo("createOrder -> Entering function."); + + const symbol = bot.market.baseAsset + '/' + bot.market.quotedAsset + + logInfo("createOrder -> market = " + market.assetA + "_" + market.assetB); + logInfo("createOrder -> side = " + side); + logInfo("createOrder -> cost = " + cost); + logInfo("createOrder -> amount = " + amount); + logInfo("createOrder -> price = " + price); + + if (side !== "buy" && side !== "sell") { + logError("createOrder -> side must be either 'buy' or 'sell'."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + let type = 'market' + let order + + if (exchange.has['createOrder']) { + order = await(exchange.createOrder(symbol, type, side, amount)) + callBackFunction(global.DEFAULT_OK_RESPONSE, order) + } else { + logError("createOrder -> Exchange does not support createOrder command."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + } catch (err) { + logError("createOrder -> err = " + JSON.stringify(err.stack) || err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function logInfo(message) { + if (LOG_INFO === true) { logger.write(MODULE_NAME, '[INFO] ' + message) } + } + + function logError(message) { + logger.write(MODULE_NAME, '[ERROR] ' + message) + } +}; diff --git a/TaskServer/MultiPeriod.js b/TaskServer/MultiPeriod.js index 94a1cd318..2ad6c0757 100644 --- a/TaskServer/MultiPeriod.js +++ b/TaskServer/MultiPeriod.js @@ -29,7 +29,7 @@ return thisObject; - function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, pAssistant, callBackFunction) { + function initialize(pProcessConfig, pStatusDependencies, pDataDependencies, exchangeAPI, callBackFunction) { try { @@ -63,7 +63,7 @@ let USER_BOT_MODULE = require("./TradingBot") botInstance = USER_BOT_MODULE.newTradingBot(bot, logger, UTILITIES, FILE_STORAGE); - botInstance.initialize(pAssistant, callBackFunction); + botInstance.initialize(exchangeAPI, callBackFunction); } catch (err) { logger.write(MODULE_NAME, "[ERROR] initialize -> err = " + err.stack); diff --git a/TaskServer/Session.js b/TaskServer/Session.js index 0aaf44d55..3d61e42aa 100644 --- a/TaskServer/Session.js +++ b/TaskServer/Session.js @@ -2,6 +2,7 @@ const MODULE_NAME = "Session" const FULL_LOG = true; + const ONE_YEAR_IN_MILISECONDS = 365 * 24 * 60 * 60 * 1000 let thisObject = { initialize: initialize @@ -97,6 +98,17 @@ bot.STOP_SESSION = true } + function checkDatetimes() { + if (bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() < (new Date()).valueOf()) { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Overriding initialDatetime with present datetime because " + bot.VALUES_TO_USE.timeRange.initialDatetime + " is in the past."); } + bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() + } + if (bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf() < (new Date()).valueOf()) { + if (FULL_LOG === true) { parentLogger.write(MODULE_NAME, "[WARN] initialize -> startLiveTrading -> Overriding finalDatetime with present datetime plus one year because " + bot.VALUES_TO_USE.timeRange.finalDatetime + " is in the past."); } + bot.VALUES_TO_USE.timeRange.finalDatetime = new Date() + ONE_YEAR_IN_MILISECONDS + } + } + function startBackTesting(message) { bot.startMode = "Backtest" if (bot.VALUES_TO_USE.timeRange.finalDatetime.valueOf() > (new Date()).valueOf()) { @@ -117,9 +129,7 @@ } bot.startMode = "Live" - if (bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() < (new Date()).valueOf()) { - bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() - } + checkDatetimes() bot.resumeExecution = false; bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) bot.hasTheBotJustStarted = true @@ -135,9 +145,7 @@ } bot.startMode = "Live" - if (bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() < (new Date()).valueOf()) { - bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() - } + checkDatetimes() bot.resumeExecution = false; bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) bot.hasTheBotJustStarted = true @@ -164,9 +172,7 @@ function startPaperTrading(message) { bot.startMode = "Backtest" - if (bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf() < (new Date()).valueOf()) { - bot.VALUES_TO_USE.timeRange.initialDatetime = new Date() - } + checkDatetimes() bot.resumeExecution = false; bot.hasTheBotJustStarted = true bot.multiPeriodProcessDatetime = new Date(bot.VALUES_TO_USE.timeRange.initialDatetime.valueOf()) @@ -204,7 +210,7 @@ } /* Session Type Dependant Default Values */ - const ONE_YEAR_IN_MILISECONDS = 365 * 24 * 60 * 60 * 1000 + switch (bot.SESSION.type) { case 'Backtesting Session': { bot.VALUES_TO_USE.timeRange.initialDatetime = new Date((new Date()).valueOf() - ONE_YEAR_IN_MILISECONDS) @@ -247,7 +253,9 @@ bot.VALUES_TO_USE.baseAsset = code.codeName; } - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + code = tradingSystem.parameters.baseAsset.code + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { if (code.initialBalance !== undefined) { bot.VALUES_TO_USE.initialBalanceA = code.initialBalance; bot.VALUES_TO_USE.initialBalanceB = 0 @@ -347,16 +355,18 @@ /* Base Asset and Initial Balances. */ { if (bot.SESSION.parameters.baseAsset !== undefined) { - if (tradingSystem.parameters.baseAsset.referenceParent !== undefined) { - if (tradingSystem.parameters.baseAsset.referenceParent.referenceParent !== undefined) { + if (bot.SESSION.parameters.baseAsset.referenceParent !== undefined) { + if (bot.SESSION.parameters.baseAsset.referenceParent.referenceParent !== undefined) { - let code = tradingSystem.parameters.baseAsset.referenceParent.referenceParent.code + let code = bot.SESSION.parameters.baseAsset.referenceParent.referenceParent.code if (code.codeName !== undefined) { bot.VALUES_TO_USE.baseAsset = code.codeName; } - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + code = bot.SESSION.parameters.baseAsset.code + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { if (code.initialBalance !== undefined) { bot.VALUES_TO_USE.initialBalanceA = code.initialBalance; bot.VALUES_TO_USE.initialBalanceB = 0 diff --git a/TaskServer/TradingBot.js b/TaskServer/TradingBot.js index e969a90fc..3129df6d7 100644 --- a/TaskServer/TradingBot.js +++ b/TaskServer/TradingBot.js @@ -18,7 +18,7 @@ const COMMONS = require('./Commons.js'); let commons = COMMONS.newCommons(bot, logger, UTILITIES, FILE_STORAGE); - let assistant; + let exchangeAPI; return thisObject; @@ -29,7 +29,7 @@ commons = undefined } - function initialize(pAssistant, callBackFunction) { + function initialize(pExchangeAPI, callBackFunction) { try { @@ -38,7 +38,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] initialize -> Entering function."); } - assistant = pAssistant; + exchangeAPI = pExchangeAPI; callBackFunction(global.DEFAULT_OK_RESPONSE); } catch (err) { @@ -130,7 +130,7 @@ timeFrameLabel, currentDay, interExecutionMemory, - assistant, + exchangeAPI, writeFiles, callBackFunction) diff --git a/TaskServer/TradingBotProcessMainLoop.js b/TaskServer/TradingBotProcessMainLoop.js index e2ef012a6..85ae4182d 100644 --- a/TaskServer/TradingBotProcessMainLoop.js +++ b/TaskServer/TradingBotProcessMainLoop.js @@ -145,9 +145,7 @@ /* We define here all the modules that the rest of the infraestructure, including the bots themselves can consume. */ const UTILITIES = require(ROOT_DIR + 'CloudUtilities'); - const EXCHANGE_API = require('@superalgos/exchange-gateway'); - const CONTEXT = require(ROOT_DIR + 'Context'); - const ASSISTANT = require(ROOT_DIR + 'Assistant'); + const EXCHANGE_API = require(ROOT_DIR + 'ExchangeAPI'); const STATUS_REPORT = require(ROOT_DIR + 'StatusReport'); const STATUS_DEPENDENCIES = require(ROOT_DIR + 'StatusDependencies'); const DATA_DEPENDENCIES = require(ROOT_DIR + 'DataDependencies'); @@ -443,7 +441,7 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeDataDependencies -> onInizialized -> Execution finished well."); } - initializeContext(); + initializeExchangeAPI(); return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -500,88 +498,13 @@ } } - function initializeContext() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> Entering function."); } - - context = CONTEXT.newContext(bot, logger, UTILITIES); - context.initialize(statusDependencies, onInizialized); - - function onInizialized(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> Entering function."); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeContext -> onInizialized -> Execution finished well."); } - initializeExchangeAPI(); - return; - } - case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeContext -> onInizialized -> Retry Later. Requesting Execution Retry."); - nextWaitTime = 'Retry'; - loopControl(nextWaitTime); - return; - } - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - sessionStopped() - processStopped() - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Operation Failed. Aborting the process."); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - return; - } - default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> Unhandled err.result received. -> err = " + err.message); - - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> onInizialized -> err = "+ err.stack); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeContext -> err = "+ err.stack); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - } - } - function initializeExchangeAPI() { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> Entering function."); } - exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, bot.exchange); + exchangeAPI = EXCHANGE_API.newExchangeAPI(logger, bot); exchangeAPI.initialize(onInizialized); @@ -594,7 +517,25 @@ switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeExchangeAPI -> onInizialized -> Execution finished well."); } - initializeAssistant(); + + switch (processConfig.framework.name) { + case 'Multi-Period': { + processFramework = MULTI_PERIOD.newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS); + intitializeProcessFramework(); + break; + } + default: { + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Process Framework not Supported."); + logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeExchangeAPI -> onInizialized -> Process Framework Name = " + processConfig.framework.name); + logger.persist(); + clearInterval(fixedTimeLoopIntervalHandle); + clearTimeout(nextLoopTimeoutHandle); + clearTimeout(checkLoopHealthHandle); + bot.enableCheckLoopHealth = false; + callBackFunction(err); + return; + } + } return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -651,112 +592,13 @@ } } - function initializeAssistant() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> Entering function."); } - - assistant = ASSISTANT.newAssistant(bot, logger, UTILITIES); - assistant.initialize(context, exchangeAPI, dataDependencies, onInizialized); - - function onInizialized(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> onInizialized -> Entering function."); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> initializeAssistant -> onInizialized -> Execution finished well."); } - - /* If the process is configured to run inside a framework, we continue there, otherwise we run the bot directly. */ - if (processConfig.framework === undefined) { - initializeUserBot(); - return; - } - - switch (processConfig.framework.name) { - case 'Multi-Period': { - processFramework = MULTI_PERIOD.newMultiPeriod(bot, logger, UTILITIES, USER_BOT_MODULE, USER_BOT_COMMONS); - intitializeProcessFramework(); - break; - } - default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Process Framework not Supported."); - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Process Framework Name = " + processConfig.framework.name); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - return; - } - } - return; - } - case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[WARN] run -> loop -> initializeAssistant -> onInizialized -> Retry Later. Requesting Execution Retry."); - nextWaitTime = 'Retry'; - loopControl(nextWaitTime); - return; - } - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - sessionStopped() - processStopped() - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Operation Failed. Aborting the process."); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - return; - } - default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Unhandled err.result received. -> err.result = " + err.result); - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> Unhandled err.result received. -> err = " + err.message); - - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> onInizialized -> err = "+ err.stack); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> initializeAssistant -> err = "+ err.stack); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - } - } - function intitializeProcessFramework() { try { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> intitializeProcessFramework -> Entering function."); } - processFramework.initialize(processConfig, statusDependencies, dataDependencies, assistant, onInizialized); + processFramework.initialize(processConfig, statusDependencies, dataDependencies, exchangeAPI, onInizialized); function onInizialized(err) { @@ -859,7 +701,7 @@ case global.DEFAULT_OK_RESPONSE.result: { logger.write(MODULE_NAME, "[INFO] run -> loop -> startProcessFramework -> onFinished -> Execution finished well."); nextWaitTime = 'Normal'; - saveContext(); + raiseEventsProcessOutput() return; } case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. @@ -943,84 +785,6 @@ } } - function saveContext() { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> Entering function."); } - - context.saveThemAll(onFinished); - - function onFinished(err) { - - try { - - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> onFinished -> Entering function."); } - - switch (err.result) { - case global.DEFAULT_OK_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] run -> loop -> saveContext -> onFinished -> Execution finished well."); } - raiseEventsProcessOutput() - return; - } - case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. - logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Can not retry at this point."); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. - sessionStopped() - processStopped() - logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Operation Failed. Aborting the process."); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - return; - } - default: { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Unhandled err.result received. -> err.result = " + err.result); - logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> Unhandled err.result received. -> err = " + err.message); - - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(global.DEFAULT_FAIL_RESPONSE); - return; - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> onFinished -> err = "+ err.stack); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - } - } - - } catch (err) { - logger.write(MODULE_NAME, "[ERROR] run -> loop -> saveContext -> err = "+ err.stack); - logger.persist(); - clearInterval(fixedTimeLoopIntervalHandle); - clearTimeout(nextLoopTimeoutHandle); - clearTimeout(checkLoopHealthHandle); - bot.enableCheckLoopHealth = false; - callBackFunction(err); - } - } - function raiseEventsProcessOutput() { try { diff --git a/TaskServer/TradingSimulation.js b/TaskServer/TradingSimulation.js index 0573fe091..06c48ab4a 100644 --- a/TaskServer/TradingSimulation.js +++ b/TaskServer/TradingSimulation.js @@ -39,7 +39,7 @@ timeFrameLabel, currentDay, interExecutionMemory, - assistant, + exchangeAPI, callback, callBackFunction) { @@ -167,6 +167,8 @@ let periods = 0; let positionPeriods = 0; + let closeRate + /* Usefull counters for conditions and formulas */ let distanceToLast = { @@ -554,8 +556,6 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle Begin @ " + (new Date(candle.begin)).toLocaleString()) } if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Candle End @ " + (new Date(candle.end)).toLocaleString()) } - /* Assistant Info */ - let ticker = { bid: candle.close, ask: candle.close, @@ -706,7 +706,7 @@ if (openStage !== undefined) { /* Default Values*/ - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { positionSize = balanceBaseAsset; positionRate = candle.close; } else { @@ -729,13 +729,13 @@ initialDefinition.positionSize.formula.error = err.message } if (isNaN(positionSize)) { - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { positionSize = balanceBaseAsset; } else { positionSize = balanceQuotedAsset; } } else { - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { if (positionSize > balanceBaseAsset) { positionSize = balanceBaseAsset } } else { if (positionSize > balanceQuotedAsset) { positionSize = balanceQuotedAsset } @@ -756,7 +756,7 @@ initialDefinition.positionRate.formula.error = err.message } if (isNaN(positionRate)) { - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { positionRate = candle.close; } else { positionRate = candle.close; @@ -1093,7 +1093,7 @@ let maximumBalance let balance - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { balance = balanceBaseAsset minimumBalance = bot.VALUES_TO_USE.minimumBalanceA maximumBalance = bot.VALUES_TO_USE.maximumBalanceA @@ -1675,7 +1675,7 @@ /* Stop Loss condition: Here we verify if the Stop Loss was hitted or not. */ - if ((bot.VALUES_TO_USE.baseAsset === 'BTC' && candle.max >= stopLoss) || (bot.VALUES_TO_USE.baseAsset !== 'BTC' && candle.min <= stopLoss)) { + if ((bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset && candle.max >= stopLoss) || (bot.VALUES_TO_USE.baseAsset !== bot.market.baseAsset && candle.min <= stopLoss)) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Stop Loss was hit."); } /* @@ -1685,7 +1685,7 @@ If we take the stop loss value at those situation would be a huge distortion of facts. */ - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { if (stopLoss < candle.min) { stopLoss = candle.min if (processingDailyFiles) { @@ -1710,42 +1710,15 @@ /* Apply the Slippage */ let slippageAmount = slippedStopLoss * bot.VALUES_TO_USE.slippage.stopLoss / 100 - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { slippedStopLoss = slippedStopLoss + slippageAmount } else { slippedStopLoss = slippedStopLoss - slippageAmount } - let finalStopLoss = slippedStopLoss; + closeRate = slippedStopLoss; - let feePaid = 0 - - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { - strategy.positionSize = balanceQuotedAsset / finalStopLoss; - strategy.positionRate = finalStopLoss; - - feePaid = balanceQuotedAsset / finalStopLoss * bot.VALUES_TO_USE.feeStructure.taker / 100 - - balanceBaseAsset = balanceBaseAsset + balanceQuotedAsset / finalStopLoss - feePaid; - balanceQuotedAsset = 0; - } else { - strategy.positionSize = balanceBaseAsset * finalStopLoss; - strategy.positionRate = finalStopLoss; - - feePaid = balanceBaseAsset * finalStopLoss * bot.VALUES_TO_USE.feeStructure.taker / 100 - - balanceQuotedAsset = balanceQuotedAsset + balanceBaseAsset * finalStopLoss - feePaid; - balanceBaseAsset = 0; - } - - if (processingDailyFiles) { - if (positionedAtYesterday) { - yesterday.balanceBaseAsset = balanceBaseAsset; - yesterday.balanceQuotedAsset = balanceQuotedAsset; - } - } - - marketRate = finalStopLoss; + marketRate = closeRate; type = '"Close@StopLoss"'; strategyStage = 'Close Stage'; stopLossStage = 'No Stage'; @@ -1753,7 +1726,7 @@ currentTrade.end = candle.end; currentTrade.status = 1; currentTrade.exitType = 1; - currentTrade.endRate = finalStopLoss; + currentTrade.endRate = closeRate; closePositionNow = true; @@ -1772,7 +1745,7 @@ /* Take Profit condition: Here we verify if the Take Profit was hit or not. */ - if ((bot.VALUES_TO_USE.baseAsset === 'BTC' && candle.min <= takeProfit) || (bot.VALUES_TO_USE.baseAsset !== 'BTC' && candle.max >= takeProfit)) { + if ((bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset && candle.min <= takeProfit) || (bot.VALUES_TO_USE.baseAsset !== bot.market.baseAsset && candle.max >= takeProfit)) { if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> Take Profit was hit."); } /* @@ -1782,7 +1755,7 @@ If we take the stop loss value at those situation would be a huge distortion of facts. */ - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { if (takeProfit > candle.max) { takeProfit = candle.max if (processingDailyFiles) { @@ -1806,43 +1779,15 @@ /* Apply the Slippage */ let slippageAmount = slippedTakeProfit * bot.VALUES_TO_USE.slippage.takeProfit / 100 - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { slippedTakeProfit = slippedTakeProfit + slippageAmount } else { slippedTakeProfit = slippedTakeProfit - slippageAmount } - let finalTakeProfit = slippedTakeProfit; + closeRate = slippedTakeProfit; - let feePaid = 0 - - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { - strategy.positionSize = balanceQuotedAsset / finalTakeProfit; - strategy.positionRate = finalTakeProfit; - - feePaid = balanceQuotedAsset / finalTakeProfit * bot.VALUES_TO_USE.feeStructure.taker / 100 - - balanceBaseAsset = balanceBaseAsset + balanceQuotedAsset / finalTakeProfit - feePaid; - balanceQuotedAsset = 0; - } else { - strategy.positionSize = balanceBaseAsset * finalTakeProfit; - strategy.positionRate = finalTakeProfit; - - feePaid = balanceBaseAsset * finalTakeProfit * bot.VALUES_TO_USE.feeStructure.taker / 100 - - balanceQuotedAsset = balanceQuotedAsset + balanceBaseAsset * finalTakeProfit - feePaid; - balanceBaseAsset = 0; - } - - if (processingDailyFiles) { - if (positionedAtYesterday) { - yesterday.balanceBaseAsset = balanceBaseAsset; - yesterday.balanceQuotedAsset = balanceQuotedAsset; - - } - } - - marketRate = finalTakeProfit; + marketRate = closeRate; type = '"Close@TakeProfit"'; strategyStage = 'Close Stage'; stopLossStage = 'No Stage'; @@ -1851,7 +1796,7 @@ currentTrade.end = candle.end; currentTrade.status = 1; currentTrade.exitType = 2; - currentTrade.endRate = finalTakeProfit; + currentTrade.endRate = closeRate; closePositionNow = true; @@ -1895,7 +1840,7 @@ /* We take what was calculated at the formula and apply the slippage. */ let slippageAmount = tradePositionRate * bot.VALUES_TO_USE.slippage.positionRate / 100 - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { tradePositionRate = tradePositionRate - slippageAmount } else { tradePositionRate = tradePositionRate + slippageAmount @@ -1993,20 +1938,7 @@ } } - - /* Checking the status of current positions */ - let positions = assistant.getPositions(); - if (positions.length > 0) { - let position = positions[positions.length - 1] // We are allways checking the the last position is not open. - if (position.status === 'open') { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Not placing the trade at the exchange because the last position is still open. "); } - afterLoop(); - return - } - } - /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ - if (interExecutionMemory.executionContext !== undefined) { if (interExecutionMemory.executionContext.periods !== undefined) { if (periods <= interExecutionMemory.executionContext.periods) { @@ -2030,34 +1962,28 @@ } } - let openPositionRate + let orderPrice let amountA let amountB - let positionDirection - let availableBalance = assistant.getAvailableBalance() + let orderSide + - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { - positionDirection = "sell" + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + orderSide = "sell" - openPositionRate = tradePositionRate - 100 // 100 is Provisional since we do have management of orders yet + orderPrice = tradePositionRate - 100 // This is going to be ingnored at the Exchange API for now since we only put market orders. - amountA = tradePositionSize * openPositionRate + amountA = tradePositionSize * orderPrice amountB = tradePositionSize - - if (amountB > availableBalance.quotedAsset) { // The assistant know what fees were paid. - amountB = availableBalance.quotedAsset - } + } else { - positionDirection = "buy" + orderSide = "buy" - openPositionRate = tradePositionRate + 100 // 100 is Provisional since we do have management of orders yet + orderPrice = tradePositionRate // This is going to be ingnored at the Exchange API for now since we only put market orders. amountA = tradePositionSize - amountB = tradePositionSize / openPositionRate + amountB = tradePositionSize / orderPrice - if (amountA > availableBalance.baseAsset) { // The assistant know what fees were paid. - amountA = availableBalance.baseAsset - } } interExecutionMemory.executionContext = { @@ -2065,46 +1991,47 @@ periods: periods, } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to put order."); } - assistant.putPosition(positionDirection, openPositionRate, amountA, amountB, onOrderPut) + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> Ready to create order."); } + exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated) - function onOrderPut(err) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Entering function."); } + function onOrderCreated(err, order) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Entering function."); } try { switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE "); } interExecutionMemory.executionContext = { status: "In a Position", periods: periods, amountA: amountA, - amountB: amountB + amountB: amountB, + orderId: order.id } takePositionAtSimulation() return; } case global.DEFAULT_FAIL_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Message = " + err.message); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = " + err.message); } strategy.openStage.openExecution.error = err.message afterLoop() return; } case global.DEFAULT_RETRY_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Message = " + err.message); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Message = " + err.message); } strategy.openStage.openExecution.error = err.message afterLoop() return; } } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> Unexpected Response -> Message = " + err.message); } callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } catch (err) { - logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> err = " + err.stack); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } @@ -2135,7 +2062,7 @@ let feePaid = 0 - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { feePaid = tradePositionSize * tradePositionRate * bot.VALUES_TO_USE.feeStructure.taker / 100 @@ -2231,19 +2158,7 @@ if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> putClosingOrder -> Entering function."); } - /* Checking the status of current positions */ - let positions = assistant.getPositions(); - if (positions.length > 0) { - let position = positions[positions.length - 1] // We are allways checking the the last position is not open. - if (position.status === 'open') { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> Exiting function because status of last position is Open."); } - afterLoop(); - return - } - } - /* Mechanism to avoid putting the same order over and over again at different executions of the simulation engine. */ - if (interExecutionMemory.executionContext !== undefined) { if (interExecutionMemory.executionContext.periods !== undefined) { if (periods <= interExecutionMemory.executionContext.periods) { @@ -2254,27 +2169,26 @@ } } - let closePositionRate + let orderPrice let amountA let amountB - let positionDirection - let availableBalance = assistant.getAvailableBalance() + let orderSide - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { - positionDirection = "buy" + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + orderSide = "buy" - closePositionRate = ticker.last + 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + orderPrice = ticker.last + 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. - amountA = availableBalance.baseAsset - amountB = availableBalance.baseAsset / closePositionRate + amountA = balanceQuotedAsset + amountB = balanceQuotedAsset / orderPrice } else { - positionDirection = "sell" + orderSide = "sell" - closePositionRate = ticker.last - 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. + orderPrice = ticker.last - 100; // This is provisional and totally arbitrary, until we have a formula on the designer that defines this stuff. - amountA = availableBalance.quotedAsset * closePositionRate - amountB = availableBalance.quotedAsset + amountA = balanceBaseAsset * orderPrice + amountB = balanceBaseAsset } @@ -2284,44 +2198,45 @@ } if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> About to close position at the exchange."); } - assistant.putPosition(positionDirection, closePositionRate, amountA, amountB, onOrderPut) + exchangeAPI.createOrder(bot.market, orderSide, orderPrice, amountA, amountB, onOrderCreated) - function onOrderPut(err) { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> Entering function."); } + function onOrderCreated(err, order) { + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Entering function."); } try { switch (err.result) { case global.DEFAULT_OK_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> DEFAULT_OK_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_OK_RESPONSE "); } interExecutionMemory.executionContext = { status: "Position Closed", periods: periods, amountA: amountA, - amountB: amountB + amountB: amountB, + orderId: order.id } closePositionAtSimulation() return; } case global.DEFAULT_FAIL_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderPut -> DEFAULT_FAIL_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> DEFAULT_FAIL_RESPONSE "); } /* We will assume that the problem is temporary, and expect that it will work at the next execution.*/ strategy.closeStage.closeExecution.error = err.message afterLoop() return; } case global.DEFAULT_RETRY_RESPONSE.result: { - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderPut -> DEFAULT_RETRY_RESPONSE "); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[INFO] runSimulation -> loop -> putOpeningOrder -> onOrderCreated -> DEFAULT_RETRY_RESPONSE "); } strategy.closeStage.closeExecution.error = err.message afterLoop() return; } } - if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderPut -> Unexpected Response -> Message = " + err.message); } + if (FULL_LOG === true) { logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> Unexpected Response -> Message = " + err.message); } callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } catch (err) { - logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderPut -> err = " + err.stack); + logger.write(MODULE_NAME, "[ERROR] runSimulation -> loop -> putClosingOrder -> onOrderCreated -> err = " + err.stack); callBackFunction(global.DEFAULT_FAIL_RESPONSE); return } @@ -2339,7 +2254,35 @@ } } - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + let feePaid = 0 + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { + strategy.positionSize = balanceQuotedAsset / closeRate; + strategy.positionRate = closeRate; + + feePaid = balanceQuotedAsset / closeRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceBaseAsset = balanceBaseAsset + balanceQuotedAsset / closeRate - feePaid; + balanceQuotedAsset = 0; + } else { + strategy.positionSize = balanceBaseAsset * closeRate; + strategy.positionRate = closeRate; + + feePaid = balanceBaseAsset * closeRate * bot.VALUES_TO_USE.feeStructure.taker / 100 + + balanceQuotedAsset = balanceQuotedAsset + balanceBaseAsset * closeRate - feePaid; + balanceBaseAsset = 0; + } + + if (processingDailyFiles) { + if (positionedAtYesterday) { + yesterday.balanceBaseAsset = balanceBaseAsset; + yesterday.balanceQuotedAsset = balanceQuotedAsset; + + } + } + + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { lastTradeProfitLoss = balanceBaseAsset - previousBalanceBaseAsset; lastTradeROI = lastTradeProfitLoss * 100 / tradePositionSize; if (isNaN(lastTradeROI)) { lastTradeROI = 0; } @@ -2386,7 +2329,7 @@ } } - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { ROI = (bot.VALUES_TO_USE.initialBalanceA + profit) / bot.VALUES_TO_USE.initialBalanceA - 1; hitRatio = hits / roundtrips; anualizedRateOfReturn = ROI / days * 365; @@ -2652,7 +2595,7 @@ currentTrade.endRate = candle.close /* Here we will calculate the ongoing ROI */ - if (bot.VALUES_TO_USE.baseAsset === 'BTC') { + if (bot.VALUES_TO_USE.baseAsset === bot.market.baseAsset) { currentTrade.lastTradeROI = (tradePositionRate - candle.close) / tradePositionRate * 100 } else { currentTrade.lastTradeROI = (candle.close - tradePositionRate) / tradePositionRate * 100 diff --git a/TaskServer/package-lock.json b/TaskServer/package-lock.json index e576ae33f..fbf97a4f3 100644 --- a/TaskServer/package-lock.json +++ b/TaskServer/package-lock.json @@ -1,212 +1,19 @@ { - "name": "@superalgos/clone-executor", - "version": "1.0.51", + "name": "@superalgos/taskserver", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { - "@superalgos/exchange-gateway": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@superalgos/exchange-gateway/-/exchange-gateway-0.0.10.tgz", - "integrity": "sha512-VsiGeEyqD7NXvn2tFoqqz036WRBtzy6HvpbXqhl/+xG4SMbo2w9hb6AdAeH4KxuU2S7l/oO8zI9d15f30Q191Q==", - "requires": { - "axios": "0.19.0", - "dotenv": "8.0.0", - "nonce": "1.0.4", - "querystring": "0.2.0", - "request": "2.88.0", - "retry": "0.12.0", - "superagent": "5.1.0", - "underscore": "1.9.1" - } - }, - "@superalgos/mqservice": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@superalgos/mqservice/-/mqservice-0.0.17.tgz", - "integrity": "sha512-mxg2AtfFJ78n/p+JK/o6nTrnR+ZPlBNcq7k+6Epnl1t63ldrcm98WOgIQZaNezPPf0E7gSjVX3p+uZO1Ass0kA==" - }, "@types/node": { "version": "12.7.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz", "integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==" }, - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "requires": { - "fast-deep-equal": "2.0.1", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" - } - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "2.1.2" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "requires": { - "lodash": "4.17.11" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "axios": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", - "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", - "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "2.0.3" - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "0.14.5" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "ccxt": { "version": "1.21.82", "resolved": "https://registry.npmjs.org/ccxt/-/ccxt-1.21.82.tgz", "integrity": "sha512-+HX/6WK8BM5c9OwhALl9zzMKvueZfHDXVmfrKNmQ4005hc+BtNuNe47XEs6dAylY+ll7n9JpzaoIZhy6Lipiew==" }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "1.9.3", - "color-string": "1.5.3" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "1.1.3", - "simple-swizzle": "0.2.2" - } - }, - "colornames": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", - "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" - }, - "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.0", - "text-hex": "1.0.0" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "diagnostics": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", - "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", - "requires": { - "colorspace": "1.1.2", - "enabled": "1.0.2", - "kuler": "1.0.1" - } - }, "dotenv": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz", @@ -217,153 +24,11 @@ "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=" }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "0.1.1", - "safer-buffer": "2.1.2" - } - }, - "enabled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", - "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", - "requires": { - "env-variable": "0.0.5" - } - }, - "env-variable": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", - "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" - }, "event-pubsub": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==" }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-safe-stringify": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", - "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" - }, - "fecha": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", - "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "3.1.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.8", - "mime-types": "2.1.24" - } - }, - "formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "6.10.2", - "har-schema": "2.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.16.1" - } - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "js-message": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", @@ -377,97 +42,6 @@ "easy-stack": "1.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "kuler": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", - "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", - "requires": { - "colornames": "1.1.1" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - }, - "logform": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", - "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", - "requires": { - "colors": "1.3.3", - "fast-safe-stringify": "2.0.6", - "fecha": "2.3.3", - "ms": "2.1.1", - "triple-beam": "1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", @@ -483,195 +57,11 @@ "js-queue": "2.0.0" } }, - "nonce": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nonce/-/nonce-1.0.4.tgz", - "integrity": "sha1-7nMCrejBvvR28wG4yR9cxRpIdhI=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "one-time": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" - }, - "psl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", - "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "2.0.3", - "string_decoder": "1.2.0", - "util-deprecate": "1.0.2" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.8.0", - "caseless": "0.12.0", - "combined-stream": "1.0.8", - "extend": "3.0.2", - "forever-agent": "0.6.1", - "form-data": "2.3.3", - "har-validator": "5.1.3", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.24", - "oauth-sign": "0.9.0", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.2", - "tough-cookie": "2.4.3", - "tunnel-agent": "0.6.0", - "uuid": "3.3.2" - } - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, "sandwich-stream": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==" }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "0.3.2" - } - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "0.2.4", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.2", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.2", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "safer-buffer": "2.1.2", - "tweetnacl": "0.14.5" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", - "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "superagent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.1.0.tgz", - "integrity": "sha512-7V6JVx5N+eTL1MMqRBX0v0bG04UjrjAvvZJTF/VDH/SH2GjSLqlrcYepFlpTrXpm37aSY6h3GGVWGxXl/98TKA==", - "requires": { - "component-emitter": "1.3.0", - "cookiejar": "2.1.2", - "debug": "4.1.1", - "fast-safe-stringify": "2.0.6", - "form-data": "2.3.3", - "formidable": "1.2.1", - "methods": "1.1.2", - "mime": "2.4.4", - "qs": "6.7.0", - "readable-stream": "3.4.0", - "semver": "6.3.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - } - } - }, "telegraf": { "version": "3.32.0", "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.32.0.tgz", @@ -703,132 +93,6 @@ "version": "3.6.1", "resolved": "https://registry.npmjs.org/telegram-typings/-/telegram-typings-3.6.1.tgz", "integrity": "sha512-njVv1EAhIZnmQVLocZEADYUyqA1WIXuVcDYlsp+mXua/XB0pxx+PKtMSPeZ/EE4wPWTw9h/hA9ASTT6yQelkiw==" - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "1.2.0", - "punycode": "1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "2.1.1" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, - "winston": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", - "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", - "requires": { - "async": "2.6.2", - "diagnostics": "1.1.1", - "is-stream": "1.1.0", - "logform": "2.1.2", - "one-time": "0.0.4", - "readable-stream": "3.4.0", - "stack-trace": "0.0.10", - "triple-beam": "1.3.0", - "winston-transport": "4.3.0" - } - }, - "winston-transport": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", - "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", - "requires": { - "readable-stream": "2.3.6", - "triple-beam": "1.3.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "5.1.2" - } - } - } } } } diff --git a/TaskServer/package.json b/TaskServer/package.json index f50ec7979..e9977e889 100644 --- a/TaskServer/package.json +++ b/TaskServer/package.json @@ -1,25 +1,21 @@ { - "name": "@superalgos/clone-executor", - "version": "1.0.51", - "description": "Module responsible for executing the bots on the cloud.", - "author": "Superalgos, ltd.", + "name": "@superalgos/taskserver", + "version": "2.0.0", + "description": "Executes Tasks.", + "author": "Superalgos.org", "license": "Apache-2.0", "repository": { "type": "git", - "url": "https://github.com/Superalgos/CloneExecutor.git" + "url": "https://github.com/Superalgos/TaskServer.git" }, "main": "Run.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { - "@superalgos/exchange-gateway": "0.0.10", - "@superalgos/mqservice": "^0.0.17", - "axios": "^0.19.0", "ccxt": "^1.21.82", "dotenv": "^8.0.0", "node-ipc": "^9.1.1", - "telegraf": "^3.32.0", - "winston": "^3.2.1" + "telegraf": "^3.32.0" } } diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-000.png b/WebServer/Images/Icons/style-01/angle-to-parent-000.png index d458e70c1..08de6ae0f 100644 Binary files a/WebServer/Images/Icons/style-01/angle-to-parent-000.png and b/WebServer/Images/Icons/style-01/angle-to-parent-000.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-045.png b/WebServer/Images/Icons/style-01/angle-to-parent-045.png index 906a1d914..736285891 100644 Binary files a/WebServer/Images/Icons/style-01/angle-to-parent-045.png and b/WebServer/Images/Icons/style-01/angle-to-parent-045.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-090.png b/WebServer/Images/Icons/style-01/angle-to-parent-090.png index 4b3583bc6..4170f18ef 100644 Binary files a/WebServer/Images/Icons/style-01/angle-to-parent-090.png and b/WebServer/Images/Icons/style-01/angle-to-parent-090.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-180.png b/WebServer/Images/Icons/style-01/angle-to-parent-180.png index ce921425f..a6b79d09b 100644 Binary files a/WebServer/Images/Icons/style-01/angle-to-parent-180.png and b/WebServer/Images/Icons/style-01/angle-to-parent-180.png differ diff --git a/WebServer/Images/Icons/style-01/angle-to-parent-360.png b/WebServer/Images/Icons/style-01/angle-to-parent-360.png index 6cf98ad3f..bbe1a83ff 100644 Binary files a/WebServer/Images/Icons/style-01/angle-to-parent-360.png and b/WebServer/Images/Icons/style-01/angle-to-parent-360.png differ diff --git a/WebServer/Images/Icons/style-01/data-mining.png b/WebServer/Images/Icons/style-01/data-mining.png new file mode 100644 index 000000000..19b697435 Binary files /dev/null and b/WebServer/Images/Icons/style-01/data-mining.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-000.png b/WebServer/Images/Icons/style-01/distance-to-parent-000.png index 984873dd9..4a4e8b578 100644 Binary files a/WebServer/Images/Icons/style-01/distance-to-parent-000.png and b/WebServer/Images/Icons/style-01/distance-to-parent-000.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-025.png b/WebServer/Images/Icons/style-01/distance-to-parent-025.png index e9fa9f3a0..e03403d12 100644 Binary files a/WebServer/Images/Icons/style-01/distance-to-parent-025.png and b/WebServer/Images/Icons/style-01/distance-to-parent-025.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-050.png b/WebServer/Images/Icons/style-01/distance-to-parent-050.png index dce0a9386..3c3f68e33 100644 Binary files a/WebServer/Images/Icons/style-01/distance-to-parent-050.png and b/WebServer/Images/Icons/style-01/distance-to-parent-050.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-100.png b/WebServer/Images/Icons/style-01/distance-to-parent-100.png index 6ba242342..82a025731 100644 Binary files a/WebServer/Images/Icons/style-01/distance-to-parent-100.png and b/WebServer/Images/Icons/style-01/distance-to-parent-100.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-150.png b/WebServer/Images/Icons/style-01/distance-to-parent-150.png index 041adb15c..d0646bdd7 100644 Binary files a/WebServer/Images/Icons/style-01/distance-to-parent-150.png and b/WebServer/Images/Icons/style-01/distance-to-parent-150.png differ diff --git a/WebServer/Images/Icons/style-01/distance-to-parent-200.png b/WebServer/Images/Icons/style-01/distance-to-parent-200.png index 54aa46ff2..f381ad210 100644 Binary files a/WebServer/Images/Icons/style-01/distance-to-parent-200.png and b/WebServer/Images/Icons/style-01/distance-to-parent-200.png differ diff --git a/WebServer/Images/Icons/style-01/initial-definition.png b/WebServer/Images/Icons/style-01/initial-definition.png new file mode 100644 index 000000000..d6e3db3d5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/initial-definition.png differ diff --git a/WebServer/Images/Icons/style-01/next-phase-event.png b/WebServer/Images/Icons/style-01/next-phase-event.png new file mode 100644 index 000000000..7f958daf5 Binary files /dev/null and b/WebServer/Images/Icons/style-01/next-phase-event.png differ diff --git a/WebServer/Images/Icons/style-01/production-environment.png b/WebServer/Images/Icons/style-01/production-environment.png new file mode 100644 index 000000000..6d0820e63 Binary files /dev/null and b/WebServer/Images/Icons/style-01/production-environment.png differ diff --git a/WebServer/Images/Icons/style-01/task-manager.png b/WebServer/Images/Icons/style-01/task-manager.png new file mode 100644 index 000000000..109d16159 Binary files /dev/null and b/WebServer/Images/Icons/style-01/task-manager.png differ diff --git a/WebServer/Images/Icons/style-01/testing-environment.png b/WebServer/Images/Icons/style-01/testing-environment.png new file mode 100644 index 000000000..a8e493ed1 Binary files /dev/null and b/WebServer/Images/Icons/style-01/testing-environment.png differ diff --git a/docs/_data/charting_space.md b/docs/_data/charting_space.md new file mode 100644 index 000000000..67aebbc0a --- /dev/null +++ b/docs/_data/charting_space.md @@ -0,0 +1,17 @@ +charting_space: "The Charting Space is the hierarchy that governs the configuration of the charts." + +viewport: "A viewport represents the bidimensional space on which the charts are drawn." + +time_machine: "A time machine synchronizes multiple timeline charts over the same timeline. Timeline charts within a time machine may have different rate scales or time frame scales, but their time scale will always match." + +time_scale: "The time scale controls the scale of the horizontal axis, that is, the timeline of the corresponding time machine, and of all of the timeline charts within it." + +rate_scale: "The rate scale controls the scale of the vertical axis, that is, the scale of the quoted asset, either of the time machine or of the timeline chart, depending on where the node is located." + +time_frame_scale: "The time frame scale controls the time frame setting, that is, the period represented by each candle, either of the time machine or of the timeline chart, depending on where the node is located." + +timeline_chart: "The timeline chart represents a chart within a time machine, which may have independent rate and time frame scales, as well as particular layers." + +layers_manager: "The layer manager determines which layers will be visible on the corresponding timeline chart." + +layer: "A layer represents a reference to a data product in the data storage node of the Network hierarchy. Conceptually, it means that a certain data product, which exists in a certain location on the network, holds the data to be rendered on the charts." \ No newline at end of file diff --git a/docs/_data/charting_space.yml b/docs/_data/charting_space.yml new file mode 100644 index 000000000..67aebbc0a --- /dev/null +++ b/docs/_data/charting_space.yml @@ -0,0 +1,17 @@ +charting_space: "The Charting Space is the hierarchy that governs the configuration of the charts." + +viewport: "A viewport represents the bidimensional space on which the charts are drawn." + +time_machine: "A time machine synchronizes multiple timeline charts over the same timeline. Timeline charts within a time machine may have different rate scales or time frame scales, but their time scale will always match." + +time_scale: "The time scale controls the scale of the horizontal axis, that is, the timeline of the corresponding time machine, and of all of the timeline charts within it." + +rate_scale: "The rate scale controls the scale of the vertical axis, that is, the scale of the quoted asset, either of the time machine or of the timeline chart, depending on where the node is located." + +time_frame_scale: "The time frame scale controls the time frame setting, that is, the period represented by each candle, either of the time machine or of the timeline chart, depending on where the node is located." + +timeline_chart: "The timeline chart represents a chart within a time machine, which may have independent rate and time frame scales, as well as particular layers." + +layers_manager: "The layer manager determines which layers will be visible on the corresponding timeline chart." + +layer: "A layer represents a reference to a data product in the data storage node of the Network hierarchy. Conceptually, it means that a certain data product, which exists in a certain location on the network, holds the data to be rendered on the charts." \ No newline at end of file diff --git a/docs/_data/concepts.md b/docs/_data/concepts.md index 72949b763..1e817f5b6 100644 --- a/docs/_data/concepts.md +++ b/docs/_data/concepts.md @@ -10,7 +10,7 @@ parent-offspring_relationships: "Nodes in structures of nodes are chained to eac chain: "Nodes in the workspace may be unchained or detached from its parent, carrying all offspring with it. When a node is left unchained, it is no longer taken into account in the logic of the hierarchy, as it no longer belongs to it. When a node is chained back or reattached to the hierarchy, it goes back to being functional." -hierarchy: "A hierarchy is the representation of an overarching, top-level concept resulting in a long chain of nodes, usually with many ramifications. Currently, the system manages five different types of hierarchies: Charting System, Crypto Ecosystem, Trading Systems, Network, and Data Mines." +hierarchy: "A hierarchy is the representation of an overarching, top-level concept resulting in a long chain of nodes, usually with many ramifications. Currently, the system manages five different types of hierarchies: Charting Space, Crypto Ecosystem, Trading Systems, Network, and Data Mines." reference: "A reference is a mechanism by which information in a node is related to, or accessed by, other nodes. The ability of a node to establish a reference with another node enables the first to access the information or features entailed in the second one." diff --git a/docs/_data/concepts.yml b/docs/_data/concepts.yml index 72949b763..1e817f5b6 100644 --- a/docs/_data/concepts.yml +++ b/docs/_data/concepts.yml @@ -10,7 +10,7 @@ parent-offspring_relationships: "Nodes in structures of nodes are chained to eac chain: "Nodes in the workspace may be unchained or detached from its parent, carrying all offspring with it. When a node is left unchained, it is no longer taken into account in the logic of the hierarchy, as it no longer belongs to it. When a node is chained back or reattached to the hierarchy, it goes back to being functional." -hierarchy: "A hierarchy is the representation of an overarching, top-level concept resulting in a long chain of nodes, usually with many ramifications. Currently, the system manages five different types of hierarchies: Charting System, Crypto Ecosystem, Trading Systems, Network, and Data Mines." +hierarchy: "A hierarchy is the representation of an overarching, top-level concept resulting in a long chain of nodes, usually with many ramifications. Currently, the system manages five different types of hierarchies: Charting Space, Crypto Ecosystem, Trading Systems, Network, and Data Mines." reference: "A reference is a mechanism by which information in a node is related to, or accessed by, other nodes. The ability of a node to establish a reference with another node enables the first to access the information or features entailed in the second one." diff --git a/docs/_data/data_mine.md b/docs/_data/data_mine.md index 860236ac3..b583f394b 100644 --- a/docs/_data/data_mine.md +++ b/docs/_data/data_mine.md @@ -1,8 +1,8 @@ data_mine: "A data mine is a hierarchy that contains definitions regarding bots, including the trading bot, sensors, indicators, and even plotters. These definitions make up the actual programs of such bots—the source code." -indicator_bot: "An indicator bot is an algorithm that processes information that other bots have generated, and produces elaborate datasets for others to consume. " +indicator_bot: "An indicator bot is an algorithm that processes information that other bots have generated, and produces elaborate datasets for others to consume." -process_definition: "The process definition node groups all definitions required for a process to function. The Multi Period Market Process deals with time frames of one hour and above. The Multi Period Daily Process deals with time frames of 45 minutes and below." +process_definition: "The process definition node groups all definitions required for a process to function. The Multi-Period-Market Process deals with time frames of one hour and above. The Multi-Period-Daily Process deals with time frames of 45 minutes and below." process_output: "The process output groups the definitions of which datasets are impacted by the process, that is, which datasets the process builds or takes a part in building." @@ -22,7 +22,7 @@ execution_finished_event: "The execution finished event is the event that proces product_definition: "The product definition node holds all definitions that make up a product, including how the product is calculated and the datasets the product is comprised of." -dataset_definition: "A dataset definition contains a configuration file that defines all aspects of the dataset. There are two types of dataset definitions: multi period market and multi period daily." +dataset_definition: "A dataset definition contains a configuration file that defines all aspects of the dataset. There are two types of dataset definitions: Multi-Period-Market and Multi-Period-Daily." record_definition: "The record definition is where indicators define how many fields, what fields and in which order those fields will be stored in each record." @@ -32,7 +32,7 @@ formula: "In the context of a record definition, formulas are used to assign a v calculations_procedure: "The calculations procedure processes information to be fed to calculated properties. Calculated properties are not stored in the data set; instead, they are calculated in real-time as the data is consumed by others." -procedure_loop: "The procedure loop holds the JavaScript code that cycles through an input processing information to generate an output." +procedure_loop: "The procedure loop holds the JavaScript code that reads an input, processes the information, and generates an output recursively." javascript_code: "The JavaScript code node may hold any snippet of valid JavaScript." @@ -40,34 +40,34 @@ procedure_initialization: "The procedure initialization is used to initialize va data_building_procedure: "The data building procedure processes information to be fed to properties that are stored in the output dataset." -plotter: "A plotter is a device used to produce a graphic representation of the data generated by indicators." +plotter: "A plotter is a device used to produce a graphic representation of datasets generated by bots." -plotter_module: "A plotter module is a device within a plotter that contains the definitions to create the visualization, often times, for a particular product definition." +plotter_module: "A plotter module is a device within a plotter that contains the definitions to create the visualization for a product definition." plotter_panel: "A plotter panel usually shows numerical representations of data over the charts on a small window or panel. The plotter panel node features definitions to make that possible." panel_data: "A panel data node represents a data field to be plotted on the plotter panel." -data_formula: "A data formula is the device used to assign a value to the panel data, usually in the form of the name of the corresponding record property." +data_formula: "A data formula is a device used to assign a value to the panel data, usually in the form of the name of the corresponding record property." -shapes: "The shapes node contains definitions that define polygons and styles to be rendered on a per period basis over the charts." +shapes: "The shapes node contains definitions of polygons and styles to be rendered on a per-period basis over the charts." -chart_points: "The chart points node groups definitions of points." +chart_points: "A chart points groups definitions of coordinate points used to define polygons." -point: "A point is a definition of an [x,y] coordinate where x is usually a datetime and y is usually a value that refers to a price, as output by the indicator." +point: "A point is a definition of an [x,y] coordinate. The value [x] usually represents a datetime. The value [y] may refer to a price or some other concept." -point_formula: "A point formula is the node used to input the coordinates of a point." +point_formula: "A point formula is a node used to input the [x,y] coordinates of a point." -polygon: "A polygon features definitions that make up shapes and their styles." +polygon: "A polygon features definitions that makeup shapes and their styles." -polygon_vertex: "A polygon vertex is a reference to a point defined in the chart points, constituting a specific coordinate to be used as vertex of a shape." +polygon_vertex: "A polygon vertex is a reference to a point defined in the chart points, constituting a specific coordinate to be used as a vertex of a shape." polygon_border: "A polygon border is a shape defined by at least two vertices used to draw lines or to apply a style to the stroke of a closed shape." -style: "A style is a definition of color and opacity in case of a polygon body, and of color, opacity and line thickness in case of polygon borders." +style: "A style is a definition of color and opacity in case of a polygon body, and color, opacity and line thickness in case of polygon borders." style_condition: "A style condition is a rule that allows applying an alternative style in cases where the rule validates true." -polygon_body: "A polygon body is is the fill of a shape made out of at least 3 vertices." +polygon_body: "A polygon body is the fill of a shape made out of at least three vertices." pending: "daily files, market files and so on, Multi-Period-Daily, Multi-Period-Market" \ No newline at end of file diff --git a/docs/_data/data_mine.yml b/docs/_data/data_mine.yml index 860236ac3..b583f394b 100644 --- a/docs/_data/data_mine.yml +++ b/docs/_data/data_mine.yml @@ -1,8 +1,8 @@ data_mine: "A data mine is a hierarchy that contains definitions regarding bots, including the trading bot, sensors, indicators, and even plotters. These definitions make up the actual programs of such bots—the source code." -indicator_bot: "An indicator bot is an algorithm that processes information that other bots have generated, and produces elaborate datasets for others to consume. " +indicator_bot: "An indicator bot is an algorithm that processes information that other bots have generated, and produces elaborate datasets for others to consume." -process_definition: "The process definition node groups all definitions required for a process to function. The Multi Period Market Process deals with time frames of one hour and above. The Multi Period Daily Process deals with time frames of 45 minutes and below." +process_definition: "The process definition node groups all definitions required for a process to function. The Multi-Period-Market Process deals with time frames of one hour and above. The Multi-Period-Daily Process deals with time frames of 45 minutes and below." process_output: "The process output groups the definitions of which datasets are impacted by the process, that is, which datasets the process builds or takes a part in building." @@ -22,7 +22,7 @@ execution_finished_event: "The execution finished event is the event that proces product_definition: "The product definition node holds all definitions that make up a product, including how the product is calculated and the datasets the product is comprised of." -dataset_definition: "A dataset definition contains a configuration file that defines all aspects of the dataset. There are two types of dataset definitions: multi period market and multi period daily." +dataset_definition: "A dataset definition contains a configuration file that defines all aspects of the dataset. There are two types of dataset definitions: Multi-Period-Market and Multi-Period-Daily." record_definition: "The record definition is where indicators define how many fields, what fields and in which order those fields will be stored in each record." @@ -32,7 +32,7 @@ formula: "In the context of a record definition, formulas are used to assign a v calculations_procedure: "The calculations procedure processes information to be fed to calculated properties. Calculated properties are not stored in the data set; instead, they are calculated in real-time as the data is consumed by others." -procedure_loop: "The procedure loop holds the JavaScript code that cycles through an input processing information to generate an output." +procedure_loop: "The procedure loop holds the JavaScript code that reads an input, processes the information, and generates an output recursively." javascript_code: "The JavaScript code node may hold any snippet of valid JavaScript." @@ -40,34 +40,34 @@ procedure_initialization: "The procedure initialization is used to initialize va data_building_procedure: "The data building procedure processes information to be fed to properties that are stored in the output dataset." -plotter: "A plotter is a device used to produce a graphic representation of the data generated by indicators." +plotter: "A plotter is a device used to produce a graphic representation of datasets generated by bots." -plotter_module: "A plotter module is a device within a plotter that contains the definitions to create the visualization, often times, for a particular product definition." +plotter_module: "A plotter module is a device within a plotter that contains the definitions to create the visualization for a product definition." plotter_panel: "A plotter panel usually shows numerical representations of data over the charts on a small window or panel. The plotter panel node features definitions to make that possible." panel_data: "A panel data node represents a data field to be plotted on the plotter panel." -data_formula: "A data formula is the device used to assign a value to the panel data, usually in the form of the name of the corresponding record property." +data_formula: "A data formula is a device used to assign a value to the panel data, usually in the form of the name of the corresponding record property." -shapes: "The shapes node contains definitions that define polygons and styles to be rendered on a per period basis over the charts." +shapes: "The shapes node contains definitions of polygons and styles to be rendered on a per-period basis over the charts." -chart_points: "The chart points node groups definitions of points." +chart_points: "A chart points groups definitions of coordinate points used to define polygons." -point: "A point is a definition of an [x,y] coordinate where x is usually a datetime and y is usually a value that refers to a price, as output by the indicator." +point: "A point is a definition of an [x,y] coordinate. The value [x] usually represents a datetime. The value [y] may refer to a price or some other concept." -point_formula: "A point formula is the node used to input the coordinates of a point." +point_formula: "A point formula is a node used to input the [x,y] coordinates of a point." -polygon: "A polygon features definitions that make up shapes and their styles." +polygon: "A polygon features definitions that makeup shapes and their styles." -polygon_vertex: "A polygon vertex is a reference to a point defined in the chart points, constituting a specific coordinate to be used as vertex of a shape." +polygon_vertex: "A polygon vertex is a reference to a point defined in the chart points, constituting a specific coordinate to be used as a vertex of a shape." polygon_border: "A polygon border is a shape defined by at least two vertices used to draw lines or to apply a style to the stroke of a closed shape." -style: "A style is a definition of color and opacity in case of a polygon body, and of color, opacity and line thickness in case of polygon borders." +style: "A style is a definition of color and opacity in case of a polygon body, and color, opacity and line thickness in case of polygon borders." style_condition: "A style condition is a rule that allows applying an alternative style in cases where the rule validates true." -polygon_body: "A polygon body is is the fill of a shape made out of at least 3 vertices." +polygon_body: "A polygon body is the fill of a shape made out of at least three vertices." pending: "daily files, market files and so on, Multi-Period-Daily, Multi-Period-Market" \ No newline at end of file diff --git a/docs/_data/network.md b/docs/_data/network.md index 974e534e0..31ee62915 100644 --- a/docs/_data/network.md +++ b/docs/_data/network.md @@ -1,4 +1,4 @@ -network: "The network hierarchy contains definitions regarding the physical location in which certain nodes live or function. For instance, a certain process my run and store data in your local machine, or in some other machine in the network." +network: "The network hierarchy contains definitions regarding the physical location in which certain nodes live or function. For instance, a certain process my run and store data in your local machine or some other machine in the network." network_node: "A network node represents a machine in the network on which processes run or data is stored." diff --git a/docs/_data/network.yml b/docs/_data/network.yml index 974e534e0..31ee62915 100644 --- a/docs/_data/network.yml +++ b/docs/_data/network.yml @@ -1,4 +1,4 @@ -network: "The network hierarchy contains definitions regarding the physical location in which certain nodes live or function. For instance, a certain process my run and store data in your local machine, or in some other machine in the network." +network: "The network hierarchy contains definitions regarding the physical location in which certain nodes live or function. For instance, a certain process my run and store data in your local machine or some other machine in the network." network_node: "A network node represents a machine in the network on which processes run or data is stored." diff --git a/docs/_data/sidebars/suite_sidebar.yml b/docs/_data/sidebars/suite_sidebar.yml index 6411824c0..6a1afafe1 100644 --- a/docs/_data/sidebars/suite_sidebar.yml +++ b/docs/_data/sidebars/suite_sidebar.yml @@ -6,108 +6,108 @@ entries: version: 0.0.6 folders: - - title: - output: pdf - type: frontmatter - folderitems: - - title: - url: /titlepage.html - output: pdf - type: frontmatter - - title: - url: /tocpage.html - output: pdf - type: frontmatter +# - title: +# output: pdf +# type: frontmatter +# folderitems: +# - title: +# url: /titlepage.html +# output: pdf +# type: frontmatter +# - title: +# url: /tocpage.html +# output: pdf +# type: frontmatter - - title: Before you Begin - output: web - folderitems: +# - title: Before you Begin +# output: web +# folderitems: - - title: Welcome! - url: /index.html - output: web - type: homepage +# - title: Welcome! +# url: /index.html +# output: web +# type: homepage - - title: What to expect - url: /suite-what-to-expect.html - output: web +# - title: What to expect +# url: /suite-what-to-expect.html +# output: web - - title: System requirements - url: /suite-system-requirements.html - output: web +# - title: System requirements +# url: /suite-system-requirements.html +# output: web - - title: Getting Started - output: web - folderitems: +# - title: Getting Started +# output: web +# folderitems: - - title: Download and set up - url: /suite-download-and-set-up.html - output: web +# - title: Download and set up +# url: /suite-download-and-set-up.html +# output: web - - title: Run the suite - url: /suite-run-the-suite.html - output: web +# - title: Run the suite +# url: /suite-run-the-suite.html +# output: web - - title: Quick overview - url: /suite-quick-overview.html - output: web +# - title: Quick overview +# url: /suite-quick-overview.html +# output: web - - title: Run your first backtest - url: /suite-run-your-first-backtest.html - output: web +# - title: Run your first backtest +# url: /suite-run-your-first-backtest.html +# output: web - - title: Update datasets - url: /suite-update-datasets.html - output: web +# - title: Update datasets +# url: /suite-update-datasets.html +# output: web - - title: Trade with an open-source strategy - url: /suite-trade-live-with-an-open-source-strategy.html - output: web +# - title: Trade with an open-source strategy +# url: /suite-trade-live-with-an-open-source-strategy.html +# output: web - - title: What's next? - url: /suite-what-is-next.html - output: web +# - title: What's next? +# url: /suite-what-is-next.html +# output: web - - title: Charts - output: web - folderitems: +# - title: Charts +# output: web +# folderitems: - - title: Overview - url: /suite-charts-overview.html - output: web +# - title: Overview +# url: /suite-charts-overview.html +# output: web - - title: Interface - url: /suite-charts-interface.html - output: web +# - title: Interface +# url: /suite-charts-interface.html +# output: web - - title: Layers panel - url: /suite-layers-panel.html - output: web +# - title: Layers panel +# url: /suite-layers-panel.html +# output: web - - title: Designer - output: web - folderitems: +# - title: Designer +# output: web +# folderitems: - - title: Intro, workspace and data structures - url: /suite-designer-workspace-and-data-structures.html - output: web +# - title: Intro, workspace and data structures +# url: /suite-designer-workspace-and-data-structures.html +# output: web - - title: Hierarchies - url: /suite-hierarchies.html - output: web +# - title: Hierarchies +# url: /suite-hierarchies.html +# output: web - - title: Interface - url: /suite-designer-interface.html - output: web +# - title: Interface +# url: /suite-designer-interface.html +# output: web - - title: Atachment and detachment of nodes - url: /suite-detachment-and-attachment-of-nodes.html - output: web +# - title: Atachment and detachment of nodes +# url: /suite-detachment-and-attachment-of-nodes.html +# output: web - - title: References - url: /suite-references.html - output: web +# - title: References +# url: /suite-references.html +# output: web - title: Trading Concepts output: web @@ -129,149 +129,149 @@ entries: url: /suite-parameters.html output: web - - title: Conditions & Formulas - output: web - folderitems: +# - title: Conditions & Formulas +# output: web +# folderitems: - - title: Syntax Overview - url: /suite-sysntax-overview.html - output: web +# - title: Syntax Overview +# url: /suite-sysntax-overview.html +# output: web - - title: Internal variables - url: /suite-internal-variables.html - output: web +# - title: Internal variables +# url: /suite-internal-variables.html +# output: web - - title: Indicators included in the data release - url: /suite-indicators-included-in-the-data-release.html - output: web +# - title: Indicators included in the data release +# url: /suite-indicators-included-in-the-data-release.html +# output: web - - title: Indicators not included in the release - url: /suite-indicators-not-included-in-the-data-release.html - output: web +# - title: Indicators not included in the release +# url: /suite-indicators-not-included-in-the-data-release.html +# output: web - - title: Using non-included indicators - url: /suite-using-non-included-indicators.html - output: web +# - title: Using non-included indicators +# url: /suite-using-non-included-indicators.html +# output: web - - title: Network - output: web - folderitems: +# - title: Network +# output: web +# folderitems: - - title: Overview - url: /suite-network-overview.html - output: web +# - title: Overview +# url: /suite-network-overview.html +# output: web - - title: Managing processes - url: /suite-managing-processes.html - output: web +# - title: Managing processes +# url: /suite-managing-processes.html +# output: web - - title: Backtests & Paper Trading - output: web - folderitems: +# - title: Backtests & Paper Trading +# output: web +# folderitems: - - title: Overview - url: /suite-testing-overview.html - output: web +# - title: Overview +# url: /suite-testing-overview.html +# output: web - - title: Session parameters - url: /suite-session-parameters.html - output: web +# - title: Session parameters +# url: /suite-session-parameters.html +# output: web - - title: Simulation layers - url: /suite-simulation-layers.html - output: web +# - title: Simulation layers +# url: /suite-simulation-layers.html +# output: web - - title: Advanced backtesting - url: /suite-advanced-backtesting.html - output: web +# - title: Advanced backtesting +# url: /suite-advanced-backtesting.html +# output: web - - title: Forward Tests & Live Trading - output: web - folderitems: +# - title: Forward Tests & Live Trading +# output: web +# folderitems: - - title: Overview - url: /suite-live-trading-overview.html - output: web +# - title: Overview +# url: /suite-live-trading-overview.html +# output: web - - title: Getting ready to trade live - url: /suite-getting-ready-to-trade-live.html - output: web +# - title: Getting ready to trade live +# url: /suite-getting-ready-to-trade-live.html +# output: web - - title: Live trading history layer - url: /suite-live-trading-history-layer.html - output: web +# - title: Live trading history layer +# url: /suite-live-trading-history-layer.html +# output: web - - title: Execution limitations - url: /suite-execution-limitations.html - output: web +# - title: Execution limitations +# url: /suite-execution-limitations.html +# output: web - - title: Multiple sessions - url: /suite-multiple-forward-testing-and-live-trading-sessions.html - output: web +# - title: Multiple sessions +# url: /suite-multiple-forward-testing-and-live-trading-sessions.html +# output: web - - title: Data Mining & Research - output: web - folderitems: +# - title: Data Mining & Research +# output: web +# folderitems: - - title: Data Mining Overview - url: /suite-data-mining-overview.html - output: web +# - title: Data Mining Overview +# url: /suite-data-mining-overview.html +# output: web - - title: Bots - url: /suite-bots.html - output: web +# - title: Bots +# url: /suite-bots.html +# output: web - - title: Anatomy of an indicator - url: /suite-anatomy-of-an-indicator.html - output: web +# - title: Anatomy of an indicator +# url: /suite-anatomy-of-an-indicator.html +# output: web - - title: ":: Process definition" - url: /suite-process-definition.html - output: web +# - title: ":: Process definition" +# url: /suite-process-definition.html +# output: web - - title: ":: Product definition" - url: /suite-product-definition.html - output: web +# - title: ":: Product definition" +# url: /suite-product-definition.html +# output: web - - title: Input-to-output cycle - url: /suite-input-to-output-cycle.html - output: web +# - title: Input-to-output cycle +# url: /suite-input-to-output-cycle.html +# output: web - - title: Datasets - url: /suite-datasets.html - output: web +# - title: Datasets +# url: /suite-datasets.html +# output: web - - title: Dependencies - url: /suite-dependencies.html - output: web +# - title: Dependencies +# url: /suite-dependencies.html +# output: web - - title: Build an indicator - url: /suite-build-an-indicator.html - output: web +# - title: Build an indicator +# url: /suite-build-an-indicator.html +# output: web - - title: Build a plotter - url: /suite-build-a-plotter.html - output: web +# - title: Build a plotter +# url: /suite-build-a-plotter.html +# output: web - - title: Productivity Tools - output: web - folderitems: +# - title: Productivity Tools +# output: web +# folderitems: - - title: Backups - url: /suite-backups.html - output: web +# - title: Backups +# url: /suite-backups.html +# output: web - - title: Clones - url: /suite-clones.html - output: web +# - title: Clones +# url: /suite-clones.html +# output: web - - title: Shares - url: /suite-shares.html - output: web +# - title: Shares +# url: /suite-shares.html +# output: web - - title: Telegram Announcements - url: /suite-telegram-announcements.html - output: web +# - title: Telegram Announcements +# url: /suite-telegram-announcements.html +# output: web - title: Hierarchies Directory output: web @@ -281,8 +281,8 @@ entries: url: /suite-crypto-ecosystem-hierarchy.html output: web - - title: Charting system - url: /suite-charting-system-hierarchy.html + - title: Charting Space + url: /suite-charting-space-hierarchy.html output: web - title: Trading system @@ -297,33 +297,33 @@ entries: url: /suite-data-mine-hierarchy.html output: web - - title: Troubleshooting - output: web - folderitems: +# - title: Troubleshooting +# output: web +# folderitems: - - title: Upgrading your existing installation - url: /suite-upgrading-your-existing-instalation.html - output: web +# - title: Upgrading your existing installation +# url: /suite-upgrading-your-existing-instalation.html +# output: web - - title: My system gets slow - url: /suite-my-system-gets-slow.html - output: web +# - title: My system gets slow +# url: /suite-my-system-gets-slow.html +# output: web - - title: On-screen errors and warnings - url: /suite-on-screen-errors-and-warnings.html - output: web +# - title: On-screen errors and warnings +# url: /suite-on-screen-errors-and-warnings.html +# output: web - - title: Market data seems to be outdated - url: /suite-market-data-indicators-seem-to-be-outdated.html - output: web +# - title: Market data seems to be outdated +# url: /suite-market-data-indicators-seem-to-be-outdated.html +# output: web - - title: Reporting bugs - url: /suite-reporting-bugs.html - output: web +# - title: Reporting bugs +# url: /suite-reporting-bugs.html +# output: web - - title: Consulting logs - url: /suite-consulting-logs.html - output: web +# - title: Consulting logs +# url: /suite-consulting-logs.html +# output: web diff --git a/docs/_data/topnav.yml b/docs/_data/topnav.yml index 63a54875b..e232fbb32 100644 --- a/docs/_data/topnav.yml +++ b/docs/_data/topnav.yml @@ -11,24 +11,24 @@ topnav: url: index.html #Topnav dropdowns -topnav_dropdowns: -- title: Topnav dropdowns - folders: - - title: Product Docs - folderitems: - - title: Systematic Trading - url: /systematic-trading-system-overview.html - - title: Algorithmic Trading - url: /algorithmic-trading-system-overview.html - - title: Discretionary Trading - url: /discretionary-introduction.html - - title: Smart Holding - url: /smart-introduction.html - - title: Trading Coach - url: /coach-introduction_page.html - - title: Signals Broadcast - url: /signal-introduction.html - - title: Markets Research - url: /market-introduction_page.html - - title: Data Mining - url: /mine-introduction.html \ No newline at end of file +#topnav_dropdowns: +#- title: Topnav dropdowns +# folders: +# - title: Product Docs +# folderitems: +# - title: Systematic Trading +# url: /systematic-trading-system-overview.html +# - title: Algorithmic Trading +# url: /algorithmic-trading-system-overview.html +# - title: Discretionary Trading +# url: /discretionary-introduction.html +# - title: Smart Holding +# url: /smart-introduction.html +# - title: Trading Coach +# url: /coach-introduction_page.html +# - title: Signals Broadcast +# url: /signal-introduction.html +# - title: Markets Research +# url: /market-introduction_page.html +# - title: Data Mining +# url: /mine-introduction.html \ No newline at end of file diff --git a/docs/_includes/sidebar.html b/docs/_includes/sidebar.html index 6bdcbb1e1..4ed2627c8 100644 --- a/docs/_includes/sidebar.html +++ b/docs/_includes/sidebar.html @@ -47,11 +47,11 @@ {% endif %} {% endfor %} {% endfor %} - + diff --git a/docs/_includes/trading_system/base-asset.md b/docs/_includes/trading_system/base-asset.md index 3a886ca33..669fb7347 100644 --- a/docs/_includes/trading_system/base-asset.md +++ b/docs/_includes/trading_system/base-asset.md @@ -89,14 +89,3 @@ Select *Configure Base Asset* on the menu to access the configuration. {% endif %} -{% if include.starting != "" %} - -{{include.starting}} Starting {{preposition}} {{title}} - - - -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - - -{% endif %} \ No newline at end of file diff --git a/docs/_includes/trading_system/fee-structure.md b/docs/_includes/trading_system/fee-structure.md index 9c7aada7b..2b9a2028d 100644 --- a/docs/_includes/trading_system/fee-structure.md +++ b/docs/_includes/trading_system/fee-structure.md @@ -1,7 +1,7 @@ {% assign title = "Fee Structure" %} -{% assign definition = site.data.trading_system.free_structure %} +{% assign definition = site.data.trading_system.fee_structure %} {% assign preposition = "a" %} @@ -40,8 +40,6 @@ {% if include.content != "n" %} -**{{ definition }}** - Exchange fees are a crucial part of trading. A strategy may work like a charm when you leave fees out of the equation but would lead you to bankruptcy in a live trading situation. @@ -101,14 +99,3 @@ Select *Configure Fee Structure* on the menu to access the configuration. {% endif %} -{% if include.starting != "" %} - -{{include.starting}} Starting {{preposition}} {{title}} - - - -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - - -{% endif %} \ No newline at end of file diff --git a/docs/_includes/trading_system/quoted-asset.md b/docs/_includes/trading_system/quoted-asset.md index 4657da30a..552c0a88c 100644 --- a/docs/_includes/trading_system/quoted-asset.md +++ b/docs/_includes/trading_system/quoted-asset.md @@ -1,7 +1,7 @@ {% assign title = "Quoted Asset" %} -{% assign definition = site.data.trading_system.quote_asset %} +{% assign definition = site.data.trading_system.quoted_asset %} {% assign preposition = "a" %} diff --git a/docs/_includes/trading_system/slippage.md b/docs/_includes/trading_system/slippage.md index 465e51abb..e2c44b857 100644 --- a/docs/_includes/trading_system/slippage.md +++ b/docs/_includes/trading_system/slippage.md @@ -1,7 +1,7 @@ {% assign title = "Slippage" %} -{% assign definition = site.data.trading_system.slipage %} +{% assign definition = site.data.trading_system.slippage %} {% assign preposition = "a" %} diff --git a/docs/css/theme-blue.css b/docs/css/theme-blue.css index 1ded1de0d..277a304ab 100644 --- a/docs/css/theme-blue.css +++ b/docs/css/theme-blue.css @@ -104,7 +104,7 @@ div.sidebarTitle { li.sidebarTitle { margin-top:30px; font-weight:600; - font-size:130%; + font-size:120%; color: #000; margin-bottom:10px; margin-left: 5px; diff --git a/docs/images/icons/150-charting-space.png b/docs/images/icons/150-charting-space.png new file mode 100644 index 000000000..f47ef3d3f Binary files /dev/null and b/docs/images/icons/150-charting-space.png differ diff --git a/docs/images/icons/charting-space.png b/docs/images/icons/charting-space.png new file mode 100644 index 000000000..025131da1 Binary files /dev/null and b/docs/images/icons/charting-space.png differ diff --git a/docs/index.md b/docs/index.md index c880d81dc..beaa4e3ae 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,32 +4,16 @@ sidebar: suite_sidebar permalink: index.html --- -The Superalgos Platform provides a visual environment for developing, testing and automating crypto-trading strategies. +## About this Documentation -**The platform's vocation is to become the most comprehensive data processing and trading automation system available to all traders, data scientists, analysts and crypto holders.** +This documentation is currently work in progress and corresponds to an unreleased version of the Superalgos Suite, still in development. The system has experienced a major shift on multiple fronts, and changes are currently being documented. -The platform allows designing and automating a wide variety of strategies, as well as augmenting your *intelligence* by handling and creating your own datasets, with great flexibility. - -This is not a simple plug-and-play bot. However, we try our best to make light use of the platform as easy as possible, for instance, for loading existing strategies and trading live right off the bat, with minimal user input. - -[![Illustration](https://user-images.githubusercontent.com/13994516/63528460-4550ae80-c503-11e9-8db6-22995e0b9c16.gif)](https://user-images.githubusercontent.com/13994516/63528460-4550ae80-c503-11e9-8db6-22995e0b9c16.gif) - -## About this Manual - -This manual is arranged in three sections: - -* **Basic Use** helps you set up the platform, teaches you the very basic operation, and takes you by the hand until you are ready to start trading live with one of the open-source strategies currently available. - -* **Power Use** takes you through the learning curve to modify strategies and design your own, and shows you what's under the hood of the platform so that you may take control of all the resources that make up a successful trader. - -* **Data Mining** pumps the potential for growing your *intelligence* through the roof by teaching you how to process data and build your own datasets. Analysing and processing data is what turns data into *information*. Using information effectively is what realises *intelligence*. - -You may access each topic by using the menu on the right. We try our best to organize the content in a logical order, so that you are exposed to information incrementally, as you need it while you go through the learning curve. +The Hierarchies Directory feature extensive information about all the data structures handled by the designer, including the new, unreleased ones. ## Beta Stage Notice This is a pre-release in beta stage. The platform is under heavy development. We appreciate your help in testing the platform and reporting any errors you may experience. We will keep improving user experience with your feedback. -> **WARNING:** The Superalgos Platform is at a very early stage of development. As such, errors may occur at any point, including errors that may cause you to lose money. You are responsible for taking all precautions before starting trading live. Make sure you forward-test with small amounts of money, the kind you can afford losing. Trade live at your own risk. +{% include important.html content="The Superalgos Platform is at a very early stage of development. As such, errors may occur at any point, including errors that may cause you to lose money. You are responsible for taking all precautions before starting trading live. Make sure you forward-test with small amounts of money, the kind you can afford losing. Trade live at your own risk." %} diff --git a/docs/pages/suite/suite-charting-space-hierarchy.md b/docs/pages/suite/suite-charting-space-hierarchy.md new file mode 100644 index 000000000..021097a92 --- /dev/null +++ b/docs/pages/suite/suite-charting-space-hierarchy.md @@ -0,0 +1,420 @@ +--- +title: Charting Space Hierarchy +summary: "The charting space provides the tools to flexibly configure charts with data from different markets, exchanges, and multiple data products." +sidebar: suite_sidebar +permalink: suite-charting-space-hierarchy.html +toc: false +--- + +{% include note.html content="Hover your mouse over a node for a tooltip definition, and click to get all the details." %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Charting Space
+
+ + +
Viewport
+
+ + +
Time Machine
+
+ + +
Time Scale
+
+ + +
Rate Scale
+
+ + +
Time Frame Scale
+
+ + +
Timeline Chart
+
+ + +
Rate Scale
+
+ + +
Time Frame Scale
+
+ + +
Layers Manager
+
+ + +
Layer
+
+ + + + + + + + + + +## Charting Space + + + +**{{site.data.charting_space.charting_space}}** + +There is only one Charting Space hierarchy, thus all charts are configured here. As is common throughout the system, different concepts and elements of the charts such as the scales, data layers and so on, are represented by nodes in the hierarchy. The system allows great flexibility on how to visualize information over the charts by adding, configuring, and arranging these nodes. + + + + + +## Viewport + + + +**{{site.data.charting_space.viewport}}** + +At this point, the system supports one viewport only. When you navigate through the charts, panning, zooming or moving elements around, what you are doing is panning and zooming on the viewport. That is, you are not moving the charts, but the space that contains them. + +Think of the viewport as a huge drawing board. You can have lots of information plotted on different parts of the board, so you move the viewport around and zoom in to focus on specific bits of information. + +When you zoom out, you are bringing more information in focus. Because information is handled dynamically, reading data from files, interpreting it, and drawing the corresponding visual elements on the screen in real-time, zooming out or—in general—bringing lots of information in focus may slow your system down. The information that is out of focus, that is, out of the visible screen, is not processed. + +The concept of the viewport allows having multiple charts configured all at once, so that you may consult different exchanges, different markets, or different technical studies on demand, simply by moving around and zooming in or out on the viewport. You may put any of those elements side by side, or even superimpose them to study market flows, search for arbitrage opportunities, or simply keep them in sight when you analyze particular market situations. + + + + + +## Time Machine + + + +**{{site.data.charting_space.time_machine}}** + +Think of a time machine as an aide that helps you keep any number of pieces of information synchronized on the same time scale. Every data structure that you set up within the same time machine, will always be synchronized in time. Put in other words, the open and closing datetime of each dataset will always be aligned on the vertical axis. + +On the other hand, when you arrange charts on different time machines, they are completely independent of one another. + +### Adding a Time Machine + +To add a time machine, select *Add Time Machine* on the Charting Space node menu. + + + + + +## Time Scale + + + +**{{site.data.charting_space.time_scale}}** + +### Adding a Time Scale + +To add a time scale, select *Add Time Scale* on the time machine node menu. + +### Scale Value + +The time scale is represented by a numerical value between 0 and 100. When the scale is set to zero, the horizontal span of the time machine and charts within it is the smallest possible. When the scale is set to 100, it is the largest possible. In other words, the charts are compressed on the horizontal axis with low scale numbers and expanded as the scale increases. + +### Setting a Time Scale + +You may set a scale value from within the charts by placing the mouse pointer over the corresponding time machine datetime box and scrolling the mouse wheel. + +{% include tip.html content="Pressing the mouse wheel while scrolling accelerates the process." %} + + + +You may also set a scale value from within the designer by selecting *Configure Time Scale* on the time scale node menu and editing the corresponding configuration. + +```json +{"scale":"0"} +``` + +{% include note.html content="The values entered via the designer and the charts are synchronized and stored in the node." %} + + + + + +## Rate Scale + + + +**{{site.data.charting_space.rate_scale}}** + +Rate scales may exist both at the level of a time machine and at the level of a timeline chart, each affecting the corresponding concept. + +When set at the level of the time machine, the scale setting affects all charts within the time machine. When set at the level of the timeline chart, the setting overrides the rate scale at the time machine level. This allows having multiple charts on the same time machine, each with a different rate scales. + +### Adding a Rate Scale + +To add a rate scale, select *Add Rate Scale* on the time machine or the timeline chart node menu. + +### Rate Scale Value + +The rate scale is represented by a numerical value between 0 and 100. When the scale is set to zero, the vertical span of the time machine or the timeline chart is the smallest possible. When the scale is set to 100, it is the largest possible. In other words, the charts are compressed on the vertical axis with low scale numbers and expanded as the scale increases. + +### Rate Scale Minimum and Maximum + +The rate scale remains the same at all times unless you change it. This means that&mdashunlike in other platforms—the rate scale does not adjust dynamically depending on the information on the screen. This is a design choice so that rates may be easily comparable along the datetime span of the market. + +### Offset Value + +The offset is a property of the rate scale by which the scale may be shifted upwards or downwards. As a result, charts aligned in the vertical axis—synchronized in datetime by a shared time machine—may be put one above the other. The offset value is a numerical value which may be positive or negative, with no specific minimum or maximum. + +### Setting Rate Scale and Offset Values + +You may set a scale value from within the charts by placing the mouse pointer over the corresponding time machine or timeline chart rate box and scrolling the mouse wheel. Add the Shift key to affect the offset. + +{% include tip.html content="Pressing the mouse wheel while scrolling accelerates the process." %} + + + +You may also set a scale setting, minimum and maximum values for the scale, and offset value from within the designer by selecting *Configure Rate Scale* on the rate scale node menu and editing the configuration. + +```json +{"scale":"50","offset":0,"minValue":0,"maxValue":25000} +``` + +* The ```minValue``` sets the value for the origin of the y-axis, that is, the rate scale. Usually, this value would be zero, but it doesn't need to be zero, as it is configurable. + +* The ```maxValue``` may be set to any positive number. + +{% include note.html content="The values entered via the designer and the charts are synchronized and stored in the node." %} + + + + + +## Time Frame Scale + + + +**{{site.data.charting_space.time_frame_scale}}** + +When the time frame scale is set at the level of the time machine, the scale setting affects all charts within the time machine. When set at the level of the timeline chart, the setting overrides the time frame scale at the time machine level. This allows comparing charts in different time frames, but still synchronized at the datetime level. + +### Adding a Time Frame Scale + +To add a time frame scale, select *Add Time Frame Scale* on the time machine or the timeline chart node menu. + +### Time Frame Scale Value + +The time frame scale value can be any of the time frames supported by the system: + +* 1, 2, 3, 4, 5, 10, 15, 20, 30, 40, and 45 minutes + +* 1, 2, 3, 4, 6, 8, 12, and 24 hours. + +### Setting a Time Frame + +You may set a time frame value from within the charts by placing the mouse pointer over the corresponding time machine or timeline chart time frame box and scrolling the mouse wheel. + + + +You may also set a time frame from within the designer by selecting *Configure Rate Scale* on the rate scale node menu and editing the configuration. + +```json +{"value":"06-hs"} +``` + +{% include note.html content="The values entered via the designer and the charts are synchronized and stored in the node." %} + + + + + +## Timeline Chart + + + +**{{site.data.charting_space.timeline_chart}}** + +In other words, a timeline chart is a set of information to be displayed over a timeline. The information may include candles—the main and foremost resource—as well as any other indicator, study or—in general—data product that may be available. + +You may add as many charts as you wish. Charts within the same time machine are synchronized in the y-axis, that is, at the datetime level. Charts in different time machines are independent of each other concerning the datetime. In either case, you may also add rate scales and time frame scales at the timeline charts level. + +The information that each timeline chart makes available on the screen is given by the layers set up in the corresponding layers manager. + +### Adding a Timeline Chart + +To add a timeline chart, select *Add Timeline Chart* on the preferred time machine node menu. + + + + + +## Layers Manager + + + +**{{site.data.charting_space.layers_manager}}** + +In other words, you use the layers manager node to configure which data products you wish to be made available for visualization purposes on the charts, in particular, on a specific timeline chart to which the layers manager node is attached to. + +### Adding a Layers Manager + +To add a layers manager, select *Add Layers Manager* on the preferred timeline chart node menu. + + + + + +## Layer + + + +**{{site.data.charting_space.layer}}** + +### Adding Layers + +To add a layer, select *Add Layer* on the layer manager node menu. + +### Setting Up Layers + +To set up a layer, you need to establish a reference with the data product of your choice. + +### Configuring Layers + +Select *Configure Layer* on the menu to access the configuration. + +```json +{"status":"on", "showPanels":true} +``` + +* ```status``` may be *on* or *off* and refers to the layer being visible or not on the charts. + +* ```showPanels``` may be *true* or *false*; *true* shows the plotter panel that may be associated with the data product as per the plotter module definitions; *false* makes panels invisible. diff --git a/docs/pages/suite/suite-crypto-ecosystem-hierarchy.md b/docs/pages/suite/suite-crypto-ecosystem-hierarchy.md index 527858e41..0e1e441b4 100644 --- a/docs/pages/suite/suite-crypto-ecosystem-hierarchy.md +++ b/docs/pages/suite/suite-crypto-ecosystem-hierarchy.md @@ -1,11 +1,13 @@ --- title: Crypto Ecosystem Hierarchy -summary: "Hover your mouse over a node for a quick definition, and click for the details." +summary: "The crypto ecosystem provides the tools to define which exchanges, which assets and which markets you wish to work with." sidebar: suite_sidebar permalink: suite-crypto-ecosystem-hierarchy.html toc: false --- +{% include note.html content="Hover your mouse over a node for a tooltip definition, and click to get all the details." %} + @@ -444,7 +446,7 @@ To add an exchange accounts node, select *Add Exchange Account* on the crypto ex ### Adding a User Account -To add user account, select *Add User Account* on the exchange accounts node menu. The user account node is created along with the basic structure of nodes used to define keys and user assets with the exchange. +To add a user account, select *Add User Account* on the exchange accounts node menu. The user account node is created along with the basic structure of nodes used to define keys and user assets with the exchange. {% include tip.html content="You may add and administer as many user accounts as you wish." %} diff --git a/docs/pages/suite/suite-data-mine-hierarchy.md b/docs/pages/suite/suite-data-mine-hierarchy.md index 3f379857f..c97887791 100644 --- a/docs/pages/suite/suite-data-mine-hierarchy.md +++ b/docs/pages/suite/suite-data-mine-hierarchy.md @@ -1,11 +1,12 @@ --- title: Data Mine Hierarchy -summary: "Hover your mouse over a node for a quick definition, and click for the details." +summary: "A data mine provides the tools to build indicators, produce data visualization plotters, and process markets data, with minimal coding requirements." sidebar: suite_sidebar permalink: suite-data-mine-hierarchy.html toc: false --- +{% include note.html content="Hover your mouse over a node for a tooltip definition, and click to get all the details." %}
@@ -793,9 +794,9 @@ toc: false **{{site.data.data_mine.data_mine}}** -In a way, data mines are catalogs of bots that anyone may create. This hierarchy provides all the tools required to create sensors, indicators and plotters, with minimal coding required. +In a way, data mines are catalogs of bots that anyone may create. This hierarchy provides all the tools required to create sensors, indicators, and plotters, with minimal coding required. -Because the hierachy provides definitions, it does not intervene in the execution of bots. Instead, the definitions in data mines are instanciated from the Network hierarchy by bot instances. Bot instances and process instances are the ones actually running the code according to the definitions in the corresponding data mine. +Because the hierarchy provides definitions, it does not intervene in the execution of bots. Instead, the definitions in data mines are instantiated from the Network hierarchy by bot instances. Bot instances and process instances are the ones running the code according to the definitions in the corresponding data mine. ### Adding a Data Mine @@ -825,11 +826,9 @@ Select *Configure Data Mine* on the menu to access the configuration. **{{site.data.data_mine.indicator_bot}}** -indicator_bot: "An indicator bot is an algorithm that processes information that other bots have generated, and produces elaborate datasets for others to consume. " +The indicator bot node holds all definitions required for an indicator bot to function. Definitions are split between the definition of processes and products. Processes are algorithms that go through an input dataset, perform certain calculations, and produce an output. Part of that output is the data products the bot creates. -The indicator bot node holds all definitions required for an indicator bot to function. Definitions are split between the definition of processes and products. Processes are algorithms that go through an input dataset, perform certain calculations, and produce an output. Part of that output are the data products the bot creates. - -### Adding an Inidicator Bot +### Adding an Indicator Bot To add an indicator bot, select *Add Indicator Bot* on the data mine node menu. An indicator bot is created along with the basic structure of nodes required to define it. @@ -858,13 +857,11 @@ Select *Configure Bot* on the menu to access the configuration. **{{site.data.data_mine.process_definition}}** -process_definition: "The process definition node groups all definitions required for a process to function. The Multi Period Market Process deals with time frames of one hour and above. The Multi Period Daily Process deals with time frames of 45 minutes and below." - -As hinted above, most bots, in particular indicators, have two different processes. The reason is that different data structures need be handled in different manners. The Multi Period Daily process handles daily files, while the Multi Period Market process handles market files. +As hinted above, most bots, in particular indicators, have two different processes. The reason is that different data structures need to be handled in different manners. The Multi-Period-Daily process handles daily files, while the Multi-Period-Market process handles market files. ### Adding a Process Definition -To add a process definition, select *Add Process Definition* on the bot's menu. A process definition node is created along with the basic structure of nodes comprissing the definition. +To add a process definition, select *Add Process Definition* on the bot's menu. A process definition node is created along with the basic structure of nodes comprising the definition. ### Configuring a Process Definition @@ -952,7 +949,7 @@ Select *Configure Process* on the menu to access the configuration. } ``` -Both configurations deal with certain aspects of the internal workings of these kinds of processes. These are the ones you should adjust when creating a new bot: +Both configurations deal with certain aspects of the internal workings of these kinds of processes. These are the ones to be adjusted when creating a new bot: * ```codeName``` is the name of the process as used within the code of the system; for consistency, always use ```Multi-Period-Market``` and ```Multi-Period-Daily``` accordingly. @@ -971,8 +968,6 @@ Both configurations deal with certain aspects of the internal workings of these **{{site.data.data_mine.process_output}}** -process_output: "The process output groups the definitions of which datasets are impacted by the process, that is, which datasets the process builds or takes a part in building." - ### Adding a Process Output To add a process output node, select *Add Missing Items* on the process definition node menu. Items that may be missing are created along with the basic structure of nodes required to define them. @@ -991,11 +986,9 @@ To add a process output node, select *Add Missing Items* on the process definiti **{{site.data.data_mine.output_dataset}}** -output_dataset: "The output dataset is a reference to a dataset definition. By establishing such a reference, the process acquires the definitions as of how the dataset is to be constructed." - There are other effects of establishing a reference from the output dataset to a product dataset definition. Upon execution, every time a process finishes a processing cycle, it triggers an event that may be consumed by other entities. This event indicates that the datasets impacted by the process have been updated. -An example of other entities that may be listening to such events is that of plotters. Plotters read datasets and create graphical representations of this data over the charts. You surely have noticed that the charts are constantly updating the information in form of candles and indicators in realtime, syncronized with the data being extracted from the exchange by the sensor bot. That kind of automatism is possible thanks to the events processes trigger every time an execution cycle is finished, signaling to everyone listening that new data is available on each of the impacted datasets. +An example of other entities that may be listening to such events is that of plotters. Plotters read datasets and create graphical representations of this data over the charts. Charts are constantly updating the information in the form of candles and indicators in realtime, synchronized with the data being extracted from the exchange by the sensor bot. That kind of automatism is possible thanks to the events that processes trigger every time an execution cycle is finished, signaling to everyone listening that new data is available on each of the impacted datasets. [![Indicators-Process-Output-01](https://user-images.githubusercontent.com/13994516/68976798-f01cf400-07f6-11ea-9ffb-198b5853a220.gif)](https://user-images.githubusercontent.com/13994516/68976798-f01cf400-07f6-11ea-9ffb-198b5853a220.gif) @@ -1009,7 +1002,7 @@ An output dataset must reference a dataset definition. To add an output dataset, select *Add Output Dataset* on the process output node menu. -{% include note.html content="Remember to establish a reference to a valid dataset definition after creating the output dataset." %} +{% include tip.html content="Remember that an output dataset must reference a valid dataset definition after creating the output dataset." %} @@ -1023,9 +1016,7 @@ To add an output dataset, select *Add Output Dataset* on the process output node **{{site.data.data_mine.process_dependencies}}** -process_dependencies: "Process dependencies are references to various data structures on which the process depends to function." - -While processes run autonomously, most process participate in a value-adding chain by which a process produces a data product that other processes may consume as an input to be processed further. This means that bots—while autonomous in their particular jobs—do depend both on other bots and on the data other bots produce. +While processes run autonomously, most processes participate in a value-adding chain by which a process produces a data product that other processes may consume as an input to be processed further. This means that bots—while autonomous in their particular jobs—do depend both on other bots and on the data other bots produce. ### Adding Process Dependencies @@ -1042,15 +1033,13 @@ To add a process dependencies node, select *Add Missing Items* on the process de **{{site.data.data_mine.status_dependency}}** -status_dependency: "Status dependencies are references to a status report that define which process the process establishing the reference depends on." +The reference is established to acquire the information relative to what the target process is doing. For example, by reading a status report a process may learn when was the last time the referenced process ran, and what was the last file processed. -The reference is established so as to aquire the information relative to what the target process is doing. For example, by reading a status report a process may learn when was the last time the referenced process ran, and what was the last file it processed. +The status report referenced may belong to the same process— which is called a self-reference. In such a case, the process is learning what it did the last time it ran. Also, the status report referenced may belong to another process—another bot. In that case, the dependency may be of the Market Starting Point or Market Ending Point types. -The status report referenced may belong to the same process—what is called a self-reference. In such a case, the process is learning what it did the last time it ran. Also, the status report referenced may belong to another process—another bot. In that case, the dependency may be of the Market Starting Point or Market Ending Point types. +* **Self Reference** is mandatory, as a process needs to read it's own status report every time it wakes up. -* **Self Reference** is mandatory, as a process will always need to read it's own status report every time it weaks up. - -* **Market Starting Point** is a status dependency existing on Multi-Period-Daily processes so that the process establishing teh reference learns the datetime of the start of the market. Usually, the reference is established with the sensor's Historic-Trades process status report. Multi-Period-Market processes do not have this type of status dependency as the date of the start of the market is implied in their own dataset (a single file with all market data). +* **Market Starting Point** is a status dependency existing on Multi-Period-Daily processes so that the process establishing the reference learns the datetime of the start of the market. Usually, the reference is established with the sensor's Historic-Trades process status report. Multi-Period-Market processes do not have this type of status dependency as the date of the start of the market is implied in their dataset (a single file with all market data). * **Market Ending Point** is a status dependency existing both in Multi-Period-Market and Multi-Period-Daily processes so that the process establishing the reference knows the datetime of the end of the market. @@ -1060,9 +1049,9 @@ The image above shows a case of a self-reference status dependency as well as a ### Adding a Status Dependency -To add a status dependecy, select *Add Status Dependency* on the process dependencies node menu. +To add a status dependency, select *Add Status Dependency* on the process dependencies node menu. -{% include note.html content="Remember to configure the new status dependency, and to establish a reference to the appropriate status report." %} +{% include tip.html content="Remember that a status dependency must be configured, and must establish a reference to the appropriate status report." %} ### Configuring a Status Dependency @@ -1087,8 +1076,6 @@ Select *Configure Status Dependency* on the menu to access the configuration. **{{site.data.data_mine.data_dependency}}** -data_dependency: "Data dependencies are references established with other bot's dataset definitions, determining which datasets the process establishing the reference uses as input." - Most bots consume data other bots have produced. Because bots need the data as input for their calculations, processes establish a data dependency with the dataset definitions of other bots. The reference provides the process with all the information needed to decode the dataset, enabling it to perform the required calculations. [![Indicators-Process-Dependencies-02](https://user-images.githubusercontent.com/13994516/68993034-7840df00-0873-11ea-804d-d24e88ce25f7.gif)](https://user-images.githubusercontent.com/13994516/68993034-7840df00-0873-11ea-804d-d24e88ce25f7.gif) @@ -1099,7 +1086,7 @@ The image above shows data dependencies in one bot referencing dataset definitio To add a data dependency, select *Add Data Dependency* on the process dependencies node menu. -{% include note.html content="Remember to establish a reference to the appropriate dataset definition." %} +{% include tip.html content="Remember that a data dependency must establish a reference to the appropriate dataset definition." %} @@ -1112,9 +1099,7 @@ To add a data dependency, select *Add Data Dependency* on the process dependenci **{{site.data.data_mine.status_report}}** -status_report: "Status reports serve as temporal annotations that bots read every time they run to know what was done in the previous cycle and what the state of affairs is at present. Status reports are dynamic, and they change constantly, with updates after every cycle of the associated process." - -Bots do not run continuosly. Instead, they run in cycles. A cycle usually lasts until there is no more data to process, and once they finish, they shut down until the next cycle is due. A status report is a file every bot writes at the end of each cycle with information about the last run, including the datetime of the run and the last record it processed. +Bots do not run continuously. Instead, they run in cycles. A cycle usually lasts until there is no more data to process, and once they finish, they shut down until the next cycle is due. A status report is a file every bot writes at the end of each cycle with information about the last run, including the datetime of the run and the last record processed. A status report may be consumed by the same bot producing it, or by other bots. @@ -1134,8 +1119,6 @@ To add a status report, select *Add Missing Items* on the process definition nod **{{site.data.data_mine.execution_started_event}}** -execution_started_event: "The execution started event is the event that triggers the execution of a process. It usually references the execution finished event of another process on which the process depends on." - These references determine when a process is due for another run. By listening to the execution finished event of the process it depends on, it may wake up just in time to process the new batch of data the dependency has just delivered. Bots form a sort of multi-branched execution sequence with an indeterminate number of dependencies. Every time the bot further down the tree of dependencies finishes a cycle, it triggers the execution of multiple bots listening to its execution finished event. @@ -1144,7 +1127,7 @@ Bots form a sort of multi-branched execution sequence with an indeterminate numb To add an execution started event, select *Add Missing Items* on the process definition node menu. Items that may be missing are created along with the basic structure of nodes required to define them. -{% include note.html content="Remember to establish a reference to the appropriate execution fisnihed event." %} +{% include tip.html content="Remember that an execution started event must establish a reference to the appropriate execution finished event." %} @@ -1158,9 +1141,7 @@ To add an execution started event, select *Add Missing Items* on the process def **{{site.data.data_mine.execution_finished_event}}** -execution_finished_event: "The execution finished event is the event that processes trigger once they have finished an execution cycle. The event is broadcasted to whoever wants to listen, so that other bots may know when the process has finished its execution cycle." - -The execution finished event is responsible for triggering the execution of every processes that depends on the data a bot produces. If bot Alice depends on bot Bob, Alice will listen to the execution finished event of Bob so that it may start a new execution cycle as soon as Bob finishes its cycle. Alice listens to Bob's execution finished event by establishing a reference from its execution started event. +The execution finished event is responsible for triggering the execution of every process that depends on the data a bot produces. If bot Alice depends on bot Bob, Alice listens to the execution finished event of Bob so that it may start a new execution cycle as soon as Bob finishes its cycle. Alice listens to Bob's execution finished event by establishing a reference from its execution started event. [![Indicators-Process-Execution-Started-Finished-Events-01](https://user-images.githubusercontent.com/13994516/68993254-39605880-0876-11ea-9ee7-9f49976bd2dc.gif)](https://user-images.githubusercontent.com/13994516/68993254-39605880-0876-11ea-9ee7-9f49976bd2dc.gif) @@ -1180,9 +1161,9 @@ To add an execution finished event, select *Add Missing Items* on the process de **{{site.data.data_mine.product_definition}}** -product_definition: "The product definition node holds all definitions that make up a product, including how the product is calculated and the datasets the product is comprised of." +A bot may feature multiple products, thus, there may be multiple product definitions. Three main aspects need to be defined in terms of products: the characteristics of the dataset, the composition of each record, and the way records are calculated. -A bot may feature multiple products, thus, there may be multiple product definitions. There are three main aspects that need to be defined in terms of products: the characteristics of the dataset, the composition of each record, and the way records are calculated. +{% include note.html content="A product definition usually references a plotter module, which provides a graphic representation of the underlying dataset" %} ### Adding a Product Definition @@ -1202,7 +1183,7 @@ Select *Configure Product* on the menu to access the configuration. * ```codeName``` is the name of the product as it is used within the bot's code. -* ```singularVariableName``` is the singular form of the name of the variable representing the product, as it will later be used on strategies (*i.e.:* ```bollingerChannel.direction```, where *bollingerChannel* is the name of the product as defined in its configuration and *direction* is the name of the property. +* ```singularVariableName``` is the singular form of the name of the variable representing the product, as is used on strategies (*i.e.:* ```bollingerChannel.direction```, where *bollingerChannel* is the name of the product as defined in its configuration and *direction* is the name of the property. * ```pluralVariableName``` is the plural form of the variable. @@ -1217,8 +1198,6 @@ Select *Configure Product* on the menu to access the configuration. **{{site.data.data_mine.dataset_definition}}** -dataset_definition: "A dataset definition contains a configuration file that defines all aspects of the dataset. There are two types of dataset definitions: multi period market and multi period daily." - A good part of what makes datasets easy to consume by other bots is the fact that they are standardized in terms of their structure. It is that structure that is specified in the dataset definitions. ### Adding a Dataset Definition @@ -1236,7 +1215,7 @@ Select *Configure Definition* on the menu to access the configuration. "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": "Your-Data-Mine-Name/Your-Bot-Name/@Exchange/Output/Your-Product-Name/Multi-Period-Market/@Period", + "filePath": "Data-Mine-Name/Bot-Name/@Exchange/Output/Product-Name/Multi-Period-Market/@Period", "fileName": "@BaseAsset_@QuotedAsset.json" } ``` @@ -1248,10 +1227,10 @@ Select *Configure Definition* on the menu to access the configuration. "codeName": "Multi-Period-Daily", "type": "Daily Files", "validPeriods": [ "45-min", "40-min", "30-min", "20-min", "15-min", "10-min", "05-min", "04-min", "03-min", "02-min", "01-min" ], - "filePath": "Your-Data-Mine-Name/Your-Bot-Name/@Exchange/Output/Your-Product-Name/Multi-Period-Daily/@Period/@Year/@Month/@Day", + "filePath": "Data-Mine-Name/Bot-Name/@Exchange/Output/Product-Name/Multi-Period-Daily/@Period/@Year/@Month/@Day", "fileName": "@BaseAsset_@QuotedAsset.json", "dataRange": { - "filePath": "Your-Data-Mine-Name/Your-Bot-Name/@Exchange/Output/Your-Product-Name/Multi-Period-Daily", + "filePath": "Data-Mine-Name/Bot-Name/@Exchange/Output/Product-Name/Multi-Period-Daily", "fileName": "Data.Range.@BaseAsset_@QuotedAsset.json" } } @@ -1264,7 +1243,7 @@ Select *Configure Definition* on the menu to access the configuration. * ```validPeriods``` refers to the time frames handled by the dataset. -* ```filePath``` sets the path on which files will be stored; make sure you replace the values corresponding to your data mine, bot and product. +* ```filePath``` sets the path on which files are stored; the proper name of the data mine, the bot and the product need to be entered. * ```fileName``` sets the name of the files that constitute the dataset. @@ -1279,17 +1258,15 @@ Select *Configure Definition* on the menu to access the configuration. **{{site.data.data_mine.record_definition}}** -record_definition: "The record definition is where indicators define how many fields, what fields and in which order those fields will be stored in each record." - Bots store data in the form of arrays of records, in a minimized plain text file and the standard JSON format, although not as objects with named properties, but as arrays. [![image](https://user-images.githubusercontent.com/13994516/68998023-303eae00-08ad-11ea-9baa-ddea801c7d6d.png)](https://user-images.githubusercontent.com/13994516/68998023-303eae00-08ad-11ea-9baa-ddea801c7d6d.png) -The choice of plain text for storage of large volumes of information has benefits in terms of not requireing any sophisticated technology to store and serve the data (*i.e.:* no database is required). +The choice of plain text for storage of large volumes of information has benefits in terms of not requiring any sophisticated technology to store and serve the data (*i.e.:* no database is required). Best practice indicates that there needs to be a balance between the amount of data that is stored and calculations that may be performed at runtime. -Storing objects with named properties in the JSON format would be innapropriate as labels would repeat over and over, creating ridiculous amount of unnecessary information. However, storing arrays in the JSON standard facilitates the manipulatioon of files in the context of JavaScript and Node.JS. +Storing objects with named properties in the JSON format would be inappropriate as labels would repeat over and over, creating a ridiculous amount of unnecessary information. However, storing arrays in the JSON standard facilitates the handling of files in the context of JavaScript and Node.JS. ### Adding a Record Definition @@ -1306,8 +1283,6 @@ To add a record definition node, select *Add Record Definition* on the product d **{{site.data.data_mine.record_property}}** -record: "A record is the compendium of information stored for each period of time, including the start and end datetime of the period along with the properties that provide the information the indicator is meant to generate." - Records may have as many properties as required. {% include important.html content="The order in which record properties are defined around the parent record definition node is the order in which they are stored in the actual record." %} @@ -1332,9 +1307,9 @@ Select *Configure Property* on the menu to access the configuration. * ```codeName``` is the name of the record as it is used in the bot's code. -* ```isString``` determines if the field is a text string or if, in turn, is a numeric field. Dates are stored in the *epoch* format, which is numeric. The reason why this configuration is important is because fields which are strings need to be stored between "double quotes". +* ```isString``` determines if the field is a text string or if, in turn, is a numeric field. Dates are stored in the *epoch* format, which is numeric. The reason why this configuration is important is that fields that are strings need to be stored between "double quotes". -* ```isCalculated``` determines if the field is stored in the dataset or if, instead, is calculated at a later stage. A value *true* means that the record is not stored. In the case ```isCaculated``` is not defined in the configuration, it is assumed to be false. +* ```isCalculated``` determines if the field is stored in the dataset or if, instead, is calculated at a later stage. A value *true* means that the record is not stored. In the case ```isCaculated``` is not defined in the configuration, it is assumed to be false. [![Indicators-Record-Definitions-01](https://user-images.githubusercontent.com/13994516/69139160-3ce72000-0ac0-11ea-9566-a259c9ea6194.gif)](https://user-images.githubusercontent.com/13994516/69139160-3ce72000-0ac0-11ea-9566-a259c9ea6194.gif) @@ -1350,8 +1325,6 @@ The image above shows a record definition with four record properties. **{{site.data.data_mine.formula}}** -formula: "In the context of a record definition, formulas are used to assign a value to the property, usually in the form of a variable declared in the calculation or data building procedure." - A formula may act in slightly different ways depending on the ```isCaculated``` attribute in the record property configuration: * When a property is calculated (```"isCalculated": true```), the formula assigns to the property a value that is calculated in the calculations procedure. Even if the property is not stored in the dataset, it is made available as a *calculated property* to other bots that may have the dataset as input. @@ -1375,13 +1348,11 @@ To add a formula, select *Add Formula* on the record property menu. **{{site.data.data_mine.calculations_procedure}}** -calculations_procedure: "The calculations procedure processes information to be fed to calculated properties. Calculated properties are not stored in the data set; instead, they are calculated in real-time as the data is consumed by others." - -Bots store data in the minimized array format as a strategy to build the lightest possible datasets. For the same reason, a bot may chose not to store information that is easily calculable. +Bots store data in the minimized array format as a strategy to build the lightest possible datasets. For the same reason, a bot may choose not to store easily calculable information. For example, imagine an indicator that stores buy and sell volumes... does it *need* to store the total volume? Given that it is so easy to calculate, the answer is *probably* no. IT is up to the developer to decide, but it is certainly not required. -{include note.html content="It may not be worthy to store information that can be easily calculated at a later stage." %} +{% include note.html content="It may not be worthy to store information that can be easily calculated at a later stage." %} The calculations procedure calculates the properties that the developer chose not to store in the dataset. @@ -1401,8 +1372,41 @@ To add a calculations procedure, select *Add Missing Items* on the product defin **{{site.data.data_mine.procedure_loop}}** -procedure_loop: "The procedure loop holds the JavaScript code that cycles through reading an input processing information to generate an output." +This is an example of a procedure loop code snippet, in particular, the code that calculates the Popular SMAs product of the Simple Moving Average (SMA) indicator. +{% include important.html content="Calculation procedures may only access data up to 48 hours in the past counting from the datetime of the record being processed. For that reason, the calculation of a moving average requires temporarily storing data in an array." %} + +```js +/* Loop Code. */ + +let candle = record.current // Our main dependencies are candles +variable.last200.push(candle.close) // Add the current close value to the last 200 array. + +if (variable.last200.length > 200) { + variable.last200.splice(0, 1) // Remove the first element of the array to keep it at a maximun of 200 elements. +} + +variable.sma20 = calculateSMA(20) +variable.sma50 = calculateSMA(50) +variable.sma100 = calculateSMA(100) +variable.sma200 = calculateSMA(200) + +function calculateSMA(periods) { // Having a function saves us from duplicating code. + /* We check we have enough values to make the calculation */ + if (variable.last200.length < periods) { return 0 } // If we dont, we define the value is zero. + + let sum = 0 // Initialize sum variable. + for (let i = variable.last200.length - periods; i < variable.last200.length; i++) { // Iterate through the last periods + sum = sum + variable.last200[i] + } + let sma = sum / periods + return sma +} +``` + +### Adding a Procedure Loop + +To add a procedure loop, select *Add Procedure loop* on the corresponding procedure node menu. A procedure loop node is created along with the corresponding JavaScript node. @@ -1413,7 +1417,14 @@ procedure_loop: "The procedure loop holds the JavaScript code that cycles throug **{{site.data.data_mine.javascript_code}}** -javascript_code: "The JavaScript code node may hold any snippet of valid JavaScript." +The platform allows developers great flexibility as all valid JavaScript code is allowed. + +{% include important.html content="Until a robust JavaScript editor is implemented on the UI, it is recommended to write code on an external editor and paste it in the UI code bubble to ensure consistency." %} + +### Adding a JavaScript Code + +To add a JavaScript code node, select *Add Code* on the corresponding parent node menu. + @@ -1427,6 +1438,22 @@ javascript_code: "The JavaScript code node may hold any snippet of valid JavaScr procedure_initialization: "The procedure initialization is used to initialize variables used by the procedure loop." +This is an example of a procedure loop code snippet, in particular, the code that initializes the array used in the calculation of the Popular SMAs product of the Simple Moving Average (SMA) indicator. + +```js +/* Initialization Code. */ +variable.last200 = [] +``` + +### Adding a Procedure Initialization + +To add a procedure initialization, select *Add Procedure Initialization* on the corresponding procedure node menu. A procedure initialization node is created along with the corresponding JavaScript node. + + + + + + @@ -1438,8 +1465,11 @@ procedure_initialization: "The procedure initialization is used to initialize va **{{site.data.data_mine.data_building_procedure}}** -data_building_procedure: "The data building procedure processes information to be fed to properties that are stored in the output dataset." +This procedure holds the core logic of the bot, as it features the code that performs the calculations required to produce the new data set. The code is split between a procedure initialization and a procedure loop. +### Adding a Data Building Procedure + +To add a data buildings procedure, select *Add Missing Items* on the product definition node menu. Items that may be missing are created along with the basic structure of nodes required to define them. @@ -1454,7 +1484,17 @@ data_building_procedure: "The data building procedure processes information to b **{{site.data.data_mine.plotter}}** -plotter: "A plotter is a device used to produce a graphic representation of the data generated by indicators." +Plotters usually have a one-to-one relationship with bots, as they are often created as a customized solution. However, in practice, there is no constraint to creating any number of plotters for a single bot or a single plotter for multiple bots. + +The overall workflow of a plotter, while reading a dataset, consists in: + +* Applying a set of rules to identify certain data points; + +* Use those points to form lines and polygons; + +* Apply graphical styles to those lines and polygons. + +Those actions are performed recursively for each record, and the graphics are plotted on the charts in real-time as data is processed. @@ -1466,7 +1506,57 @@ plotter: "A plotter is a device used to produce a graphic representation of the **{{site.data.data_mine.plotter_module}}** -plotter_module: "A plotter module is a device within a plotter that contains the definitions to create the visualization, often times, for a particular product definition." +A single plotter may have several modules. Different modules may be used to plot each product of the bot, or as best suits the expected use-pattern. + +[![Plotter-Module-01](https://user-images.githubusercontent.com/13994516/71016077-eab41180-20f4-11ea-87e7-7262ff10a1ab.gif)](https://user-images.githubusercontent.com/13994516/71016077-eab41180-20f4-11ea-87e7-7262ff10a1ab.gif) + +{% include important.html content="For a bot to use a plotter, the corresponding product definition must establish a reference with the plotter module built to interpret the dataset." %} + +[![Plotter-Module-02-Product-Link](https://user-images.githubusercontent.com/13994516/71016079-eab41180-20f4-11ea-8afb-96321f9c68d8.gif)](https://user-images.githubusercontent.com/13994516/71016079-eab41180-20f4-11ea-8afb-96321f9c68d8.gif) + +### Adding a Plotter Module + +To add a plotter module, select *Add Plotter Module* on the plotter node menu. + +### Configuring a Plotter Module + +Select *Configure Plotter Module* on the menu to access the configuration. + +```json +{ + "codeName": "Module Name", + "isLegacy": true, + "icon": "histogram" +} +``` + +* ```codeName``` is the name used in the code to refer to the module. + +* ```isLegacy``` is ```true``` for plotters modules developed entirely in JavaScript, and is ```false``` or doesn't show in the configuration for plotters built with the tools featured in the data mine hierarchy. + +* ```icon``` is the name of the image that illustrates the corresponding layer in the Layers Panel. In case the banner is not specified, the *default banner* is used. Current possible values are: + + +| Value | Image| +| :---: | :---: | +| ```Default``` | ![Default-Banner](https://user-images.githubusercontent.com/13994516/70896073-5bbdd100-1ff0-11ea-84e3-5bd90475976f.png) | +| ```Histogram``` | ![Histogram-Banner](https://user-images.githubusercontent.com/13994516/70896074-5bbdd100-1ff0-11ea-8dad-ccc6cfe64beb.png) | +| ```Band``` | ![Bands-Banner](https://user-images.githubusercontent.com/13994516/70896072-5bbdd100-1ff0-11ea-8626-61c36bd22a3c.png) | +| ```Moving Average``` | ![Moving-Average-Banner](https://user-images.githubusercontent.com/13994516/70896075-5bbdd100-1ff0-11ea-9909-dae33e57f65f.png) | +| ```Oscillator``` | ![Oscillator-Banner](https://user-images.githubusercontent.com/13994516/70896076-5c566780-1ff0-11ea-8a5b-20077257c560.png) | +| ```Trend``` | ![trends-banner](https://user-images.githubusercontent.com/13994516/70989177-744aeb80-20c3-11ea-88f5-d10473fa79b9.png) | + +Custom images may be added too. To do that: + +1. name the image in the following format: ```The-Image-Name-Icon.png```; + +1. place images in the ```WebServer\Images\``` folder; + +1. in the configuration of the module, use the name without the *-Icon* ending (e.g.: ```"icon": "The-Image-Name"```) + + + + @@ -1478,7 +1568,14 @@ plotter_module: "A plotter module is a device within a plotter that contains the **{{site.data.data_mine.plotter_panel}}** -plotter_panel: "A plotter panel usually shows numerical representations of data over the charts on a small window or panel. The plotter panel node features definitions to make that possible." +Beyond the graphic representation of data over the charts, plotters may also feature a *data panel*, that is, a numerical representation of the data showing the exact figures corresponding to the column or period that is in focus (the point of the charts where the mouse pointer is at any point). + +### Adding a Plotter Panel + +To add a plotter panel, select *Add Plotter Panel* on the plotter module node menu. + + + @@ -1490,7 +1587,37 @@ plotter_panel: "A plotter panel usually shows numerical representations of data **{{site.data.data_mine.panel_data}}** -panel_data: "A panel data node represents a data field to be plotted on the plotter panel." +The system renders a panel featuring a centered, single-column vertical arrangement of data, with a label and a data field right below it. The label of each data field on the panel is given—by default—by the label set for each panel data node. + +### Adding a Panel Data + +To add a panel data node, select *Add Panel Data* on the plotter panel menu. + +### Configuring a Panel Data + +Select *Configure Panel Data* on the menu to access the configuration. + +```json +{ + "valueDecimals": 2 +} +``` + +* ```valueDecimals``` sets the number of decimals to be shown on the corresponding data field. + +To override the default manner in which the data is assembled on the panel, the following properties may be included and defined: + +```json +{ + "labelText": "The Label", + "labelPosition": 15, + "valuePosition": 20, + "valueDecimals": 2 +} +``` + +* ```labelPosition``` and ```valuePosition``` represent the vertical distance of the corresponding fields to the top margin of the panel. + @@ -1502,8 +1629,9 @@ panel_data: "A panel data node represents a data field to be plotted on the plot **{{site.data.data_mine.data_formula}}** -data_formula: "A data formula is the device used to assign a value to the panel data, usually in the form of the name of the corresponding record property." +### Adding a Data Formula +To add a data formula, select *Add Data Formula* on the plotter panel menu. @@ -1514,7 +1642,21 @@ data_formula: "A data formula is the device used to assign a value to the panel **{{site.data.data_mine.shapes}}** -shapes: "The shapes node contains definitions that define polygons and styles to be rendered on a per period basis over the charts." +A plotter recursively renders graphics for each period or candle. This means that a definition of what shapes need to be drawn for a single period is required. The plotter does the same thing for every period or candle that is visible on the screen at any point in time, for any time frame, as long as the corresponding data is available. + +[![Shapes-01-show-off](https://user-images.githubusercontent.com/13994516/71016080-eab41180-20f4-11ea-84c6-a514b87d568f.gif)](https://user-images.githubusercontent.com/13994516/71016080-eab41180-20f4-11ea-84c6-a514b87d568f.gif) + +In general terms, the process of defining what is going to be rendered on the screen consist of the following four steps: + + 1. Identifying points defined by ```[x,y]``` coordinates + 2. Defining polygons + 3. Defining default styles + 4. Defining conditional styles + +### Adding Shapes + +To add a shapes node, select *Add Shapes* on the plotter module menu. + @@ -1526,7 +1668,11 @@ shapes: "The shapes node contains definitions that define polygons and styles to **{{site.data.data_mine.chart_points}}** -chart_points: "The chart points node groups definitions of points." +The definition of a point consists of the name of the point and the corresponding values for the ```x``` and ```y``` coordinates. To create such definitions, point and point formulas are used. + +### Adding a Chart Points + +To add a chart points node, select *Add Chart Points* on the shapes menu. @@ -1538,7 +1684,14 @@ chart_points: "The chart points node groups definitions of points." **{{site.data.data_mine.point}}** -point: "A point is a definition of an [x,y] coordinate where x is usually a datetime and y is usually a value that refers to a price, as output by the indicator." +These coordinate points are used to define polygons. Values for ```[x,y]```coordinates are entered with a point formula. + +{% include tip.html content="Each point should be given a significant name so that it is easier to identify them at the time of establishing references with polygon vertices." %} + +### Adding a Point + +To add a point, select *Add Point* on the chart points node menu. + @@ -1550,7 +1703,84 @@ point: "A point is a definition of an [x,y] coordinate where x is usually a date **{{site.data.data_mine.point_formula}}** -point_formula: "A point formula is the node used to input the coordinates of a point." +To represent some form of variation of the price or a price derivative over time, a typical point formula consists of the following two coordinates: ```x = datetime``` and ```y = rate```. + +For example: + +**Point *SMA 20 Begin*** +```js +x = record.begin +y = record.previous.sma20 +``` + +The example above shows the definition of the first of two points necessary to draw the segment of the 20-periods SMA curve for any particular period or candle. In this case, ```x = record.begin``` is the starting datetime of the period and ```y = record.previous.sma20``` is the rate of the 20-periods SMA of the previous period. + +{% include note.html content="The above variables are constructed with information originating at the product definition that references the corresponding plotter module." %} + +To complete the line segment corresponding to a single period, the closing datetime of the period and the closing rate for the 20-periods SMA are required: + +**Point *SMA 20 End*** +```js +x = record.end +y = record.sma20 +``` + +The above system works very well for plotting all sorts of information over the candles, on the charts. + +An oscillator like RSI too needs the definition of the starting and ending datetime of the period as the value for the *x-axis*. However, the value for *axis y* fits a different scale. + +Anyway, the points definition is just as straight forward, for example: + +**Point *RSI Begin*** +```js +x = record.begin +y = record.previous.value +``` + +**Point *RSI End*** +```js +x = record.end +y = record.value +``` + +Points do not necessarily need to refer to values on the dataset produced by the corresponding data product. For example, absolute values for the *y-axis* may be used to describe fixed graphical elements, for instance, a horizontal line, or a box. + +**Point *Begin 20*** +```js +x = record.begin +y = 20 +``` + +**Point *End 20*** +```js +x = record.end +y = 20 +``` + +**Point *Begin 30*** +```js +x = record.begin +y = 30 +``` + +**Point *End 30*** +```js +x = record.end +y = 30 +``` + +The above four points are used to paint the background of the 20 to 30 value range of the RSI chart. The first two points are also used to draw the dotted line signaling the 20-value mark, and the last two are used for the dotted line indicating the 30-value mark. + +{% include note.html content="The reason from separating the definition of points from the definition of polygons is that any point may be used by one or more polygons." %} + +### Adding a Point Formula + +To add a point formula, select *Add Point Formula* on the point node menu. + + + + + @@ -1562,7 +1792,13 @@ point_formula: "A point formula is the node used to input the coordinates of a p **{{site.data.data_mine.polygon}}** -polygon: "A polygon features definitions that make up shapes and their styles." +Polygons are defined by two or more vertices, which map points in a charts point. The graphic representation of polygons originates in styles applied to either or both polygon bodies and polygon borders. + +{% include note.html content="The order in which polygons are ordered around the parent shapes node determines the order in which they are drawn on the screen. In cases on which polygons intersect, those drawn last are drawn on top of those drawn first." %} + +### Adding a Polygon + +To add a polygon, select *Add Polygon* on the shapes node menu. @@ -1574,7 +1810,21 @@ polygon: "A polygon features definitions that make up shapes and their styles." **{{site.data.data_mine.polygon_vertex}}** -polygon_vertex: "A polygon vertex is a reference to a point defined in the chart points, constituting a specific coordinate to be used as vertex of a shape." +polygon_vertex: "A polygon vertex is a reference to a point defined in the chart points, constituting a specific coordinate to be used as a vertex of a shape." + +Vertices are the main element defining polygons. Once created, each vertex must establish a reference to the corresponding point in the charts point. + +[![Vertex-01-add-and-link](https://user-images.githubusercontent.com/13994516/71016081-eb4ca800-20f4-11ea-8fee-4d38ecfb0596.gif)](https://user-images.githubusercontent.com/13994516/71016081-eb4ca800-20f4-11ea-8fee-4d38ecfb0596.gif) + +Drawing curves requires two vertices. Painting areas, such as the background of a chart, likely requires four vertices. Note that vertices are not capable of featuring a style. Instead, styles are applied to polygon borders and polygon bodies. + +{% include important.html content="Bear in mind that the order in which vertices in a polygon are arranged around the parent polygon node when their *rotational symmetry* property is selected, is the order in which the plotter computes the points. This means that points need to be in the correct order, for example, when drawing a box. Imagine drawing a box on a piece of paper without lifting the pencil... that is how the plotter does it." %} + +### Adding a Polygon Vertex + +To add a polygon vertex, select *Add Polygon Vertex* on the polygon node menu. + +{% include tip.html content="Remember that a vertex must establish a reference to a point in the charts point to be properly defined" %} @@ -1586,9 +1836,11 @@ polygon_vertex: "A polygon vertex is a reference to a point defined in the chart **{{site.data.data_mine.polygon_border}}** -polygon_border: "A polygon border is a shape defined by at least two vertices used to draw lines or to apply a style to the stroke of a closed shape." +A polygon border needs at least two vertices to be defined. A border serves to create a *stroke* for the outlines of any sort of polygon, either closed or open, by giving it a style. +### Adding a Polygon Border +To add a polygon border, select *Add Polygon Border* on the polygon node menu. @@ -1598,7 +1850,85 @@ polygon_border: "A polygon border is a shape defined by at least two vertices us **{{site.data.data_mine.style}}** -style: "A style is a definition of color and opacity in case of a polygon body, and of color, opacity and line thickness in case of polygon borders." +A style applied to a polygon body or a polygon border node acts as the default style. + +### Adding a Style + +To add a style, select *Add Style* on a polygon border or polygon body node menu. + +### Configuring a Style + +Select *Configure Style* on the menu to access the configuration. + +A typical definition for a polygon body style looks like this: + +```json +{ + "default": { + "opacity": 0.2, + "paletteColor": "UI_COLOR.DARK_TURQUOISE" + }, + "atMousePosition": { + "opacity": 0.3, + "paletteColor": "UI_COLOR.DARK_TURQUOISE" + } +} +``` + +A typical definition for a polygon border style has a couple more properties: + +```json +{ + "default": { + "opacity": 0.2, + "lineWidth": 1, + "lineDash": [0,0], + "paletteColor": "UI_COLOR.DARK_TURQUOISE" + }, + "atMousePosition": { + "opacity": 0.3, + "lineWidth": 2, + "lineDash": [0,0], + "paletteColor": "UI_COLOR.RUSTED_RED" + } +} +``` + +The first section of the definition refers, as explicitly stated, to the default state of the style. + +The second section refers to the style assigned to the column or period on which the mouse pointer is located. This is very useful for highlighting the specific zone of interest as the user browses the charts. + +* ```opacity``` is probably self-explanatory: it may range from ```0``` for a fully transparent object to ```1``` for a solid object. + +* ```paletteColor``` sets the color for the *fill* or *stroke* in cases of polygon bodies and polygon borders respectively. Use the variable ```UI_COLOR``` followed by a ```.``` and any of the properties in the following list for a selection of currently available colors: + +| Variable Value | RGB Value | Color Sample | +| :--- | :---: | :------------------- | +| ```DARK``` | 48, 48, 54 |   | +| ```LIGHT``` | 234, 226, 222 |   | +| ```GREY``` | 150, 150, 150 |   | +| ```LIGHT_GREY``` | 247, 247, 247 |   | +| ```WHITE``` | 255, 255, 255 |   | +| ```BLACK``` | 0, 0, 0 |   | +| ```GOLDEN_ORANGE``` | 240, 162, 2 |   | +| ```RUSTED_RED``` | 204, 88, 53 |   | +| ```GREEN``` | 188, 214, 67 |   | +| ```RED``` | 223, 70, 60 |   | +| ```PATINATED_TURQUOISE``` | 27, 153, 139 |   | +| ```TITANIUM_YELLOW``` | 244, 228, 9 |   | +| ```MANGANESE_PURPLE``` | 91,80, 122 |   | +| ```TURQUOISE``` | 74, 207, 217 |   | +| ```DARK_TURQUOISE``` | 2, 149, 170 |   | + +* ```lineWidth``` is the width of the border line, which should be greater than 0. + +* ```lineDash``` is used to turn the border into a dashed-line; the first value corresponds to the length of the dash while the second value defines the length of the space. + +{% include note.html content="When using dashed lines, bear in mind that the drawing from one column or period is independent of the next. Big values for dashes and spaces may not work well for lines that span several columns." %} + + + + @@ -1610,7 +1940,47 @@ style: "A style is a definition of color and opacity in case of a polygon body, **{{site.data.data_mine.style_condition}}** -style_condition: "A style condition is a rule that allows applying an alternative style in cases where the rule validates true." +Style conditions offer a way to introduce additional intelligence on the visual representation of data by using styles that respond to certain conditions. + +For example, the *Percentage Bandwidth* plotter module assigns a different color to the Moving Average line when it's going up or down. + +To do that, a style condition features it's own style so that, when the condition validates true, the new style is applied. + +Style conditions me be created in a sequence so that the style may continue changing upon the validation of a series of style conditions. The system evaluates all style conditions in a sequential order determined by their position in the rotational symmetry around the parent node. Each condition that is met may modify or add up to the resulting style that is rendered on screen. That is, the final style is the result of the sequential application of the styles defined for each condition that is met, on top of the default style. + +### Adding a Style Condition + +To add a style condition, select *Add Style Condition* on a polygon border or polygon body node menu. + +### Editing a Style Condition + +Select *Edit Condition* on the menu to access the configuration. + +Style conditions work pretty much like conditions used on trading systems. The main difference is that the variables available are solely those provided by the bot product definition that reference the corresponding plotter module. + +To continue the example of the Percentage Bandwidth plotter, the following is the condition that determines the newly added styles: + +```js +record.previous.movingAverage > record.movingAverage +``` + +As hinted earlier, when the condition is met, the default style is modified by a second style that may be created on the menu of the style condition node. + +The definition of secondary styles is slightly different from that of the default style, as—in such cases—the ```atMousePosition``` property may no longer be defined: + +```js +{ + "opacity": 0.55, + "lineWidth": 1, + "lineDash": [0,0], + "paletteColor": "UI_COLOR.RED" +} +``` + + + + + @@ -1623,6 +1993,8 @@ style_condition: "A style condition is a rule that allows applying an alternativ **{{site.data.data_mine.polygon_body}}** -polygon_body: "A polygon body is is the fill of a shape made out of at least 3 vertices." +A polygon body needs at least three vertices to be defined. A body serves to create a *fill* for the outlines of any sort of polygon, either closed or open, by giving it a style. +### Adding a Polygon Body +To add a polygon body, select *Add Polygon Body* on the polygon node menu. diff --git a/docs/pages/suite/suite-designer-workspace-and-data-structures.md b/docs/pages/suite/suite-designer-workspace-and-data-structures.md index e33409f36..72c9e1273 100644 --- a/docs/pages/suite/suite-designer-workspace-and-data-structures.md +++ b/docs/pages/suite/suite-designer-workspace-and-data-structures.md @@ -17,7 +17,7 @@ This page explains the concepts that deal with the structuring and managing of t In practical terms, is the portion of the system you access when you pull up the sliding panel, hidding the charts in the process. -IMAGE + ## Workspace @@ -45,7 +45,7 @@ IMAGE As a concequence, structures of nodes are hierarchical structures, and a logical representation of how the concepts embodied by each node relate to each other. -IMAGE + ## Parent-Offspring Relationships diff --git a/docs/pages/suite/suite-hierarchies.md b/docs/pages/suite/suite-hierarchies.md index 8780e2a5c..3a4e1dca7 100644 --- a/docs/pages/suite/suite-hierarchies.md +++ b/docs/pages/suite/suite-hierarchies.md @@ -1,6 +1,6 @@ --- title: "Hierarchies" -summary: "There are five different types of hierarchies: Charting System, Crypto Ecosystem, Trading Systems, Network, and Data Mines." +summary: "There are five different types of hierarchies: Charting Space, Crypto Ecosystem, Trading Systems, Network, and Data Mines." sidebar: suite_sidebar permalink: suite-hierarchies.html --- @@ -9,11 +9,11 @@ permalink: suite-hierarchies.html On the designer, the head or parent node of each hierarchy is represented by the corresponding icon surrounded by an ever-present white ring, so that they are easier to locate within the workspace. -IMAGE -## Charting System -{{site.data.charting_system.charting_system}} +## Charting Space + +{{site.data.charting_space.charting_space}} ## Crypto Ecosystem diff --git a/docs/pages/suite/suite-network-hierarchy.md b/docs/pages/suite/suite-network-hierarchy.md index 5d636feba..da1878799 100644 --- a/docs/pages/suite/suite-network-hierarchy.md +++ b/docs/pages/suite/suite-network-hierarchy.md @@ -1,11 +1,13 @@ --- title: Network Hierarchy -summary: "Hover your mouse over a node for a quick definition, and click for the details." +summary: "The network provides the tools to set up your testing and live-trading environments, as well as the definitions on where on the network processes run, and where data is stored." sidebar: suite_sidebar permalink: suite-network-hierarchy.html toc: false --- +{% include note.html content="Hover your mouse over a node for a tooltip definition, and click to get all the details." %} +
@@ -119,7 +121,7 @@ To add a task, select *Add Task* on the task manager node menu. Select *Run* on the menu to start a task. When a task is started, the process instance of the bot instance attached to the task is started. Also, a visual indication that both the task and the process instance are running appear surrounding the corresponding nodes, in the form of a progress ring. -[IMAGE] + To stop a task, select *Stop* on the menu. diff --git a/docs/pages/suite/suite-parameters.md b/docs/pages/suite/suite-parameters.md index ec27269db..0e5d111c2 100644 --- a/docs/pages/suite/suite-parameters.md +++ b/docs/pages/suite/suite-parameters.md @@ -5,16 +5,14 @@ sidebar: suite_sidebar permalink: suite-parameters.html --- -{% include /trading_system/parameters.md heading="" icon="150-" adding="" configuring="" %} +{% include /trading_system/parameters.md heading="" icon="150-" adding="" configuring="" starting="" content="" definition="" table="y" %} -{% include /trading_system/base-asset.md heading="##" icon="150-" adding="" configuring="" %} +{% include /trading_system/base-asset.md heading="###" icon="" adding="" configuring="" starting="" content="" definition="regular" table="y" %} -{% include /trading_system/quoted-asset.md heading="##" icon="150-" adding="" configuring="" %} +{% include /trading_system/quoted-asset.md heading="###" icon="" adding="" configuring="" starting="" content="" definition="regular" table="y" %} -{% include /trading_system/time-frame.md heading="##" icon="150-" adding="" configuring="" %} +{% include /trading_system/time-frame.md heading="###" icon="" adding="" configuring="" starting="" content="" definition="regular" table="y" %} -{% include /trading_system/time-range.md heading="##" icon="150-" adding="" configuring="" %} +{% include /trading_system/slippage.md heading="###" icon="" adding="" configuring="" starting="" content="" definition="regular" table="y" %} -{% include /trading_system/slippage.md heading="##" icon="150-" adding="" configuring="" %} - -{% include /trading_system/fee-structure.md heading="##" icon="150-" adding="" configuring="" %} \ No newline at end of file +{% include /trading_system/fee-structure.md heading="###" icon="" adding="" configuring="" starting="" content="" definition="regular" table="y" %} diff --git a/docs/pages/suite/suite-test.md b/docs/pages/suite/suite-test.md index 309bfcc44..c6c5ffef7 100644 --- a/docs/pages/suite/suite-test.md +++ b/docs/pages/suite/suite-test.md @@ -164,14 +164,14 @@ permalink: suite-test.html

Network

Network Node

Task Manager
- - - - - - - - - - -

Charting System

Viewport

Time Machine

Time Scale

Rate Scale

Time Frame Scale

Timeline Chart

Rate Scale

Time Frame Scale

Layers Manager

Layer
+ + + + + + + + + + +

Charting Space

Viewport

Time Machine

Time Scale

Rate Scale

Time Frame Scale

Timeline Chart

Rate Scale

Time Frame Scale

Layers Manager

Layer
diff --git a/docs/pages/suite/suite-trading-system-hierarchy.md b/docs/pages/suite/suite-trading-system-hierarchy.md index 4370e354c..0dee5ae73 100644 --- a/docs/pages/suite/suite-trading-system-hierarchy.md +++ b/docs/pages/suite/suite-trading-system-hierarchy.md @@ -1,11 +1,12 @@ --- title: Trading System Hierarchy -summary: "Hover your mouse over a node for a quick definition, and click for the details." +summary: "A trading system provides the tools to build, test and depoy trading strategies from within a visual environment, with no coding requirements." sidebar: suite_sidebar permalink: suite-trading-system-hierarchy.html toc: false --- +{% include note.html content="Hover your mouse over a node for a tooltip definition, and click to get all the details." %}