From 15b8a9044d3adab64d0b8a2ca28169d3d4a90d5c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 7 Feb 2020 16:50:11 +0100 Subject: [PATCH] Live Trading working tested at Binance --- CanvasApp/CockpitSpace/AssetBalances.js | 6 +- CanvasApp/CockpitSpace/Speedometer.js | 4 +- .../FunctionLibraries/SessionFunctions.js | 12 +- CanvasApp/FloatingSpace/CircularMenuItem.js | 6 +- CanvasApp/Spaces/DesignerSpace.js | 8 +- Inter-Process-Files/AppSchema.json | 12 +- .../Historic-Trades/User.Bot.js | 69 +- .../TradingSimulation.js | 2 +- TaskServer/Assistant.js | 376 ++------- TaskServer/Commons.js | 2 +- TaskServer/ExchangeAPI.js | 160 ++++ TaskServer/MultiPeriod.js | 4 +- TaskServer/Session.js | 40 +- TaskServer/TradingBot.js | 8 +- TaskServer/TradingBotProcessMainLoop.js | 284 +------ TaskServer/TradingSimulation.js | 253 +++--- TaskServer/package-lock.json | 740 +----------------- TaskServer/package.json | 16 +- .../Icons/style-01/angle-to-parent-000.png | Bin 14997 -> 5713 bytes .../Icons/style-01/angle-to-parent-045.png | Bin 6088 -> 3441 bytes .../Icons/style-01/angle-to-parent-090.png | Bin 9014 -> 4484 bytes .../Icons/style-01/angle-to-parent-180.png | Bin 14688 -> 5766 bytes .../Icons/style-01/angle-to-parent-360.png | Bin 28763 -> 10314 bytes .../Images/Icons/style-01/data-mining.png | Bin 0 -> 10754 bytes .../Icons/style-01/distance-to-parent-000.png | Bin 3329 -> 2806 bytes .../Icons/style-01/distance-to-parent-025.png | Bin 5917 -> 3015 bytes .../Icons/style-01/distance-to-parent-050.png | Bin 6189 -> 3326 bytes .../Icons/style-01/distance-to-parent-100.png | Bin 6802 -> 3849 bytes .../Icons/style-01/distance-to-parent-150.png | Bin 7377 -> 4296 bytes .../Icons/style-01/distance-to-parent-200.png | Bin 7914 -> 4637 bytes .../Icons/style-01/initial-definition.png | Bin 0 -> 15865 bytes .../Icons/style-01/next-phase-event.png | Bin 0 -> 15210 bytes .../Icons/style-01/production-environment.png | Bin 0 -> 9095 bytes .../Images/Icons/style-01/task-manager.png | Bin 0 -> 4924 bytes .../Icons/style-01/testing-environment.png | Bin 0 -> 7852 bytes docs/_data/charting_space.md | 17 + docs/_data/charting_space.yml | 17 + docs/_data/concepts.md | 2 +- docs/_data/concepts.yml | 2 +- docs/_data/data_mine.md | 30 +- docs/_data/data_mine.yml | 30 +- docs/_data/network.md | 2 +- docs/_data/network.yml | 2 +- docs/_data/sidebars/suite_sidebar.yml | 420 +++++----- docs/_data/topnav.yml | 42 +- docs/_includes/sidebar.html | 4 +- docs/_includes/trading_system/base-asset.md | 11 - .../_includes/trading_system/fee-structure.md | 15 +- docs/_includes/trading_system/quoted-asset.md | 2 +- docs/_includes/trading_system/slippage.md | 2 +- docs/css/theme-blue.css | 2 +- docs/images/icons/150-charting-space.png | Bin 0 -> 12932 bytes docs/images/icons/charting-space.png | Bin 0 -> 4724 bytes docs/index.md | 24 +- .../suite/suite-charting-space-hierarchy.md | 420 ++++++++++ .../suite/suite-crypto-ecosystem-hierarchy.md | 6 +- docs/pages/suite/suite-data-mine-hierarchy.md | 536 +++++++++++-- ...-designer-workspace-and-data-structures.md | 4 +- docs/pages/suite/suite-hierarchies.md | 8 +- docs/pages/suite/suite-network-hierarchy.md | 6 +- docs/pages/suite/suite-parameters.md | 14 +- docs/pages/suite/suite-test.md | 22 +- .../suite/suite-trading-system-hierarchy.md | 3 +- 63 files changed, 1675 insertions(+), 1970 deletions(-) create mode 100644 TaskServer/ExchangeAPI.js create mode 100644 WebServer/Images/Icons/style-01/data-mining.png create mode 100644 WebServer/Images/Icons/style-01/initial-definition.png create mode 100644 WebServer/Images/Icons/style-01/next-phase-event.png create mode 100644 WebServer/Images/Icons/style-01/production-environment.png create mode 100644 WebServer/Images/Icons/style-01/task-manager.png create mode 100644 WebServer/Images/Icons/style-01/testing-environment.png create mode 100644 docs/_data/charting_space.md create mode 100644 docs/_data/charting_space.yml create mode 100644 docs/images/icons/150-charting-space.png create mode 100644 docs/images/icons/charting-space.png create mode 100644 docs/pages/suite/suite-charting-space-hierarchy.md 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 d458e70c160b8381a2c8c88c0630b25600501f3b..08de6ae0f7b48021d048a4d0086417cf63927011 100644 GIT binary patch literal 5713 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 E15)~F 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 r&l&Gv8Pl!*73%nZt<19 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 literal 6088 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 Umo2c6jQ}t;Hb0Vo$R*-`0F$;TasU7T 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 4b3583bc6bed342c7bba1673646ee7321b47cdea..4170f18ef805f5dbb6df01a659733a6c940e4179 100644 GIT binary patch literal 4484 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 literal 9014 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 EH%`e0q5uE@ 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 ce921425f9c65a5cf800fc287fe3622125ba4a5c..a6b79d09b4d2ac5800f43e239a740069b4a0de28 100644 GIT binary patch literal 5766 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 Y0YZWnZ4v#&|1>w$HM>!J-7Wrq02QGijsO4v 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 6cf98ad3f494a434c09e596827467e23b1bef62d..bbe1a83ffc01c3cf83bc65b3b07b2e5bcca714fe 100644 GIT binary patch literal 10314 zcmZ{Kc|26#8~<}>w~u|#kbM`~5<|2~Ln&k#Qk1fnCCid#KQBk5I z6(vmeLYBzR&-eHH`*;4h&pFR|p4U0|o_n6-OzTF^_p*BvT2Ln!ohIs}b2TSh&uj7hzI z{NBfTTF>tJ+5fKn)0>ZNZJk{?C8c>!D@tqX3>}?r7Z$z${AKm~cL*d`Y|MROXtmeO%gSrYRL>>|uTwiY~4tbxf`gG6jVEx64 zH%=YJv+~i`A0~CgTpEhlWbi&Hwz6=oeE1`C#(zFmj7#2p_=&N`dbInClUaY#!ipu! zI7l3M6SmY=cz!-Qwjlkhs+Q`cdBZUfdpf(nswC$twfM)N-)p)XSPuooKDPhL8lRma z@Uk`gmfr_?_&Mcz4qb|BP=`dOo1>pg?#TvE*~j#07<~W2P%m>qqlNm5TLL}Vw-R#3 zH?Bp;-(xsQ^;S@zOO=X~CiE&lI)#yT>U>}9XEm5$$;*5nytQEqLT-4WT*gqUo6AZx z+#zz~+8ek$xNy~Rzt3umn*S^1dx!^F(_T=M4;0>%U^owTC$IUA_wh^?ArhLg^`nnZB5 zDtfw=-P*&xjJyq-BPnlN?larVnBuMd#fYOrTftgc;n+!JVcSAo2zrd7-p9lc;MvOi z=Z{A)Y#o9`0H@9$AJW4$OhQRx)4WFo33=4oYQ~i}fX>vN-F^gK{9SF6ZD=`AzKiY; zgUi)-O~B$frBw50iye)dK(}it@ep3XGxDAYxAG!B&sws@)CA#HVd1$~&!}isU=yj8 z2$q#Ac48`>2}(gHNYsp`&RDTL&vgsL7&ED~l@`g1ZXcRlXrB8h)Ynd=%$9r{PS{bc zE53WXv9yV?0GL?~Wh-?)R*Kh#58b=={7Ea6$WX(C0BSHX;L)emfn`nRMmb02(>ZuX z#QlpW08V}~df^^~<9zk7V;_+vVHhTgi7G@&J#Fv$c%+SxZq?03_AXKGZ{dl$q(lKC zwe9Z018nlS{W6+xE9YwU;deO8KYKPawRL(`u+-?`0shA>m+|z4T-Ud&o|#fp{BEx1 zVkqBSQouRc3K1f@c~O-Z>P0z-?HT9XUq;KtCUwG~b4-j#)m+S%$WZUyO&+}~Xj>&< z>^Hr(1wW;8;SH2tkN$j?xLw!pI3<^X zgGt{ZdVq%8GhQru=K&_fSAusl#dMi%5Rsz>Z?)2wpMP@vs|T+?UA+`E(j( zF#{O7&58y;bM3yg4BEd}1%uqWV(Jy>t)ib6@0id3Wc4>hAA`7rzz}8prCLg<;4zZ2Q@l z=rj}3MZ!ZwiV(K+SVgv-L|diVQ9mhKg}^MWj303CR`W7rD_^!F)al_S8i*Wiw)1N) zQr9ZnD_U-ezuGL3PU|HUCweB+bl~30(Daj$SD$mP&d0cicOUL6g=xaM8JOs*;x<6B?A5 z#Dm=ai6$0K6zEM+O*qn3%w4_x#FLlUaO9Mdp?_b1k1R3ugv**zYQ{42C{ z<$*R~PAh#iT?z!b@A+AkjuBFNzgX-oD)ZI%1B2vxhoN$e9CwbNRjMV`N*L4cROYO@ zlN-%Dx?B|Fq}RSn4Pg}$?EU?|e;InZ$I^#?tR&4pV>5DE0)0eaC^I$Yblzo|kU6cM ztLZ@$1(DZZFHo)<@!OLU&BztIQ2m6tgmI*eMm{R+r6M?!5htX?L zq5OL9*4?A-6R%+wXI$E@u~JRkblJ%F0Q7Yrr3irmK8H4cpi$75Z3IQ;ySmY}IjlFFA)Z_HOaygIha3 z-d3M`#sqeNUcE3wzDl(fLaPvab8xL@TP%=@V9{{`I*$9(?)a zyt$&8Y0Iks4+#DN+XoEZ+mcv-=FbUSf!B)K-{FaYy;*a4TzH*b zMU}r^kJ)|ic#oo2{|F6Bj-dDK%bsmEJ^~XuaY!I-Ze(DueX- zF&z`3jFP`Zf1LPv80EIy4i~=so9tH7spn<1_AiZbTWWz>&466wj)wbU6JEBnd!4t* zvvSCvG3;~e>zkAuHGLQB$*Pynx27Cac!&j6Y=&68d&l|MqKEt4w|xZABvvuUw`j9#q1aHX`A01HmYnCcdE$9G@7 z+fG83f48ir8cU;iYC4k0}Th614XGP^=64@ zjZ62a_qqXha(!UOJKk21+HHB2 z)>BM=!DZ|%4I-$Y<~62xiQDr%@o20P23~Mck;GG5K+!?>v<2HxdDW)jff7>!c8k(^ zSJvVjbbT#eDnu_5b+kyn2r1G3>?%1m+ICjp1L+Xg!ZhR0Et?y_e#h?6_|dx$_rbn9 zMi=4l|H-)2f)Zcp0y@ujeSI}Noq0>ZW&JLlz01s$)85bEM`dCzUR{v<;L{xItV?dn zP$AZNYHs8cVa&a{^#jh_G2fTMxVoO_j}Wx9x$^<_z-c|s_ehIAv|uE`?@pWK>%SOe zhlODUj@aD6nERYvkJoh&{+Y~ncX1q`zj5S&nEfjaW9$2=7ted;_m^Su@b)luH|CGS z)UnQWOKRe5=26$PPbdKAg}dIY-h0ryeD4Xnal6*|1d}ZTQ94z;4^CeCm&>Pedpbl} z;UI)&A$eRS^?VqiD}>GOLOeymr4E(3)tP-4jubW6`V-K+PM#)YZ$n0B{h6GPdrRsQ z)kO{gtA_`th(FfQ(pRCN%)f3gesKs$apz*WN-X3N!m%bZEZAltY7giy$JTeCGxnLb zncqjTbVC8v^nDHY2h-QDJQL=b5g5kYhjG5{x;<{vCF}Z#p-=0LYRH(6yESMsU;F#N zu=m3kkQ^>{uLSy2=vr21t>3c~+u)!$rz;d*Iiw0x*%xS5pFCWBk z`uFJpKO7SFjGTEX1DH6=GQl`2EE>K4xE>KAbI=1{-Kt+>DW^=&Ut@}voUM2KH_Q{^ z3%j>5!%Xj%0gkKTwY=;4&rp^=6Fa)sQVgZYXOItwPp1OyDsCa2c?Tg3UFeaWSQ4m4 z5is-9w=}_$no0h@5|YDJ;WRJ)uQO1OqgQ@fnQ()~j-0Z=-(^5+hR)F`4+p6W2)oH? zw4_#-2TAbKdC*Hxv|Ez*JXj9 z1{aiwr9YqHxh?l;v$2@Kr{!zg+@14fo1}FoE{kHn`dk%1*T0{G!?FfR^x|t&9ESc; zknOUWA%vlNzd-_VXg97Yjqzc(Q0dRDjn3w2yrE6kP*Dx)BOX%IHO2>HA>9$t|8{Mm zIABVT;j2!_hSLye#+cf?;|`25opOkm+A<{yjvfnM%Ow>WN%{$*9M@2KhX4APwCCUO z&xy4dntZkSYsnE9NfijK==qSGzr&Mw4HsTNlz#f)2gL5Sz@wE)9-C?YAk}g9by%G@ z6ZU9?K5V<8qiU6gYpBI@S8nQqj+w^v7DO9cK7e`o(?4np1|td2{afUcWH{uuy{f7K zt=gNZyMZ+Krh)ez@&Rf0iPL^jSbT%0PN5$<*r_jK{j+80MCt^FU>Gyyn%SWXw}1W+ zC|#XAe-4#}0v=M%nQ*1JKkS)$Oci(m{8N zkeUUT10EP^n*d6=jeR{qRXbC%IK|VJIVB8;32>EcidPy0>pB?s>(@e;8$LM|FV0#H z{fvch@6DdES9ln{-jrvA@LsG8RjTx{>~^`=e!GrEPI{2kxdY)Om2FQxfGf@7Zi`sb ze&myZC1JKYQm*1Q>;Gp!<<{U0*${cZ9X-ZY+uWdd;Ve%OShAX2!&8vF6dVsdXi5hz z@yPQpX#|R;wmCe-kQn0DF$m3#OVbIXy@ycvn0Ut>p&1ccHk!K#g-7>a5(lXB)W3=H zTw|o#{axgN)Cx+*lc_uSJKEBuEPdw}0Aq{%C3~8~-}~~7$GXw;eriM83tc-xE7!$Z z@9X2p(WwlRc7y_bv&9D!r8$ahkpk$nyeye$3Y_m&_n-Xko-!dnFX#`fWb;XygaWma z_|dBuX?jNc9TYu_E^;K{zd!7h$r*m#tJ~>CTRmT%$Ix?4!0YxDzwX>7&cm#~@vntp zsSZhYH(_sM?>>)KD_vF>9Yi~*5+-bCmR_)BQh(^0;b^vzp0m32WVPlu za^Lfu6qyZ#;P9i!-UTkgzDpk9TOGI7jJzP9Xq=Jj%x-TLO^!h*V*;xO_^WCUpBwkf z-P*m-cZhZD5z;pVofzjbkz3>V0=5qLN?*wPA@Mm))>mb3b_}rY3N>jQZ_&JbH5{mB zDrtUk3KYgye;j<@O z7Ul6{A8=$eh{GMilB?(fu#fRAJXde4!ER{Wjkq%YJ;|6qG;|UdxG9kDIhzQ4e(aBq z3=!A|`G8MV%A9?Y&%R1BPyt5C@! z*vs@9?h~c18_rdeFi)C~h!~d+XWr)VRlY}{yyE4e!!Qo+ht+bUB1U>?HXPDLX{ARQ zo9sh1y&;rlX$l9s&e)5vb^NZ|O`xG(Ak;&`S*?(fh7 z1Hw*yIOdI+9&;E4)=!W?GclA*#fenA%RVs7g?isy(FJX6=_YVBgAeJ5lK^y*`KS%> zhzsTBqc;7IO7Wo||3{e!9m07yTBr>rnaYnf;T^Olt1yj4TMOvsyh}3hls|64ID9*Z zxRT2hq>CvR_PrcFnVWtg$bNJ$q>9G}J1@LLXzB5P8&l6>S+x=`kG#5hBG;X7pe2Wg zfDQ+%dI)6S7W5DpAqeg(7d{h5@)sDItQKvP(yYVkOF+x51vy-jRFr#O#jT@Y6qtyBJCWDJ$cu zsWHK$*KYalv8R7{m20t$L^d;zIl0rqytl}Nci}UIHT?(LPB4vmogBz*PX$0bN@}V= zN_SzyzvLk;Pq0B!90bnDygkl1tVKz|$N}yV#=;T1(*xR?7;_hIoz#<}MIn?^hZ)%g z$fP|73;o8aA&lf^taq1j(`zNP$DW@V!+nzq(6_U_`v?Gwg&74meaZUZAnYt%oVZ@+K4pI-1PetNJnBylHvp&{ld5ksi zRrQGN#l9`J+Mg7}I?1wt&&NIAayjVfNIht*%A2GrZCI^$g)EV6MTLWox#dF3vpT7# zSp0Cw7Gf?bcH#x4mgYis(8M@gV51BcyKWvt29yy_hgWtP902Rr{OEDc!XDt%j{0y- z5z!T6s?SZc7IuLm%gbw}&yq@nUwFME1Jb3ucWf~+1@PrT=@IbWillrn;t0#DYdqZ) zOZtY~h16%s9m|9}d@>e13*PBvVk{n1Q+j+X8vjCZr`NOIjsssJINJa7pE6u&#;En= zFL9JoibW|K=w3sT7gZG8bs&vO6NX#m4BWqZtvRu&sPprZ=;i{F?aGasc-lDZFW=|p zAebQlau=RjM&ztb1~u`;k!0DGUt+ATlYY+zi~`vq z5sX5rJb0gB7kqN!-g5b{WLZ4yrUl(mzk!jvo`y1Pi?|k~i@yy|br9{}Y#8G&dHf9$ z)r~)N`&NOUWh9XJT-hB>AGO&xu?y)~LSDmj9J;$wv2{-NkCagg7Hwt}GA2L=-d@67 zOt!jyMt$4f;e(!MuJd!7Yi0Kplrw{1-y zJFJVhp`Ip`dI^R5p!z2%?4V%UmHKV_QWFw=Gl{naB~1Sl*H@Er(sQx^#!T?25%zJ^ zz{tP$VL||3-M)dourlN(RsdjBv|q#q@mSFr_(YKXu%04tAWfkKu@5DtW2iq&S5!p2WfOYI=veSHV}tR}sn!BbE$!;x`(H zMJbQB-jRXd@?MXyKDluF?xQFHIOk0n!4`y|B0{C&ux(4~U&gEnHH5260F{+|8#nax zobS#wbQPS^705qh5No7gJG zbU?x%+++&f(m*IGw_-JM49rv#`u#CB6`}sbMEz0VdFoO$QS$_wilP6(c=;<_YJq>5 z0UgM0ME5L`($awo#ll`S)#y(SfU8?M|Gg&zo#!oWP~3e*1{H$bP6mS|u|I9J*E6AbuCs-Qcvl{z@Gq;9eT6d@AT>x;&eq1^FenKhWS~+%q5Mj~ zef{Bcj`EJL-trU?m~yix8hsfp=ouXv_?}=^$>`28{Gh0K5T!wI$IR8 z6&JVDTtVgsZ^{~K&d%72C~O?02z@a)#2KFI zG+4Mx>TP`r+394l>S+>*$}P5msc^%PGRutnl4l6vNR}X(&0m@mkVSe~OO=2dx2awV z>!#;i`<6DJV2hk6pntACCjz~V6wwUtjF)N8Wcf&+c~uvmoU*J$C^l*+{mssrc@fPAn3WIrm_3_Y(Po`Ztdfi77J(p`Q zb~mSUT_rD7^F?~=0pI)Wvn3_gY!y*31yvp1<%ctqW9Pp04L=$E94MKqv3TFv(s8SV zIfF^0it>Ct`G!j|rr7giy_wr-FWUWw^EEYYhAVD6B#~2?ww>>mNRtn8^==uW<0|f|utqs%q;W>hue5MMA@#RIk->HG1#)`^IMSNusoi+d5l%16Mq2 zIYFM9Sk&6dJ?<8F%6%e-7WpMK67tKC6RY@yzs<`^I_o{j6Pg&-FUnS=bjx90nv;l8 zh{u|8Mf>mMQtlfhIQlm}{kYQFBaNm*jWX%Qa~4mJVd?ux>O~48HG<=Ae}g~h;aP%E zae=}z=q)~xDVB(#ZqL(KL3|15f06^KV%U>zM;coFQ^W1s3`Bnpt?A)d@^E;kEmr&8 z`nIFy3HtAN!uv!m(13u=A_i*N`UnKOUDm`!W#!HPy*jV|jqBb_kBf5T}j z28yBhh>Rr;ng7#RlA?&B;C4lzky+xHyNeON#cm=N)c zPR;%r+5D-t;Gl=b6{C08z`kt<4%I_acc7msHxnnPcaX61|L;_y_eTpUhvL|_509R7 z=rX|J{F+zS#yH#~(`5JiJA$!6|NCdZ zFsdy%Bt(}&-zS18%uTYzQ*HkCVp4ErCXX!CUuH-HUq@)SS&rfO2rn*Em>)Ad^L zlteQxy6`*KYb)UGwZrTy#aj8`vJheuKb*NxqmwFlMUtk(JVpY1 z&z=~|FniWO_=JG~Z zeVMART+Zlgu$doI~P96M4*gS z8KzmQk6kn(fU4R;jM3*tJE^Yzt0Gg9W~a3}&v&lhzZ7S=s8WUv6LZ-FUf*@gBm5-icee}m_@tc`CExys0NS6xL9@8dpe!zoFxKBe11 z!kqdqYwhKqrK_g85r@G289k)NUV$Zy`)s=3y~2+cdmY*$dlVR>JCo3z3gLjZgnClV zgU1H0A->(U%mvWGxrT0iONRG{0GAtPAt#H9tI&o*NmZtuEunw@HTgxXsvw*%T;s2G zG2&L>$_r~EfIG-*k%8He4BXU947AKC38}LlIaN}Bvols5++Q(H>0hRb2*oob3FxWW z4i^31Hx*%|t-?}8uj=YWL2{cu^Uw(MCp%K{t5nqfh#7qqaA_|V<4Je`PP|o8Mwhof zo_jtx7MtNe{yr3HFEvz8`(NB<`LNxKI&h+-Txin51kQAy{o|vCai>XEp$%f#1?2;; z`8hWZRO31@Q`G{8h9;EHVtslBSRy>^srS8+TD`IS@ME6ALqJM?_}MUD8C`CdbuZiS z;t|kRp(AO5#~2B^w%J&w@m%cMWO2g;rDU!B`UvRP^GSaXk$&7e7Oy}(*C6t3s5$S) z%7`wU=Z9MH%Z?B7yjYSm*l%OQTJ>~=x-Ua6w#b=bz4fR~@Z$@I2Q!n%>2s(3>)RGm zZAZD;r_=P~*L0ZQ(X~aGa@RX&e(Cln!#C!;rotND&N+6rIL@2gnJ~fz?X6sSV08P9 zxg7jK8j4;Dglxrx?IQnEV?I@-&L>lEMHXHv<9{lmA2}L=VG$YoB5Ho^pmhM%bz1Gj z+}ja6RRA-x^9P-LI%Rngt6M&lEeDgqrH{jIOMkvcscn@&uNc+5*=_+#4~khOZ;99lO|lyAgJ!mkCk0ynpNdc%KlJukAPP!q*y zaRS>-;KfKp;jz#TeVvaj4<~hlE)Q8GE}9QXVgtpoZ}jD{tDRRZzRv3)=o`B>;qRHc z&CO*1L0b+8TGly zmr8Ex?JcD*{h2+eWd!xDpe@xG%iAxHUO-4%H# zVM|17Iy~NNTtR@fMP@D6{Om@kDq1&RMBosyg;{LQ^w;x#(RM|6j*44qK-LKzHSQaa z&S#Ez)oHBDgIQzpVvmZ!o7ES|uhzHiTXULSUeQ0cj)! zk?yg*58vPW*T%Tdz4zSbb5DHEIkEb8H0f!$X#fDA*Va->V~uP$5xMX#rFq>B0rlx`M1q>G<{!4?=ysXeafW= z9rRX$dRA)g)JlqlHaUftnmVAj)>LpHo?d{xN7k0ZoKgVJoO-rqVHp_^_4bc>2&oin z9E@6d&Qt!fu%V$bWOjDuJ0imI`x<@1ue{dLOwGu%72?6{voGV(V%-TBH-M@3CZUZlWT$3@;WO!25D4;Nvb zIC4TnKyk?hT>d@DH=YH6I>vcZ#p1!1jCM#fH7mgTg@ZcmLOywfJ^=sC zNpMMN>o45ckG=#1So*#hY&5AV!;-f0lj4&q@5f%%nS+I0Xr(w8VbA}_uUu<)Uta>K z+#K%t6g+@fhzbe5Mf8oqC8;#n-ERo`b+kB^&ADma^dN}vMW~x@ z3JfEDIPm<#Vq*McD*C)ZQ7J+gp%Xh3oS zgrJ57pGJsJ%AgRE7Vsnbk`8!v-trhgV<0fH6$4}<=Xhrr=3K_N8hp8D6m|1O8GP@7 zPYd;K3ZiaJ(lulAF=2<2Rz_SRIAHw2Mdz2I2=K7gocEqJ2XO}{kmEl|5BhMR0mZPn zFcAh;HR_!^-gs8BS4Z0X;JAo|KPcP46SgM)6KWjK>AsA{1JT-mH`zfuv7YlomyE-5 zz9-U^+dVj!kKcWdYbGfcSRKrdHV7UQuCT5bfc+q?g^9qf=oHfJP!5xyA0t`=WzRzp za@aFpo+_{m*4u4u)D8dK83s3`zsB^|cepm)80`DaWOz$x#T>^Ui+ z(6f`ps!4^Q-13FM+0e~sGSt)aIS*!3l!7_2%lj+{VD8K$;gifg1KbaEGyaCF>%9mx z!Wb6-iNP7i(|}M(^>OF>d?sF{@qrw`i{nyWepQPB)a?)stdA);+0GAVMnyXj0LCFw zE?YVJ4$w}0Iw?`KFrbx3j~zX#2~H|d0~8T71NC}*x8QqgPc3b)4LohGBwqVx15dd` z88$|zxDjzJsO~Rq*dSM7ZnL=hBT2mER zNGitvssBNt&A8}-OkNOr3BGXd5DY|L18x+wtWmKBBOfATg@W4}*kYjp6ck0miG`d4 z@1g)NikYjC01cH0Iqbh}IDCtilO?bM+7#h&-mwgbOSlmqkUZTZ z5dd$@5*>=_NbrIK`uiU*cb;%6Iw=jKd))Pm+{Lcz7rJmU@HsRRyfD}0h+7BOJ}9)| zS2vx5hkLV?jCWd1X63$S8dx~ffq)c|q;zXsO4oW!38e{bYGdjE7aos7IkUE($u)6* zS@Ixe4(?5?SBCfy%U^acW<9o&&B(!3xAJnuq`)2JX`DhY%&LNTQ|ZWlPmikK6j*ZMM%Q&viU=%bJ_-Z92OzV@)me+ZSglP4cmoVFVx0JvVkAzkayp zy5KukQ3A}9fM<`&2WueRLq1>XEE|=%u()h&u9K!Xibr^;qjm~=6tmq&QgrkGQ>uEh z{BO3UgPHeYKI12oruV+2&@RF~Lm=6Yo5;P3o7Z&a^E!(Q&7BU+`8l@v&xQCh)eAFk z>mj+*bmXsU>ay+BYb_l~7J~YL(8MZcDl@U;V|nKD{VK^;%d?xrHASiMy({R z8&lgB^*jPHTvZ}5+&8!ZX|!e-6zAwd_*ggZuzKY|Sk$)k3rUF!*L=i>X2q*SfxKc96tO?8U zV5BsENPBq*>KBsa8qT#l?iPo8p<$ba(4>)pqF49R<}|j09$S^o)^*mv@2TOnY3FQY zaZCWF3U#3H1EX@*w7=3u=9b`c^47%YDRi|vR>px0rD-bQYvArNr8LXp@Pm^t=*V{U zR$6-;EJtyxo5|NaTQ#2Tkt`0>QLY4o%#YnRDcKYg(+9P6K5J&|mp7*?Mm%f=Zs`jo zjxlw6;~fQfU7=$QnB!}C)72m4)xQb&)%fT*VhT)Q526@qcaDY-P$`!Igdtr}H+W6| zyvt!@&>Cfhaw0~)H5xS8&>%v(u`k?Y+lTQB!EWnfIyd{7yU}^BdZ#e_ zuQ?{b?{_I~5WDvBr{xb}k6oG42;8tr@#93GlfsaQ0Wb3jwjram@EEAB|z2)Fi zh3YHYF0noQu-JWU#^$wjNy*n6nxiQ8!DUt@xICD` zHZ08G0{o5WWm%&AG-`)Fd#*m2;Q{p?$%zOord`zpTAh^ z&natj{*(O%g_)D#2#s+Ulq!Q4o=N%>n`;YUF^{EEWX#|tLVW8uOLelKn5A-F`ng2| zP*o3X*%vc55fdnjj%&#TywB)n+BS2n&=jiq00)MXkw;EdJyCR%?-~{rrPOP5Q$(K! zw;4_Yp?<$4&>U$!?ih!YyKoT%FM$iY__OYnrgBwxErMf>qkzE(Uc-R+!vw@~)9HRN zR89U9Cc&+HS&bZs409)*N_MT2DUFh`h6j%MBr+EQh?#h7vn%1a%V17IfF=h+uEh$y zY(}otRQKxikONhf4&=wa8GF}v8Z`#-q?1OxV25QsDS59B@N&`HGUZrB&6?v?t6H4} z1D2)luy2H>YH6rnLte4PgxYVDF)ruNrf~0Es;fiK-{;A;?AUA+05nI^Y`aFR35)Q zRkUt*+G15RXHe%p=Ac*mspR`tHcy^Db(x`Z$U>|^5%>BeAb0Q2#k8q@@}o85z{Aj4 z@z`$R#ZxCja9DC#M9LI)F=o|&dR)>k7{h!6qVLVc`iBYMXdZHTM`@fMt}ypgfP3JV zs7(P)@>Rvc;)ivxDKaw-G$fgOAzLnUlS7rC-sA7{zw`_|uy9)3Vg+To8B}l*DlFZ{ zXOHWcU?}G2-a6N_w^`KQBj{S}_fjt2;I&eYGvr8KvP^&?zI3PN&DUiNj zjGLJKAG5j|05v@;Y7KW(SFJJ6jgq+8P7f(D0272W=)$Kfq8hXrx$SdJxB**wv|l&h zF;Ok#lV=B>pQoteUsOwQ1tD+kXWfrhChv$Vmq)ZE=2gw1n#t@Pf*L^B9mw;G5OD(n zl>zg;Bfc@lSsB*5b_R@5=>w1T0l~H{p}KKKb-zaQ>(-Uw@Uw!o z$L}ye7f!Y8c>DU}%@;R>{7ONyl*&ZSn=D+zbNN0XJV-Rj_oGG7srayZrJMpzFsiwFaP)9PVSaWf85Ab#~8y4z1|`eXz_$r<8eE| z>T$;I*@`yr(Sy>R$4r*Vz4W)($#h=pr7lCX;(_#dKr5cu6`!Y9?2%$Ipu@f z?yfc)*<> z4NaQ245Rsou-Td9l8D|bMb$606Lo<}n}-+oq15l%6Uj%0+x3A}H^P|U{yv54%gMkT zV{JDc<3nZ{h~PR9rU5Y}+Y(7tcVnK%zD|mJ-}&iI)!&`jH^~FCXlZ9*s4(CyN9;>= zA%v<~1(-a`dd1oR5p=`fee0`Fmhv2iizrUzhKS3Z_)c`j>Qu5R9p!Yziqg8tF+=(s zJ;8^s5~T>Y(umhD?F{wSmI=e2bJL4IC3~?2PeVWj?*WWF!YmN9pCy!S1A8>}4j-NY zy+J;@brsyQ9btWV%iNv)ao5Bzg3P6rqG3mySmzfJq3F%mj708&yTIKptSIG{Y|9b9 zd*MuR5iAGD*=9_cX?`F-()Q7qskEHi(_wA}{ZCo48c_dF8*NvSCC30-YKE`115^#( zA2YsfRePN0QE|c}62^~%BA7d@Q{)#I^{21Y7P2cS|EWWB(olDZF;OH-nHSB(k<{=x z&bW8il>{H_%^lyWCm~m6kAB~>%f_;G#d^Xa7dX`*@T_eG8fD_XQ_xv9?={S#zYSrK z4e`BKSEJM7Ib29}60)E|*Zb0vuQnOlkVsUw4C^$)nhb^<+wXt%019$OQ-@nZzLssMTn<_v)CCZX#U2$i@1ThA-ciy{k*F z4!V2^t#-*!PKAJ)@m*U?j^N$=&`Xb5-Tsd0b!v6K5aSt;Zt3_YU)Ol_p!AzIknepqAeVr;f|m`K_XRkfHDG@lUEApQ2bVrJRuI_CC% zgn^%Bs3bNb7FV)R)?KsxL?1Y^rAWYzDe!Tc#bV^V$+d3pk_l7Ni2?{+k9>wFyFHtf zTAqu4XI($g2gwjWIJ#M0DL*{wOIV+7jXq_3@XW1+?IsmNxr+aROQb*jhY&iefkcLq z!PYeLKvGKTIDbtI)gaR{u`dpo>fqS9s}K0?6Ai~!HhVhMEppUu@Trlv*VZ1+2rPVH z;AI{^y`lyQVOo7X*Okr$B--|PemmZM+r4ihv3!wHLs3UpuaD{SXXf8Ic3l%s<;M1# zDwpw+AqJlItu3|9f%}#Ir_G1gjZIXGMAj5fe4pW)u+dz#hR<~`>2hYW;QBBB4+gRe z8ndn&ffh-eK!^i(+O<4)cgoy%hRUB-~%rsL*dHV9?>dKJa-*6hTm@v}z-@^|bl=Nz_`cb71 zA)N(kZ<_<2zdn>SJ5s=o>W288Yn+G#(g#Iutlw<0>1O6`)L?to{gO6iOaBRBn&*xy z9bV*8pT_*O{WetdSyml<{Z*vwq>esI?e0;tQM@89@tL@YGEcF-FQo#od6?3@Wq zSu?4V^J|4+n7ZMWnL5n+UE_myO|CF{f(0`ZJ*V3U2RX$wzzhWpsE1EI+kd{A&&|@v zMxL$akzYG)miimA`704I{%h(k(!f!u_INY0kk_u8FHfy@n>0G{w6Z}+zvh`4qqVgZ z%8lP9;SGaUGfb-)kV?SbzMzZ^I&PXW+WMGkd~nJAtG&iEiJ{};d4cq8hO8D34Kk`v z5DN{`(I1&tBx38CNdSF79HdE;{=l}}{PgVCnR}Dx=nFBEN7f+emQnkh`;0dHBu;;x zFJ4@fWW2M-DDU_9O=tga-}Fp=#3MAh?5pfI%d%MCyKkV?2`I|5&c}AFJBwSyvz+U^ zI2dkZPi~Rt5;?6}Y>!j;JqGT#MDRkqa);W7UvO3F=mDTyfa;XXHElfVGD_lD+47DM<+o*u3 zkOO*;d!BSwjVvn_z+1NF$aKuf2bqO`vBUI49VZQ=2l01)_*FQ;@4g|^73h*9V_92% z_>WWmZ>;rOMd?XT7^nf;U!}*@Ah~l(HyE!GYB9Uif-OZ%^CO|(ZOIYGGpL!>Urw#m zdHReUfr2y#Mw$>X9HdmyQv*)+{yp?R^@+h!C!nNJWXJnrL`Zhz{DMZ3l_SmA{ei(& zVM3s);|#X#)I?^JZzUG0QZm>n?Qk%2Ms*ML=q8WGc%-v;gno5L%i{J#t*F%42iOpy zA0-Ba66+O$dPwF9>Bx{FE*S?PvvaF6j3W$@5UcJaDGvHmOHI? zm2F&s7)Z_jia$r?cJ}cI3>ZFgNZ_nHeX4fy2v_~dDEF?WK3w>c=|JD6XP)(pQaXN1 zGRvZVKxs$Tvy&dw_KO=S?>jd7lh^wh=t>_<&ep`TJa5u4BHVJ(5myUw>{&&LETP_SZyP%R@vRT~a{IO`KbdvRb!=5FR$pXB!)jjk=%4@wGB7w6E{eh#SKwpi=x=fxm?3BM#6-AfgtMg4$;>;1|gI(8_zuA|% z9`QJ~#8h)SR!8EE@;e*s0VWA!dPaBit|kUfqQvLR+bh9F+qk@B?c0Gi-2A}%ila?B zPNNb+fVAY9cTwsA{)=2TOH=4sxl9M@#qN<_x*WS2JD3 zI<8y%M9EXF^9e?r0v))CeVH;4hyC^?;|@j6P|Kg%=HG81j?~Y`|4||I|9VrbvdOUfKiy&e&6nGghYasviUBXVSH%u-Q3vAh&6+*nRxf_BJmlQ{X#2_1- z)PED;6k9_-#=sPU{H`f+Cu_5!;3ZNzmU8Z5MDEpns}RO0zM0ksOQ?8~d~RLmZ()ji zfKb6kzXT_@{Li=iUtqZIa;>y&U$+aQJ03Am^647`(st5rdyZo$TKC7q@>#w+3wn0! z^o|`rAUF7vy-ov*3o>Mh#kZ2>iNIUz*Z5Z3$>*K7X>^|hTI~g>R=lCfC8(e?oyXf^ z>D9PbD!VVI95fR--DcD>-X~>un!*-SK3t`V3_F%;ZG|0Iv1Dyq(}@!8uWwq1ofLr6 z=+fY2iTaMPvm&3`eEaHm(I*IOQkUx8TFPQ+^w&HA&>Q%QJsQ;P-Cd!>Fn5D*qVHR@ zkj`|zzOK+iQ{;Gdz@u8fF4QPf*~Bflz+5tdEjdP+KQ;y!_b16YDG&kwz(4%_i+^u% zxwImn`avJVtEJQ_+t2Ks2-;x+AR}!5J>OFOni5uIq3UrPqdAKcK|Qxt!p0aX-Z-ft@&p%C+D}d`rvF@Qt+h&Hj-Q0nX`DE=2a-1 z{@bZ9qgUAp7=P;5g{+H!Oh$H06S;GCuxr~>f}?o zNPfz2S<%rnF@BzvV90x2$hZX7ebsF*MRHS7i^|{MZ<#Yjz_NIZO=^5-J!H^z&W=^u zsQ=0EmRBYt@``kV&aY``=_5kNRzKtWRmQ|Y+S@ncm&2ng|5%|9+ZNXzuN!!EcfXgD z@`0ic4O$};0{&fIwS6a9cM|#3N|c=n*)X7<1+j)Lp0Etc+|ugh$D3(e>{!*e%Myp) zUd0eOcX{p&W=~E~OgYF(YQ!%U&nXrH>D=7)gtp!W-t5LhblD~L0*9jvs8zs5^TG>n z%D>LRA4jm}PTa)zvc!lZvWxHh5tlq}@6l-(P8{8kA$DB-;79M~%&}!|TrR_2&|P=? zJ^wu@evkNZxx>z~pD}8nEINU)E>2zL9lDp8JNr_6vm_H!xcX?3^+aM`;x(KJiQ63d z303v`uK4>g!M2mBdFzBbc@15b-z`4VNJ6JW(FxmQ4ZoR(cWhv1z~ENDBth^wzpC!M z;UE`ItNT~|Y-l|yJu)OB<{40i-ukJK^~%_YVE@&clZ_KEAbo`r(qcJo>7@{)g{*~ z_*2#jZY%z$eE3wppLj28 zn06MZFx>^`BhCwpxMVEnMo91EtwIUAs{#o25}cwMP{VLm2IbpXscXrrQIwkdfot;D zt#!y^!L)Ngia&F?v6>GkLFSY4P%ji;QP*!wyy>nNvhPH&<hT6v8TWL zWApIf9OX5N@QxU%Z#@F{-e(7s@kgi$LCeqfqM{pcQtX7ew~;}udr$Zg%D;?|&o;~$ zSOJyT+pBy(_eIgZp&7rRAvlG(T$!AZ$MjlKM&ueCi>3${)?m zYKhgV;lK9|99=tV*nq-A#{SmJS2jQB7{|#1btfg?f{lxDw&@40{O`TGk#YMsB7hPnDW2(LiB506eNB~)K4F$6x5H@>$&ykcM3T55y~ zU;7gJN!9x(%8psw6>Q-B)*z{Gu<#N$W0A#;9x;OsKl)5vM-_7l4R9}lA-ct{93<|@&^siQ zO1GBEGy4cq_Ti>*4^^*e01cN%l}VBBZX6b8){HS2{VFxxUT=z;Jtb_KN@#$WtW%!Z zhWd!K*Ci=mCiPnCM6dFs?g^MATug;$u)h0J=fnn)Y9>9@pi>ZiN|^`P((f@{!MK>p z)+cs_LayCF&;W+zK|`=HzQ@2dFs1R zQZ{oC9TTn}%9wycvO?a3wW*ZW@3c)?=X|n9@|S0c{(U+y|G--Z()nSl>O$`zcD{}3 zxk;rR7BHtEqX9EW80I)9@2zP!*viqHl%;GW>ww9oRtXoCx@_CZqMZsN(G ztL;BPQ58sK2mKXuk|IuwH}wjfW9U*=sVg>%jcVwKrCkfHE!BtJSYCO-ar(V?lsl>W zV-E^Ods})GzC^7i3j8$&J>0&6jW?R7Gq+IFTu(g_|4DjD9S}V;);kEVPtw7@V4;z_ z?9NI96__kzeoABG$(?}0ccNY9Y4a7~gHgrf%s740Flsy}DAg%0?(8dM6YFPW zfD;w~VqH#mY3Q!feO%)m6Q=3(oBinCI;^{(2m8ZGjckWoi@boZ960 zizi;sH-iH)!VKSdBD+*vn`P!~HqCY1z_t*yshX>@jMk<&f0o#=7kb zM0|=R9~n;pQdlbt|3){V2E}OL>f|;Qvn!PbQYwC6B!TRFC+Ui$r<09+L5esE=i?XKEuT<9z z0o~iLfq}u(*|C$t_lIcbU)kZ?e5gk?JJ*OyM&fF?=r5tyhL7~*U`WAC*j9}aEDH5? zUgeWs%Ro)XPnGAzkvby=xVOj_;R;bwbHu2v_jYc3zh33>eZdlluyI6X-J;JYCWzfN zC5+aZnH{S+S)&W1S}|!`@_WL!vhK6v$imBdB8EaAN?*co6?E!A1T$pd`>Ez9KI4Os zFLb&g2Nn??=CDu=)r>Ac<9dyiAS01zKr>la0NWIeWxQVy3iIEBiA|8gjH$wvJ0G_Gv@dL#|Xj7|wOm<>C zPjJ0K;HL5qYcEij!Mjp$FQS^2;ZZ1;1?=x{X7(WSHQLUvy}-z@vfE(kwp3?W0)TfY z;7w(5X|i%WN)715&5^f$Kq&UdTCyN z?7yv#b#NS~p7R(!DwWo`6805_x8to^OrC6CpVt%x?DBOdt>E`Sg@nn&%j$eOEPozi zH6@m^(!-R#O~t(_rvK=cF%3@0yD7DJg0az#Zy&O%gbv2pnCJYGCEoJm0d21^)Ps@P z>4wHzKG$IE`?blhJA^loe(QxZ;HygsGo(7;rhPNAM&~XC7`!Jws6(lYkO&yt5HuR` z?Ak&$Jdcbv0k;pVE@-~R-l`PYOY0%;0O{X9LBV1N|4{mTMKIiG7q<61M{R#34L`6t z-Gj&lT*LcrZ)SIdz>wF~ARdzD|Hecx4^dCf-lZQzK;m1wXIk&R615^P-u*h8h-i(t z5S-~6$lGQGiD6>2kMt_sUPV@9idZAs%TyP`uljMOp#l)gkC<5`q822@rkSeNWQt7)v_V35R3s@C~DX=gg=J$>6dOg1t%WtB5n(_lEC+K`<*rnW+W{GEDP+!$s<2 zlpWMJEwa0L2<-?D=^Qn3n|oQakoc|Ot(Gs{AkQqh5Y(4)?j{I^Vvl>+UQse~Ca3vb zSv=+=4c)i@5_SwmxBPVq`s9coX(Xf2I#uyg>T&trb`(a-4rUIIQ6inCUFJ?2y))nS z*G4Rim5ee0__n<7&e(mFr;_u`vt`Lt8#J@p%)&6)gx0Ds^x|=LkPA~nE0pJg5untz zj+I!2>|Tk7#xb&Mzo)@ieW=z4`QCTxAGl_r;lzu0{>DF>>wglZfYCaEIe-9Th`-CU7YIY~IOqMdPJ}{-Y zNs37BlyMozasKzzboiO7O44O7pVr4glXli3xSu@SVBF=OtSq#jwMb7f=Tw#{O(2e?-dJm z;7ceSZzSPHc^>%6*K|lc4+xA{;>Bwq}cMK0_7f&;8p~ z%~n3E44uGp9tQeJ?ZPZ5qGu9<6nqE;x~g6!ZzI}n|pTl&dLLNUPEu+ z<)SYHww_aN8ImO@oEYhPg}*~IFdYuWr#pSi7rv@=#etgNq!Ky&)G`22`qx-m>aYB2y3K#G8CKrr!8@ANT7P_@R8JK~Dm zfGQ?jZy70NGee$;uk2?0XoZ_WAtQtnq4|AHFV8%;X; zGU&&~xhjg!t+qn+gNTMn(+%5y-#=}}(D)736I+Q@zA9+4VZaHi~eJ6QLvW;Nuh7Yl0! zV@b|4i^?TEHcqLC{6Dq$edrdE=O|bxBDu@}M>*^vvA^(uKRd}z8O&BO?!zq|@!^$(VuQgO_= zQj|IkM*dwOR31vEPQuXlGEA1^kGfdVZ-m7Uoj$6(?h=T(F+%HNE?ZxjFzbbZIWf|u zJAIP+QaN)>%KNe2NHGk^P1?a;S^9|PSU{w16`4fUHZ)M<>^$5QedMh_CgHlU5%8gF zpEO6;Q^$P!;wQ|f?>MCw0KIE_U*T?5&4(AQ^Kl)WFU@cZh5bR74}X(_urxS(lKHBbYeT@IYGKxuyOVpOr&1Y!8ep@IPld_Hqx5#p9R zF+P5#1~g1jehRoL@YW{qHA+2mCYoPKjfy{uKXY>U(_0G?4B2^}m$2P+S?$6JUyaT{ zx&9`vSpISJK-5R#C;@n|KwOjV;M3e_j-(Fhx$}mmF*5o5h=22{-5HD6W^8K&A1P_L z&LH>DDj#T}K)a?^Mx^lzyY(C1m&6G<5VPT!rjOdS0ItYd`SJ6AT|ug=Ls1#GAcV_* zva8z_X9ey(K&v*RJHX@-?XmLZ2>2!#V%HDCt<<8MS`5Es;NV!ZZj*#R%h#Ybha{nk z-c0DTd3}A!>X+8G7#OuTBfU8Ax=DquHR@*I^Duv)5axKm3X!4!M5Rm$_6<2|ta5y- ztXxS4q{_zFS!?hSRtmrV)d#Bj*XG8NoqYMD%537&6nGsyCP2XU7+XbN!2wod-|zx& zKl5uJ+OVmqMuTr0!{9LQ>x?;K5dDtzdzq5@a=-7aZ{3yLvIwV=A?U%7ZNKi{0T7)@ zbw1_9_>ao~~agpnT1$+7keYB2gpN67M<>J?vP_T9fA{ zp_zb=3ZQo<;JDC#aH>JxiV_1$X|CU4KDxZF0~9<2k}-}1aEEV|Btx-M@ZQ_xV9gdB z6XSMa;SwwY6#o0V8VngB824BM06#lZbgZEJOaKke#)FOx8PS_Ie<_&_*tiT?lZU`r z4y5i=S)9BcY-|p+bVvxepfq=H0jxMHAH6~UIra?@0q5@l^HD*P6n7&(3M_a2`gmR_ zSxF>0np`k5bCM7kDu4QZS`GiUL9{%$!krt13MyDMpZQH6%-Kn%3ibkE4e9niUZi5= zgzx`xNMOFn0dDE&DpAgW8{5~Lea)HcWT7zEmpH=)+A1!KEFVsp$3>iA<;BR--S%!@ zQQ*V%*S5pXRTf{rx;_V(pclFjS8=em@R+Y_hlr2`J4|C9RxF(r=Jq5w?F?NM=z?g-*{yKwe+AWp!4u%rR*AnMmk0O z$}FmObmOHmfn&6!Hu(iR z2keXa{tRtL9e)OLC-BBy4OwWNKxf$AUHqs8G$)YiCR>R?f*4cl%LsU?XcRp+=cJeB zBEMbb#EBgQUc&aQj45)Z>W>sZ26V$BWnGk9yEg`(WyIZu=E#4Q!CX>dIJeoDR89&s zJ&rOB;#~U-z#tYuz{x%JSLW#Gm{DD?nJv6j&v2-~{ay72}>zL*QvF=jJpvCr=TY^PR?qPMZ^T z`CP@2~r37{2grLu=c+6J_a-d>A|zRcw!NAh;(4c?tQloxyj%o}IZVqNor1@JA-o~>Z?)e<=H#z52@ z*NX=${H<;aD7K^5!ZpGB#_sp$O$F3PDL^PFDF(7RJw^lA1vQKpmC3f3)c-K!Mgic07U4 zyn`b8`u_VNj{83ymb_=63iN7NG3hz(T9t#&y^J%@dX2_^d_^ zE->U7)S+_}02o%1xIq0n^M!FANR2+R`X zqpl${Cm$L35j*^N--(HHy&h3O8G~ct9uZf&0vyz~e9_R^Zv?znIh031z`5^!NwM!45(5<@_)iQ(3amB@qB=j~ zRHhd%{;&ZJiJF4oFe9(ioKW!3%%W_r>k-*daO#r0lS+fpn7I)eKnlDO1i|woW0QI8 zEP#scr2vKi@V<5YZ&OlWb&k8O#gh7&LK)~H;i^7h*xb%hi0WqJ2YKBa3 zf^EB_r}wF*T=%28XIK5&uH0{W(1iVym+pRf2F)tP%C$%rJ9C|P#&Skm_hTIPk=aQKXL+KF=x7);UKBQE!pY;l~9IOg0+q=Kc?R!Ll+G|3ljUuo&sx zL0WBKHsw%hc+%xpRPkLg_@C=Sd)KqkQoC}$*Y^#-Kf7s1I|fM;9002pHTv3ajw<^< zxNJW))!OQLnxooXtxD$a*b4_@G-7dr9+4XeF7j2J_=bK#Z0WE6zscd4$ zvHb>-M-4B*#+HuYG%$0DB`3UL^&m&jC|z|5n2%)q=D%=uD)Sqt!uMy3^s=s4sIWl+4c>0z^e%<_%yDA` z4m>I5W|HJa-hv~Vsh1Mb{6K#cOcojuq}8F5bfHRwSM0-IJlA#dws!vuh=Zvs3S zX``3)`eng2JNr%#1+Fq@YBD#sKe*1T_v5L@`kQ1Ge!# zx9vDSRAb*V-;N0~;Kq%hBUPreSI~uM!b8t@LEqE=dT=f1dLa`6wpNHL$cIL^6!N^54oKSO&$pE|033EU9Iq1zh=oQ}fxT%jJ1u zrO)WV3*?>nfGJ1EN50;a>{(4bAf9DW6d50;*?fo=V-p5XOpOy8?b_($yfBqm#XWol znxoEYW9{txS0%!dJ{R0m9_927sZtI9Z+gOy#7lu*tQ4mq{T2c^&(-dL0N~Ky{qOaS zI%W+u3lYcwYb`Rt-db4z0EJPB-aXEI>$~WLyS%{#Dj?R#{z(AG3|hSDCnBmnEw)ONTd`|9Lfg8y5ejOtN|&6<4ZztqXsEAq!~F zSwwr4xx(vxN^2XadNO<84Dn2Nu!p96n2XpD#?;Q_^>fr$jEFUHcO=|??ofNF{^kvL z?)(tN08JAMdn z+p@|9Ud>JZ>vOPhmO?2-H=1*e%5b(1G7r*gx3a{DxB7oz$PD}^VM)bwOWt&|9lc_v zs~_XQL=C1>4gX{}NsOSPiTpdGvc~`cknnrlwjfn!9!a_dM(1h5Y0jlpl}9h&tSTLc zs!nrb>2QqNW{H?!HoPp*Hu(nkLpCS!aXE2{A)JqVi#o2Vf z#dDYRB>{84H=s^%)S#DCMVNC!^z|-*t;i8E!`T^VPjVcY3lwzUL5yku-e9*fr-=xV zys)%$I~|O{Fn;boV^ZatWgvKAoZgO*9@lQ`_R2^9OXu;0v2!ab&_Ske7^**1c=&fQ zTH3=_y6WW(DS|EN#hP}L^2J8+{N?YbI>m0D2TDUV_M^wN9P#H4bksVSCP}=t*?dLT zp9zQup3j2~f1`Uv9mik~6$1iVooo^d$Aoz;ePt6Xjr$T9Lq3PEQFb|dCGQ!w@9cd^~7#gtk z;$bXg=<}g980R@8PuSHl`v+G^(Vws3*-mnEy5`q=-BfbmM|ws<87mNQWmaq0STt?y zkZc&dzQ0)%=CKL|O;$E8C0u|d4PZeIM!+WB_ExiEAa`ZM3Z8M0RIb|$_inD$&b8rt zk~Z8Vx3>a79Wd@D2xhbUn_BqXuBxMXTEBv`y#WnW@3&F-#@Lp{-^ad#&ZR)^%aXf5&B;O3IV^O^|^S4zyrSw+@qT4L><`UP&3r z^bWB2&EDy?b#qu#iwXO=LldJg)9UT1YD&7-a}3pD)Ga06XI`UtVtLl%wiFTwTH3?- z%njRnug4BMwv-drcL_Qy7%l(HBj5Wn88!1&7}dzYR%35O)IQV|tniy+6@<ykw{>M~u3v|?{4asjWh2%6w#Kpvc)~(yi!n*FFoIEkd4l~^pa892T zIA63rG5KWj$XgjzWHndfaE~X;U0B??v5y7^sy`_-qAGveJz(SMG(E`}tf*|pS-WCj zf3k*Gesuk|qDJEOaE1Jc*eJzb}+{x!213% z&X->}!E0WD;s{OGxkCw>pdoSfL|#`v#ks$#?O*1-XCwhut-;Iw(R(EaPr zBFboS(?w)&O@L6zGkemVy*481cb@TjxDj6l1L)F4pzyoB8fT^6hM`g?TYKB9(4gw| z@Aj8k|AAy$VwVPoj04=Rif1nwBsOy~>crfe=1a2%8HL)yCBlIfKW>biF8O}DwrHz;|Js$&r~I$fU_&e(xk=S z4_Sgo9BF!gTMuO5E}t1NZ3PSZ$yzrf;C7Lub$Y%EtY2&DkWtl&2Q( zXnOL;pQNAUN9K9dyMAiibRG=^kZ7p$tpJ~!Y47pQ%>)mHs(!bCO3j3rVW4*tEJ1&P z?-u~^-e;r%0Rx0XV!fvA&2y$Vjz(*QR@_X=IvNJFlm4%zuZ)Z0{obBk7FbFaq;m-+ zBqgOgM5RNNlrPiWpNk~bTQqm0@&+vQxZ+OFeW@gXabLQOF zxvuMUo9h77nlV*qBWtkwUG`ur?rK$e4W7O<<7EChP3KK+j!Nc95LssA+%**k%i`}= z@kG21P+RT>!t6{A9D?$47l6jV=Ks>&t8k)C^kWDEZ8NKS$`x1{<4|-lMF0q(?eRmK z&(19G(4ni)*8ED%*5n-OV1Br4qyO1LBQkIPuRp&xgP?G2u%--oK=trng>l$y{gi?lyn;O+xYIVhrfn9 zG~hj75`V!5$B0*ENpP?8LT6`Vd{vv7hT12GPh&u`bzL_25y|vjrlmst&OLqb4G+^^GB<|q^IQ?0+zaYQ#s3==GG{ryzRi)%rL#9rl>0OOwOtG%i2C{%biMl}10m zNGzrUYLh02*7m!NE5TMz$(Ma5?Wix;guYYUVo4;&R>+O<_fmy~&iGG0C#KFZF*?A_ zhEhHqoW^}{bC*;{+l4c z2I)w$kLg7-4eam@a`&7n@|GIz%z1=rQ}9q65JPatSFp}}VclI5Yj%uuz)<>& zcUQ#01~^DbxL>|U{&Fl;)kz)6!~avne4T@rb9YqgT`Kdd;Iggeq{)X>aJ4=?bxHfD zdu?AGE|2P)LoIZvii6yjt%$LZ)Z5*EIJu;W;vh#S84{r1HsY$TZcx-;7?t4~Gwk4W z9o$638tvFL8NU7xaRlL%JH3{YAG^nV6_8EL16gSiKA=Yg#^+d^xHo-=6zt-5tKxpW z>YX@QH@(`6P1V~18EPJkRM!KmYjbP1u7_FZ3zKOT5?7z5C72&yIN)-JL4~4X24(Ga z*au`fqEs>bgc~e-eRBI69Swdx)9XKXbnXEAzO_=8`@ok+p|q`gf>Xy}d+J8p)ncPU?YACnl^TE`#h24=h}IY? zXPi+q9P@9A(=Cci2vSDoC~LuzMbvuTq0E@S^0o;M9PSz1+dUxh#|<25!kRktNx|$9 zk0@pJpk|h_XPJrl1e8LIQ;)Q5poA@u;L=y+WfXB{E63=<`c$TKK)3T$m$VB9<~h^+ z)swDF@GESwAEgN&?zzz2%2^E~x=wgR?Dc{n)M+ zw8nMyeK{BmUs-&ggqv9^gMzq@`@=Xw3-qK?qawdpO0P!kBY(Md9fybMADF>?xyiXa|?oYjcJLP&52oNqkh8n%FcP@{aN( z2j=jO6SJdz<=GLK-zBW}@T(}ZtIo7oQ|{fnrtnI(RYlJQwb}OpF)O23L0(`=8{%EH zCVn0EwghH zQ8OgQpO|~CZ0;O(_jMLC;zDT&@bj)*m)gNg);3R8H zQ_#P?ql~3mZRj*$sj4$uTjd1yk@R`%mY=USm}PU<)6UVwn^Bu@3=IoTxUFGo{er;0 zr7bh?-0By*FjyYpZ2pO-6E?$L}IfqBSl@7PXg`?sGO`G8`)y)PJ$?ATAfDF3?| zCFNrmn~tRBG(EC8SUmZPpXujDd0sLvwsV@W@jqO=n>ufPQheO1sSM8@Q)o3X|To z(1Gu{;xYj zs5u3XSSspE5(-mXeg38bn zr93En>>R|;36oH8B#7~ZpjBplf^Q|8GGawH)uo1z&3^iVcMJpg|3QD6>smMs>!&Uq6Mrq z@gZ;LJE~#=C|dmuw6*Wf?kImMu*^q{Wc)`wGi;z`mo{>Hi+Fy9fT|0eTpPL=tk3}_ zi7F}6TcaBZllBa&(H}YFfl?kSdM`(zEs7kH#LQUgnL}X{gAM^c$Ku3S1Hd;H+ zi;A!0K{jSaTI(A%xAGhPPS(v$#}9VM-MGFFrO)05fSH`hiC$wFb@wh)Yv9;4VTcRf zS+Yo9C~@EWG4WgJd4G#8ZpreMu1e~Tf)YU@MU@&wu}r(cN`Z{+n-?@rPko|1788NE zT#w)DyIy59df7V#CZAL0>cF|%%~*NwaLs^{@^x&qSDXZH($0uX2MRCQ6W0gP#l9(o z|FP7E`f;cB?7E>t0=tPQ;7NyW$TB;uB4^p$WV<5~0p=vzcpoDeLSxyE{%}_nA48KF zpntox`*Z=}CYwr}tTZfbxz5bDcd$XXfdU;xzNV_C5SY0lIyP5C-;i8hio*Z!Qgjsd zo~sJXL1D6@z0uZ_t=`QKX?_7-(%jAhdj;|SeO7iUC+7MBy-~LI{AdIl@g4<8+Wkq@ zM=sDL@0+!b9~@rPYW4^;rvJ^9psn&hk}{nNl`HOlfxsFdN--cV?*EjcH@Z?)51G+` z4=Q^qCb0jyudm@{Pyj}b{*xd)CNlkixH5sYIQA)?+&Nh8XT9_aGcmmXsGXMe06+Qt z!F3*K0w=4AXYQYJZk6Egr>Dzl&-29#PmV~)Q`#fXt%$ZyP9OZbm@oVGCGgkQMlRQy zvF5PgR|$&OpbG(acchu;lzWSB$>%*gyH{UlA+bHoDJQ+Ls?=v0QQi#X@sdLV>b=on zzfNf<)?=GJLNgo-Vy<|6;7%dra7S7t?)7_2Hh84f)1|bKBv3{j=82 z#W|!>@}+X%O${SNvzIy;zvOaM7IJW2bOg$`g`kjYmG*?#LT zlXeM<$Q``6t>>A7{W#Lt_1WceAMyoD+MRMu^=&c-s#8ud%1lc%Lvo4?}LIsta z(XIai5_Byga_x#r)QU)CCbki!;S&PU{^wT-^Ceh;(bisM`; zd3QJ>`Nc5kJ=ZkMT=71o71vk;*8gzmC<$L#=Qr`a`LiAgbyBxtE2e%dbaOW3xsqHf z{{D%fj5l}Lwn?##-}JWfpD54cAxZPudkx!zdlm)>!F{N>vt2nG{GD$ln&Xj9Lp6=U zx5$-3e%OTEXT-f#fTeiPrbvbmf*;N?-NF58cwf`f0vBOQC$cvV zcQVWWraIFklTZxz?OHA9@{0OBQL(?wOCI|#fI$2I2_l#PkZoGB>-itY+@C99 z0d%zkLRp&hMzpF>a2sgHYJ*)DlB-z{8N z-4CgAelJg2GS$aW<;u@@$54APnV3bm7ExaAjVA+2CTYE*-l|Wk+N-CUax_gMV`JTL z%+r1IAq-dC0P_M!uYF*xlb`*3=qHHd80L5^NqNcl8ftVhx9GkcGxwo>b0HtK+Yql> z=mo9eSu5kYC5-|T%*Bg12gaNAz*XUQvnu1eR(RsTu4V50$DLOwoK)g!3>^MwMl6X$ z*ZHOeo#W6_$41NcNE_U@+znTh*u7d6o}zMLT>+JF<%18rbhe!1XcreqqX-uU z$YwBb^suGOFRWRaMsE(MTe5Mtl*PRf21w$=%{zLrV9cuIw%H$;zAl~M;#K?41sx;x zV{oU9{s`tUBS3;k9zt>O0SL$~&E+o2m94fY`8{p)o~6w{@_gf7t{ViKw)j$UX|WC+ zQ&{zaR~~|HIc;Y|=c47mS(h@8*de;zn)JV0V6u9Y*w#*#aH;eUjCzMSAM9QKI40_C zNA|1Mib{-{`#Ht|SUw*y3~6?!ik;w)4q6yHH833ZFJ&%3Gv&qwD+=)RZjIF9n`Pi#Qb~LUO+5h?xa4H+&7njti~IazX0J}CHopF6_tSbie(7e+ zHx0jjetZ(5)%)17!n@?oA=ED6&l|Xv8~AZ$Lz$h4%QRQ+hqMN-4@FLHhAd{cg1mTF zmC&_UaM)k?o%io8ZW^lP)-|8t1i()nul-9r=`fc78=%RmO>no{%o{J|)!(&N8W&yp z=c-oN7^0ydgJYfE9Be)dV?i;Ngo_-9Nk8#bv`v#*6R2LR1XxZ#iaaZ7^!6is$mO+f znHIlo9X+w*@E!A%Jr?sUPWA!Jb4gKiK&B7X;Dx)<8r5$p$g%-$dRY$i9Pj;aRl4SQfa`F*XuCN8CM@!KK{CYvnb$Qc#h3{ z#h~#4=6-}>Aw9$eM5ACdOHeL~io(e|>@4oeyw0$BlJIl6l2(GYfrj2IEMnbmEClO?REior z(!)L`mWC#9-U?qoRvTsFP!8k0TL4K=bNoqsS=Lqynmtj6&;09>j<~sa_O9|Yrx1w$ z3?4{1}vg7e}YzsEmkq8xqzBiu zi<5_TUuO5IkJ}W_<7bn_mtyjbu<>hHW#6zF3!DiMAq;)-k~T44F-^SaRj_K#JORL2uZOqXt6CpqM0yv4{lo0xti{i4inbQYmq2qcaNKPcpPjl;9SGA8B zPo({qNIObD2aPX~@u?fcC|QiOKo9Wd)$>}}8XJGn#yvJ7JpQpA#s`%ZRlHsC*g2(V z!O}QR(odrydfKsrw{)qGGXIh;e$wud?~bCMkIIjSqfHs)1%xoV!ui|iym4?Nom?c~ zPcT_jFS~Z#yhZrV_uPKA2XGNt+6G#BtPZwIiD^21-rUb__Hf-=7Xt7yj%c_4Ag8^y zUSsGvY$tq~-Tif-8Vb8_;*!=y%3N^FG100ftLLDADg`u{Jz|iMNMrm9;i-+^e40q! zop#hJajN_O9TmF%ne>fKU9I(K%$$(73v~gKn6KVsw|B@)CYam+ALfv~N_EPCN4hjY zqEhhO6tckp;<=29hSb;g6J^YYTD^5Eh4 z$N9rs|}h%|x5?fmAd|oh~4gU5cx^Q_utw`bum-4Hy9ZT%1 zR2z~<6?eXvQ4_Z`Ii&I*zfP-fHTdzU?QA0KjnQ}N)}wK??obJi)n!8!y0b#ZwVxO< z+fNqw+c5_{R`V0I9EkfIWp5MY`Hl9l5?)2bZ6W+ze}hzUGOxTL@*ig7oAB@BrxQ1f zt;QCe^TRd^LTZd_ObLUe)0e+~~9x-&`l9Su;UM(?CXER)K&ZA16 z>6nLdx&hUQ7i!vcetKo4OGVI*zHRDUcdj$}Le;m6F~Ud1gTqHO2Jg3gaJdV;iF&$$ zvJ&h2Pr04vFUBzA)!w+0`T%=^96_mmn%~s^)MZ&&mUvodEV%LQ#rZ9k2Z=wQpJ~K1 z>g`Ynk`2cdZ$%69GVnmyC1hwAiREh<<9X3-QpI0zuUB8rW?Y}1Z7yHWuC@`Eh^GJjfoD3IG%G;f8V# z{_D@XJQC-$73E7sbZ8Gg$d6!tWH23|S_R;qy-h-)q1shls-uE>=B7KUOJ3xl&MVT) zZ$0MQ5jiR>aCwKoeK7y+C&umoh5s=xc@uH9-MiiLm`p$PoSlZ7G}2`2-3McIPBtS< zVRb|Ia3REz1>(zuldd>x8ByMnqj~`A5hET9?c6!eest+!V+LIo|9vbVnaNV}unfR0 zB}8w}w)i{*8^y{89ef^S(p}pV{l$f;fA?(13PR3ro^z_o(T9aZ?37vTTYE*foCvnP zxCH1bj@57?Q3CNI`Yn6pv!NG)_J(N>u2uvDlWk>FeSRHxC5v$RK9caY5c*Z=!o)t| ze}u0%WUO`_sax~9N;`J`%Pi#ZM&3G@^c;MNTx7*ppGkZZT-n1=Y@+Z!M3vgc6nJ(l zdD=m!|5&4TYse>Nm9t6S^0DKe8m0bHaNkJ&2V9y?4pUQjL0~2k{hyA*AJ1yEixJD` zaDsasdl$aZ?6A>)jf;Hk)X~8E>7;WP`A(jQMOhQ*8yo$a9`4tCr$7x>{GMM|y|`y# zxl-n#=WI7aTd_j~OT#5`vp2SK)JavyVM%t$cj^1fclXHJsX!d{=5OqgwCa?=o^ zMpw)bONd$doqX06bCMhGPH9E}gv*ESvl+NN73DniTv~i`7ze{F829m9XS-u&B%3S-YsF z+c^TZEu^=I`eF zvyFZ1_92b#18W45t6}98R9P29S}g882-`Pu2h?nb&^t|CoH0T4RShwQ&m zQHuh+&xwMcF7{v3CNl~zktM>_j27_ZRV9<4*cCIbhs#T#&n+$9N~TptJITV-KZ+U! zyka}6#_NV2GR{Yw|K7-Q8ML5gmzLNRIOH7t7I%^+sAlv;hgYHS7igZybORo-k%;N7 zgQxX@Z>v>pPy5TUU6Y4YN{Ffk)papaiXHCZgksX{in)DD7B}519~zjIT4E%E2%+W- zlPIzGkG~bEgoxR_mnG3@c<1ro>MPM0+e4HkGk!MF{h=mylPdm;&)h|5N5ri;Rfe~u zKUyA1*JcA9w`Nt{92W*)=mfP4uD&Z-nbN;bO@UJ~2de|;WOemvA?W)fr?Tq=(wSq+ zk~p)gYAHTBdQE%qXl zuKn-xb@Oi)lH{EDO~j{jtgl#SP6suYuZO3qKqD1((k(yP(0QCw^kA;j*KZ}>=YgGq z9m!-Rdlf)2+$hOunwyeXR>45Vc|iqw8HT#h)1leV`{ba&j-qQ^bB~M`Os&+!*5nk* zAb6XRx%8g8do*3DDk8Ef$fCTvg9t)8sG{Tx5%r?7P1zh|Zo6 zHKrO8$tM%kf~+{NW5J!Eo)C>RfdME<0;s8O=fTnHS^I>=Gd*#ECwn=r2H9VKkkw@A zY5XVmw~t(#mlExbE-vO%pZXTRhqd#4P~20Wl((n*jvKtb8iNMXoEPb8@Sx7YRI>kJ z={Yx7qn2@LtWi<`dB@nqkm{*NdTvpgZ%b^k`z?NMQZ-$4_GDJ2Vq)H2#kErKwC=*+cd_{nt51@|&p>Lbrh!!a}t5*^Y7sIEZjd~ET{Vj}E*rnYg z9s(kxwJP_Y6bJmvqKBj>9CFZ3Qm&z+Z_!r@HgpFq1*=YoRDkgvx(gGL;-<%!P`fu} z9B_kPjF|yhm4qA?{0&x!5l?n0(lzvfFlH--?1~6rJ!&>>pan5O@}}?D=_G4HieJNB z^_t;u|Clq-9O?GksSKYS3HnUA!Q$1cCmY1$csVP6%~G-s^-`80R+?%J!MjvO2rE(P zBO}u(&*gbF1Mn0ra25}g{ne*-w6>4uP~kgvZ)A*30GKE=C4fk*pOCqFdMnVK9G3Yy zI{BrsVsFTUg_Nl&es*Hja7i= z2&Z7*76yam#|y>8&qqaU?-oeW^g5=@-pp&TbiucQLqXEDB(S8M$3PhkeKY-J^V1RV zZSFCL$(xeMT{DJ7EOuVB1AvH#h{sEr)gdE zn4h$-Vup#SSHwXgCWy7BXonS_u!Tc0?Ly;HoSLWP&F6xx7F~zKnjjm&CcmsY7GU;u}H9bIWFVN!NA- zx4!wXyXr6?erF;5RbCpn(4ifyG$Qpw74JOcK3@+jO1x)Zj!KXqMZAZ1kh2Rx;_Jca z5Hvq&j4YIL@l*=xcLBxE^hP0iI*>pvntT(hbrjO?B{up0b+|*QOX&F90qAB~rVq0(-{s(2;)ELi*$F}bnXgft{O4m?)+EK< z^;@C$6w=7A_Y~HEh&kzM{zEBeh<>Jo!1^4ob~4b5jG$Q?Q>ZM0xIHPC}4Ez&$v-b=7c3 z4`WlKGRWhHBfP#!+63+c!f-_Vm ztpdvD6N?~OTFdr<$!DA5h3zKa8%VeU8w)mXp(;Y%O?L9~MwF5Pm)FIU%mS4mZsnw0 zky@=GbF|MjaqPZd>ao4~k8gxhBCCX$`;Ev%v$NhqeBmZkM%@e=F48PBE4mQ1h-5(E z8ah~ARKsPruwo|MaODWH9I3HCq<;hdwI;9F07E}W8i7DPjFhNO03q7p| zL|K@h7LlMIw)YY6^-bdiiQQ)(otp_^xatjUHA#|7r*AM043yFT#SgxONDwa4IE?*m zT}8D@>?btbLeN(r9a&4-zA8-Qc)#W1$|q~mz_+b;;Sdgodkk87+;hs_ z1A;v_=JHf}TvlT&`E6a;+yQ>A$;Aj;w>eHmyaChqMqZlATfWw`bwlvg!9>)Sm0LLB z`w=+o0HF=E`x@Ngsr@aOLie)?Kbs0t3$4$M#xO~Op#S|`MQJal6O>7yjJziz! zLSc$F!-aqZv0>oA%8}tO*~)Bq4Rb29|J}ZEOFNl4=3V<)%4q(_&FbAJZg8R^tbnf* z5m9EVnuL2Hu>uB`o}NZNct3k@X1vCt(pKi@hmy)E@}Q%IU!WJ3m~uM0vU(+_z*ZHU z?qQ=vq7+0~tO*V(P(#EzNbo@aS~22YPTHLyzr~rQULR*%8I943&ds7_tmrDT1>b?h z%V`|62|xm5!tP-iX*#`{J2FK>lm+VYp;)Yi49B)N`0P;5!MC+^LbOg=-@JWpaEMc) zn<15>^(v)BMxEPXzkjxm|9YoEx@XkrWRr*U>UE80m&?2Cd_$juSKF%lRLfa*#HooL zz6q7p=!@t1)5Q;b>ZfGdQjWF5=eSXOR5+7& zDdobe{rA~U0vx(&6}0#Z@Jt_?J(XF1@M3?uj`d)zGar91@$ z&7*T>Do|6`?Z+a?&e$XiDjY;!z5w7K`DUKgD(Z{u9!j=gqjR1Z0qKr(=veU=RAKLF zht%QnI#)CnCaX;As+i&IT86#_wVeo|L{Rh7xAkV&CEBL6ZXfrzE)$Q7g`%^sLiMOd zFU}%$;bvqk&-kv9Az|sl_D;pp!ps_UUg*X_brtZiPvX2wYp~J{HbK%6SS6#{ zTV#sNC4UPp(aE6a(uUgw;K##9vCF1}!=UW?cb%fCa z-^G#McS{|QacfC?gmQxtMlKV;)nIO6z!p3)JumD-u$F!B(5gmKvHt+VfI7DBz3j%C zdYx76F&Erh10-Qw@mzh-+x369c(Vba=7g+0x|htdCP*DD(`c245CZ5SX0|b2rw6SZ zV64m3snuX263|a{Z|+n73*9>ExU^X}S@?tbFZVJUI;63s(2gorG?FD^8a)s~SGg4T z(6sn7i~%@224^PmC}@*9vqI~ky~Vx=PSVgjCV_w=O+))Rh+-0ujo6oSD&Ujqu`#o6 z;A9=pQ)#@)iDv1btGG{6ermgn1#RT@KZaGu?Nu~hKA1fLGWQgXnhsAlNx(>PAt73x z@(GxXkCiRaP__Kltu4OB`S0Jn>KoX=4~+>B>jDn#0@qTks|XnwQr}XNlEnmw0m~sq;_*@Qy_XVd zbKGY{q!EzayT4Dvv`+`By&MI|ggxmHx8D{^8Cm*j&Oebr3&Mr33|(N4DllYR6n7v- zBi}+&qMlKOUTO@#(7|LH5S8`Z^_9CHV>^Q;7-OH<*exWG;E1O?KV>_+`}Zw~STN`o z5N_(IEU#VGsmmKG(?U2Y!k?IBXh49N`G-!s0XU}1NpE`na0ns41W1rMyFymPrE;l= z1>Lq;AHe0GAdQWmz$noVGdg{o7r8F~qo+&ZYc`!G@A%ms*$3^Q__OR-cq>iF&@<=hcRs@V+iq(VmqE{1 zDlXMyk$+M=9u*j?Ne`;dRH&yrnAvg8$5U6ql?)dWW%!_t5`8bXX2+Ykpf7(Zwm4iH v?oQ(oKb{$!Mg=Q{P&G`_mC6yZ014p2nD5@iOMN*z5YX4Yr}bXL{>lFXE@43sl&)4hTd+s^syql}Nh>*Mx0Ejp` z*t!Ftd=do;fuFt2SFht|awqL}pWJ@z@X1pLLVdyN&@mrhx?|9RBfjpw2M(P+{@!;J zzY)XD*~9MUqsMJI+j&h77Z?AQ2X5)&d~50I?)xGw%$QFY#E4 zxogxjHJJ{N>i3)~GBXfp>VCLub!M%2KgrtU*p0-k92c2_)DA74DiNVL&#mbDfCza!YSUIWrlR(3-=`Ie<1CbNgL9aVKM%8kO)h$4WrE@Lw1M00}a1mbq?PfVmU$g*ZFmG|BndP+jA3eD< zW=3txOd_8&(Qf(EXWW~~qg{ZP2Pmi9k)g85zZ(%E=l)0D3!(Gp#&2pZ>pLulTd~nU z$J*mF$Cqw_J$Mzz-j}a&>rBc_E)1oxrDoQ>K*ak0{)#6e5tRhm2CK!= zi527!B14{g>|y+wk-oQrblBb$t3#W74l7*&$;N92lZdt-H0*@H$9?EM3MZh9RBJhSVr+J13d<4#zI zHXpmCE`>zgUp{SMgtW!^x8`nrD62?^o=S^>Z0fzFE7*;#mtWLt?xcD#my^_)ogSkh zndQH>Mm~^SLlzK#-;bJ%Ba0sFsC5nX6RsMvNY{9!z+aE&klhZN!{aC2o}uj)w-zsk zFu+8a(=Ji6Z8c9f)9DDHdX<2wV$NitBCgoO?I&oc-nOOZd+lmZx;>8BX<{Ux0xiN& zQ>IO0>ON(@ehaocM_Mvr$usFkKJ;cW-3Np8gqB4Fk zF3K$Tz7tHWp|FV*riQ?zi;~pO&2Ua9-LZMCxeDaHz;|EI80gzc#zLB1i8WKd*|y7W zM2791;d2GUtt&6%vzi095`SN_k2?VD%=(I*;YV-2EEPt6xBVof-KDUvDB^DvIv(Y1 zBuHXAl4`tGlE?^lFZAEFBi>&3L&ZtWaKt&qq6k%cg*Ys zLSsSmvwaG@RUtvR=cc8|efaqY#NYv2jh(_d=3v{FZ&gNO?%kotaXa_=dtu#sVs?F! z3T&mo%3x-@G;Sz@+S+4xOtow^D;4xkLF>O8%go?>w^_*k)NK#VYS$Q%#PLf87~P;D z>~F&PHtQgT>uJB3*y!%VCF?_66eVl>I?aQ>LP1K{IoDe5q#O6Zmt55|@a|FTvvs&h z0Pp+fgccCAOQB6O;|-BchZU&J4EN0gJhQ0#1# zuFtEXl(1@F@q2`dqU~;)Wp)X0D%ohiGk%}j|HGc#vcgyeZIt~m-J!PlR25nujMb#$ z5y@J#?>Dk9c5?S*RfUFbM^>fG$`;qZDHKuiIV{PrfpemQrX<8-F1yAGVQzo>YB%fB zpUZwTxrFk#Gp2*GFE&3KS0>4q2n*rw3VA|)y&dYf)S}sUJM5&T-0s{}j@`bRUpuNR z6X72sJ}6&0F(xk>;?^iXH0d;lo*^V%;ASXS~mMZz2HlQHew(A_b zq`C}J0~$Zy=#z8QAZ-ut;WK;Z7FFtNtEl+W_~-0oJP$g)5b|F1 z`UfaDl6*W@@0L=l{n?7?Il^DJJ*%_hvxx-QAPr35995DH1XY*QbvN*TqN_j45JiPn zwV!4(c^+NobWg4-pkynB9bD`S?M$T5eCny%4{e8xwKs4qD z4fMkOcW}s7Ge;ow&#c?m8CCjvySUa>a8-SR7_VR_EBht~D^lpxGNP!K2p8T|a=V!o zGu*A>Tsj!+5o5`EX?4YNR$$l&7|W8R_1Ydk$m46QNsX%SJjl3p`RfF{^T)f;kiVK^ z>glCNzd-&Mf@T~wWoK2~mP#wB6(7h*#p%=vNvzBYJ9HiKkT~P==bE$y9`}~*HZIJZ zJwi+sK_4yCPN65mmmjQiH3C`3YE_$<_3SpQ&|HtvdRP&oWsrE7AXBxG9U;p3uY6mdzaE9-gF6P*36 z{<=fqdGvJP6XfUc#LbU+LU9dZ!`yjU!vbeXrS)yZhc*5O-E3gYORep6vQ(vm3ggk= z>Sk8fn*FxcDAxRD^L|zB@rBGEME&nuu&7F#M^BC!fB3oo0#S`#CNs=hYcP*z3xq{) zqV4uSb9P1oPSw~MOwez6?kf+$2lK267=G8R^wl~>Y-%zmX< zcOTkl**s6kP=hB#+&Km@0@;}Z?S(c;JUVA!&#~nfey32-|CNj!Y`~bBaQsgGmdd?|-Nt{!_B+~LG3sJ6^V za17|LkPg}OXmuO~kB6`%3<(zd>%Hjs#}&uyQd-Lu9K_L0TNDOSDWcSeUzLv+?!WE* z+vVrNR>EDkCU=VOHjTs+UapYZ2oi|%H~vi}k3ZPr0-{w&X1Q8{6<}!RW^05zR7xV6>d)G&-66XhJTLdGr>?{a{rznHlY3N}rIr%9x&K ztJ|lMY#b9=&dV4e%&1@%n4;JK)c73x_%i6i`zCjKyq)3dSpsq_ofGE zQ989IJWV9oNwr4ffkMB}+%c){m9LGa__3mU;iC`D_uzD&IniKKU!j>XGunNe zPM6n_OslNTyr=L>V(InR!IY4h*r9*qvmr+DJl;9JjEJDROHUhlip3X|IEzo#AWgE~ z_;7TJo4nTQn~(D{xw*;^x%B1mc#SB8Dkectpo(--jquMsWyr^6D`2#;e0AADGh6Ms z5@Z8XR-%!ASk}qy1t*sIkK&j9iB>p^E>N;~(Q-VLN&>Sb&vWQu^u~&4doggJ1I}%D zsEkJ4Aafkdfx}nZrH!N7Vs_#dW~83|zF#i)2WwUz9z`%&eUJtB&wj;T#G1F&i_aF1 zemv$`9&H0I)i1Iumy27J9v;!cat}kexA#$qxn|DiNLBDT0dG$pBcL;THZYxqv3BmY zv=wk@7HChZzZ=YL6&$*vin|N5kBxY5_JYYS0y`7I{VQ_6QfwQv(byrRQ_hE-E&HMK za}R?5$%P+FafNP4xQ{1;BNcK47!QjH;p^SM6Gxqao;D`Us{E3fE`%noMQ=jq@EY8w zJj4VtOfGtGi~9@dr=|#iBy`~AWa!?;++><~Lx(hd5qV;Ho(+YuVSBJN+?ma><9q`4 zv5ubFNX5!cltb#gh=qd$Gh3O!_Q5fF|QipkK)R0ln|S7>#*Xg>?_M(4$}oB z;ZcK-uApp;!u*|uhFG2}Z|S+u10LI-y=EkB!?6}qzx)SbWS~6cio_9Le$6xbyr^kJ zqy|HH(31Dh=C0p&3dns_^R&!WLpLRpAO7Car;L*~m1QPy8`7RvKco#Ra#k*y=^lXg z5UI`s*td(SI%5q8R2Z(*bSpWPCR|0PJBB`PZ<0aY9wQvqgEh)-FSvF`{qw&n!H&Ng zgChZ5@RR9;zj@8tD*qKqeb})=5cBU*i`}2L^HLE$D6$38Rw`$)O?!3bS5Vnjk|b5w zxa1u?H@|#7Rg@#4)1I|Q<-9o+Os~%sg;AJf_Dt%2|3%Gd0@hXS-CJLK@~Dj&q$9uzM za-tr>?mb+*_m+f&1RP!3H8!FjOmQnP;Bb>f*VJqZ<(~IEvq`@9&@%ym!BP_Pk#|Ek zBLv(=STxton?JPEE7xA_I9ai$@uO?luBT_sKI14Pxtdn&ljxnAQ{zM<$<%-Lf7yXV zrT|9B5O(-!Ba+}H^e384$p|{Kg0r2-Si{#QKTokFN`#4Uv@v!rFfd0)1XDdsdXGUe z*YZ50wz5KS{&d!t!;M^Z9dZ21&Ax}%SYpV85O%Pl$4YTz+vD=YD&gBPVq{?Qwr^~d zZ!XbgwN5?G3hYv)a0RLWIOHX#gjcJX>xtMlF*4irPM7Di>QB|BQ;)Aq{SwT&!1AtN z3BFL!%rn(zSc^Kuz0`|C=m|r-?*id_7XGvm@D#buH3t2CYX?NJgC{CeJmeU?&2$EM zgsH4&E|2|uo~t_`f;S^QGv4e&9%C9m(T0#BN0xWmD7W#0~O=TTjy| zdOI#{3$y=?e%Luqo!P_VEie65RKm-48<%Tv(&o^6%kyc&!^br0ozvJ6>8lmq<$TqV z{4|dNaDt4zFyqCVHmUyjePx)uxzfogze+zYs zvUq=VgowmONiBND`W<9?FL#?_HU+yo1ut34X~e?f8!X2G->p)EkF1O2j;kWm0$4ek z)nT#!Q+&@#%7lm+G@AeTvoMh>jVkp!!9EqeHN3}HpFCt!>q)XSx1WhCbafl3orIE> zL7sYecM_NwKxdqozsG3OK~u)cBLuY{pBorH?1D^z5G_KW^sKi(y2W8h514Yh9c_G+ zT-eSRlOBt^?faYQ_1Ri?rN-!K<9XNT%a$S+`zUP3dm)T9Xo*lGiaQ=K+faBlD6eP? z`Sa8&$(oT*#@cz-+6rZkl11z!V0W((%4xc)M?>-pL==CPZwpH(aguiMM-^L(Nt}TP zt>Pu2qtxs+rbFe%>|rYVmElP{%)qU&Un%PXovktR50P`EScUWUxV@iOo{u02(c;u) zgmrH|;Y;DPi=t-8wf;tjDEfrULPC1WF0d!dJ*DR%6syX@O~1;u{vr538dT_l1FvaN z=gMh$zsN%xGpZ&v)>p)dX``i^+$)X{WRh&l3iHD)^Ur5}rt& z`$dI7q7mZ^w)#|>T{OTg_8UQbdg--HFkSffAva|;H@xUAbyE0u*i>O(*JNS(@O_LCyiQ9Lk8L8n1QcHDtPg2|A#F3Db%aEoOJifQb`*CIaDYK0mx zyx9`)S@e7U2x%scZUu@$3EgU|s!t&ITjKift^ifkLxIeoa@=HF%hKl46YIGqP#erB zJc-_CiBV9<6E`U^$H%|>3J^FPsGp1d*TtrPKRbLcvzTwXW!F*yPp;$~p%54&aKW|4 zsv?4t^%fjl={B%06<;kdd0XVwbSRJv&$=`4w~%}tUn;E#fkxT5Ed$71go>vTv^)-b zRw$Euq%Y^NB@jI3!*0vH5n8Z`irty+RwBwf@Z1%5s{+&TC>Yfn7whkT0`oUpV$Na41cq349R5j%N|k_43U!CT-i>mwf^d*dFICxg zus!h%aIy#+Md>N~oxuvC0^TnB!n3e$Qpg&Eqk^4I2|pZ@fwsOtJOmied$NL~v2#`Z zp_9n9z>umQ5(z55&eRlXl?alH)?U!4Dhge!5{2|zzv{jUlxlPC@b#m)=+goTX{UPw zl32xOR>0^jwwJNX+60%^JS7I1_IL#-q`?w-&xn3`bI4!TE?I0E}-z5v>qC)!GYVV?Oxo>oXZWW3vQ zm5Na!pixdx-%q@5A2rf8?s1r$BD{PmElpUDwhH164O z5PLk;7l}k@*M*VS2weeAFP<8&Vc*B1qIN+HfdCGOx>!b(X62Q2^s8zx5d@)e$u(*k zw(zwB)SGi<38*o}8LqAqMiT_+*NTB#&c9|6ISPL18KfANP=%or>WmZ0U94}ndf3OM z=}(?Qc2neAA*2!1yI;5kQ3M0?iGMxur%tcq?#C@!v~Q0V(UNwtzFVQQYznEbRDav~7+4E)BNOiYkO|H(F zC<;;2Zz5o;&%0GN5sM-YH>zRKo{)Nc)8b(CCdHK!dIXTKZ4239rcboN_f7N6dgZo+ zUvHM7Zld2ku1ve+>-48Y))=O)ZLztTlY3QtIoJB~1*J}vAl~~`f_|2x*0xR(zm;>& zg|Xcg@Mno zgj#>Z!SAJ#@tYMT?KPOo4wJ^41+UP0>gy<$13>&l`O?2F`J=H_?u(gBUUuOs6o z!Z1ug>B$x=KfRNa32I~wWQwOeZ{l5rMoGQqiilyOwK1kNxYESC$FJc;9;n&cH?O>! zaOD6n1&AQkJ5hDAdi}dWvV05nacRQDI8l#?i3@H#8z*yZ^N^Uz-qa0i|E)C8GNW?0U-D(h{!JN@UrrMs`f8FgB zba_D@2tY+94Ums3uO%Hby7n>eX5F{LYR3pn0j4P(Zl7oG5<@*Y{!KpsGtf$5yB%PD zT>4!P4r%l+aZo@5Z-}szEE&4byG$kWjWJOP_`$9e(1Z`(4XS+ClPb8&7Rbpoi9w}! zpqUg15E+;-M}iy}X#%CNb%c6c5bdRO&NQ){WRmx=P5e3auk3rophB`oMW_{Jivvp? zpU;t)L>S)PE8+KJ;!X;_iY=WL1qk`cCVAhUvYIArj6lxHqavi z>vsH5EfnmGDCD&rJ)1}y6>xJrI0P@$5&{J~D@J0YO`;;C2vjhn;3tGSwUOxMD}q`A z2v)$0%86A;zfBJmoR$o}hB(;d={|m=uxrly0s#qnPITeJfFC4knSg&tPNsq$l5zf< z@)Zqq??&XHrwIync+FRYZNzyj2B9;&BL%l^;o`Uu5~2^pR!at9W;5eQ>7p`sMGnXT zT!4yucm~0(6UJs_AN*t~6;y6SrknHXERD$S28vpI zVSDTsiNY$#@yLyqJ#6_Ta;Q%TUm8$9w4_hcUuKxusm?F>uF37+6SJoOV4DqZCyZ8W zsIabN^fsmA`mE7zZ(jKJH(S)%zBi+_B_~a%cI|)9eLhNgiZW8TEa6kjuPl1oCVu;N z+~z(f;CZBwk!N+)C(W&XM75Q5;Ixm9cjSiqih4Jm8M{{I#VZHhCe-A1rMslt+V|^6 zmUes`GtxO@`{*JlZsT42cRZ80?{C?uJ;m#8?l*KUf7QX-0D}a@t^2fNo{csnWdsc^ z?D#mkPNFx{{6CgiE9=X3rwlmYzt$;zooBGw@w)Op?sL{UrY+jDQwGB{`ZG42SbTK< z(AjWb*7H&lU*uDU;4@md`+6@v+4pMv)!w3AkEKU`&C1Ou7qM=J0?s6H>Y>#9Nd98( z6>5Rv$J$=^hu!7kA#JRsO#MBB+`g6OKV!ByPh>l-f~>`$rA3w`d~95U_w8VH^xrz} z#N&36@UD%_$KqP%Uz?WOOW1!{n<77~{TSYU+Zq_YL3sTJM0v>!XysyB>1slk5#w&fR$J<&M0~^pndD4mFx4QYX^o| z>UjjWa``&fsbgh)n?mVDqM{~(F)bLTFP86n&z`obd(*OmbdMJrwe<1pAAeWBH6VS(5Q)&wC=%Eods}1UAXNF_2=1pvcCLtwLehQ9uOMD{_aoQX%s2l3%@>aDxJ26!>XR#IzB}pb;5IE$}-?wh)}01$BEOBlJFg z1jWShp>uK6?dcz_T@HcS*8D-9kD+dt} zR8IErwk`!wIR6cM@714U>iEL2w+jr%>cP~?W!~0oNjUfR%3L<-B9+}_ygX6x zo`kpdzYkGQc4Xtgo;^bt^trkJ(8vvu^(U53MbF4Yp1k15u7K8X1j|}l7Gz_-BJA|V z!KsP&t0w(Yk$*|}7ZZHU#(tMwkWvn`$m4~7fA)QPBgo|P^@}M)IN>swuhB*HGQwoU zpYV06Z3s+(6#>8Ox*40d4~N$Ho{u8E`VeWd{2?Pqsu20e*#M{~DIb2IjmObX zUQWGXA9`Ruj3KPVu7gs+f?{1o(g@a|GNOCAW#>^jS%Q3^)3qYv zf3@lHoOdL=s5XnK46OAa$mNM6CYQIkqN#g7=r)5a-Yku)|2rS+0j}?{Xp29_b};rl2f83P&!K+ZEDa_m5Nf7VbkFy`l0!#*LaDyuZ72eh-? z7*(iwF6B)24M>5P6g=qVhhDGhIO#twH#RGc{Cn= z3+4N_&W%Ryy>@418l(;rTfdiYF&*ZKK!^&SdU(GZub9BT4X4(?>+NId>>%s=5 z7x60+=AO#u;Jd1V45N0HRQEPNR_?~wvc+A`b>5;PBf|q)2 rTl&4~V#f!+Z)|A{N!loYWu{R5&!=IfxS$z literal 0 HcmV?d00001 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 984873dd9d9e69b2d6d53352c229d1a04fd1f76a..4a4e8b578f67fc0ad5b35b957fa49d3c25aed690 100644 GIT binary patch literal 2806 zcmd6nYgki98pmf&P9R)!KoIJUgW`fdBCcA{s!4D`K;@z$vT_Sm5U)f_3n=9rt%|Oz zfTHU@SVHl(RYbhhA|i>m1=JENAeR*qEC?c+Kp=q0$?Vt-he3BLzowg=5IW0YLR}%2wnEH7V z9kM-fQ_||B#EqFd>yj2@CuzjW$iTu&msiv^Ouk$-^5MfzjZHx%msI`QhSs*qw>nK% z_p)0`hoWoSTQokmYx)hNbHA_rY@1Bg*wuIb#?34B&wbZM`^6=S zPM0js__|H4Ax>Ueu4+XHJqv7W!h6watHah{^S?C^8$0nG0M;NRz&|pht-t+URNHcJ zaFs(uz)}8+BXkBzZ+$N0+qv)myT^(C9_G)R2HwLivSRm|t9N#}02+`ipa3BO0swzc z160`jU)1p{3GkBtd-M41MgVwE0P=r$NySCLHM#X!yE+CuX#QFI{v}$%pHnsYsG!$} zRrJW&4x(ZrHs-NEA7Vwk+GXU>1f?uMqL35uR=r~h@JBMkjb*3s=wdAp^ezI#2YZ&S z{tuI+3m0$u@n1QC%OpNvvE2~LsRAwmG<W-{5v7+vlF;vj&%hqwP z%Z=|&vGzoT@osy2^NKw^g({=)7MUhgDmr(Ft!j(HwBZstYzfbA6xCcyeu7RpW|BhHSEi{qXy z?GKkY0(%wOVUD}%Zt$cb$hl!sqrNR!9p;AN-ODBWz@Q41HGN_fR{Q+zi3{JxERC z7~>LAj`}`EhJuSKB!)F3^6Z%G!)yR3Scn+X=?ogiktN{qwJv;(63G@&FT!O}d5)}Jg__>j z>=guCNDk-|UN4%xOA51=q0!ER{3FbAmGC9FD9jW}OBeE=1%xf_{NR?7+as5O4^Gq=?z@^vycX zDrF)2K#L|EP|MjUUah_MshhKbdAUu1FSNZgZtTYWVev>|GT(N4CS;o*oSlD3#x*B5 zl(J7x`A>o7#RVN}E6mz!iLi(_8_qGM`5l`Xi!$3wK{#~boMj_#T)9#imwJ|n56x_y z=-;X}4jQ=3w9)xxGA{iaV#g8?YeIyf?Q5j1xIc`JT9q>qfT>1f*|D8^H#Cf03BUe{RI;JUKWcb6#7KI4@;tld)WSe3( zeEX|gL5?z$;lc$QzGh*q`BUrBJzKGsPDn;=r0I^g4>1*ZwI~LA z(4TBZnH6Q$c0&9j=7K&-m%Rv(miX=P_I!4=QJl%gP+s*$ zHZzvD)LN`L5Sl8j3w>w#tuq z(OrGsvj7(pKgHQ8_LQKU*#OqN;{_sRFJ7=N-U$t%i&pN0=!*_>`wG=UPEjGN3I_;9=~1)bvIeTUtySW;_QZXBTS0Zsr47e5YCAy`8y z5Nm?+$C{vGvF1_HSo5eztVt>YYm!=tHJ=K`nok8_Z9z$}wvb%|Mo)XqR477f2wbdW zT(~75U8LwCATdb%vv!su5|S8#2PDTyQ2-8A|iF{D|c&x5&15KQN|Gz&Q=t1?)JC zFB2TVJrD44Zv%obN>6YM^G*d_)}uGkn%8CTXS0)Fj!`YXb9RxP<_P;JE7kmVQ;p&+ z=s0+U;~24e(}}UP%G*U@a?kf&Q$WU^JDCg>ZQFij30b~q=t$0^SN}AH46k4zc(pDe U|Hj1hM*z2wz_5U_rP6)>23;s|oB#j- literal 3329 zcmeHJc{JPE9={2-D^(g=hCZYAYI(7bq`IO_5s#KOXssa)O;zkksjg^;PHL&;soEKa zR7yor47HcoQ=_FQDH6Nn{d&$j=bia;`rkWe?mypq@44UQewWYpd(R$oT3TYi1OPzV z=FAC40N~IT4#Y$t!X>}ogox6OlNWAai9R<%J+FI%V_w87-fA`kPhW3GZ%?nV;NQJ1 z0g%eIIdRO1Jo06n82B$=716KxZA|*8pGV+gQcknZm z@YItfg-3o5?>+8}D>&M5zs1W?#YjCu9KjBJq&BYS18=|VaqWF?$kn{d!woaq3!iP> zFhn~Jvs=@Q@j3>>`H2~EErvZaM#3D2Z2Wuu?ScPa4_wPO*8qy6k5{jDE?sfUa1}Y! zGN_PGH(kJ01o{_W7X#qQ&^ERSC7XX}K+85j3g(uQvwao|ryN}v*w*wSNjaoHIR-$% z^IpLSxwk}a?`oXfH>T#bojEN4-e(`yus;n0*{gL0!pZ+A7ls4$+Y6BBgSe`q=D5=W zi?#O3dZt?Y@bjOyKb*>(Fj??2 zCAWE6U1oCEMe_IV1E3{va~8EuxZgig7&*ZpU6atIMOPfcN3IVskI!tWVtD;8`V9q0 z5vp?l1ArruRf0gR-nD*sLyC5@e}EL6f4)ihoC`d=@qpk%j(@t{hPf4gJvpr~>>drd ztM2tu)Onc4gL08WUxyu_N9h8pqlw^S>|$omii7(A%ToWU>!9JI`ajHMS=4=5!Z1+K zIqhyN{Xtdpyi3$L+my$?9&V;@O@ZQ-S&8S{Y~t$Dj-y@azdhs7MmubeeI2Hv+f0W< zK|yn`pv(S+=0sR=u2BYOQCxDd203OGig{|aLsD?(Bo1*UiA=Y46+va7_!$dI*~)BN zmjgiOAqCu4eu3?gTLZKo*pFP|A7*K20G)?1r<(le+Ejgj4oT#vPrcQC(%=l-0A*x3 zPA*6#Jw@$+1q|*@{Ir?iFT?pInN$C4%WG3=m#0$*&7kyhRrJ*2`qk0NnJm)J*%kOa zVRWkT&ZU}gOw1j!!$Wzn92{;QPmTOVDX~hmLz+o=TbZ|k*GmK-#chhX+hzA`d!b0O z7ILZds904(-@3^iU5lrQ^2U<>6Nf4y*~dac7QgIn4|o1O0}3i5 zSFkYI&J4R+y;>0Lg6?qi3trQ2B9?P2pVj74CU$I=nfANYF<#+@nw&I}$?U zQ;mG$!p@z`;r#J7sI0h#^jZ&(`l! z4ggy#xdYv?E_$y~mNx`ZSvV*|-1)>EPqhcNsF;ZLH*3aR(Yra};So&+p#rOnu$e#y z$=(a@s?p0IWttjmIr~?F4OH6h`0}@{Z6u;b>ll*(?qOsu<$8|hml&SCl+I5lQKxsl z3{Mv8%f!c%&ED%0$`f=;@*dHwM@mwSw#u_OiDq;V`lSn;g!GEhYqMSYlsmM z-2!X(ok$22GNcK78~n#YhpH^oRDb?ym(o`+He`&;Zm@~`s{8^LZ-)~-^QsdS78p{7 z626f!pRqj6Txmtk$>?0QfqBk%N8`9F&x4SW^R@y`6xn-ymKZ}b6MimxbbD=VY)COt zdCRxAe4*EkEh#g;JlT_e-9K%h-ys>+UV^u7Nve5YpnJgd3+j_sk$iHUzN z)@H8m)igxWb5|~1Wc{{fo%F;O04;+BO-%IkCZnt@sP3!J^v0TsrRkO2^DpzZ?(_Qi z+sJalkIc;`)#I&%~%}C7WpdniK55M|! zJ>BCRwPEKu_)aOySl}{(n~&*!Q0)`#TSFQg*gy=fO{=DkhWr{eQ2vrP(A~|oaMp8k zMMhx(&^ZhmejE;7-a0HvbtfLPARtdv>$)Arye2N=*v=E zDSMDqT$C%4AquqYbXmkPWrNJS*a!F{utI;%Zl%yoi@s<*6Kiyv%T-TE(pW6I-$Tqa zs#}O%AB62sE^74iaW`eZ`mo+R9^1(rT;n1DrRWHOl{rlgw$)HlbCDvUimODYH z*f4Mr`PNb%C5{^ZpnE>^on6+>P^|!)lb91_)*iS113Tt)ivR!s 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 e9fa9f3a016e4426196179f66b30ca1cb8b1c699..e03403d12ed0f3dcf378b79c456959ee22be6e02 100644 GIT binary patch literal 3015 zcmc&!YgAKL7T)(JArPtotsqnytP0|2V+AX+kbA9A&?+((2CWYsift`Lr1B84_gY(Q zi;6Q!ODjqgDk?1x6{$Q-lv+T_LI)5M9*Ok<3L(fNK`!^6IjsJenIAKMd)LZ$a?alS z+h^~6_Wmj)IB<&0Sq1=8RtNcS03h*S60AjdsX0Hl4lho712*n`clXY{$x#V0uyV)l z=or`4A4k0(vmqvG$G$!FF}_$SGGuLN0Kd!g)TP4mKGVZnWozr3T?(r#<6L7)Yv8#{ zP5q{3t?un>8v8S)zdv~*oMYqL4a)1a{pPVH7b|>XA<{z~lk-~6g9e)evLHa6+h5TPEU zjDIbcKP-PWju`umE0_T^GOu3%tl;%ZX0t7*s1-*%t?_cS)=3O^o956<2pH*g)_5NR z7~kiRy%r#M%O5}f8MEvw3EVB_)f1;T0`NNY&MvGSi&{wN?GPqqm3*@dR1D{i9my^0 zJ%IHa$PEFg?xap*!t2Z={azG`4f=$yu*VL*8M{|=?)E@uTGr3T2BusZ8+4-tthyg% zVAf$?iTU&v^(2!XO>!_OLk#f}F5)#{KWaxL0b6bhT@r|Q;gTIdlhv;e18BR<1Y$v> z(1mqZaLLm^GsCD4z=Erc6bo7fBevMel}}n2^?w7HG1{hoPQutD66Uu>d+Uu!!w=5o z1Ke^ha_dm6`03N&Pr2dK>sab%3ny9`TOclU09feDjWmGHzR%ezw z0G-Iy;xgReS4kk`f$(<{F6==x0r?$_Z%SWnn5w~&IUVTByi}bURLbD1Z!l3aJIgwl zp`j%n?!Z3cdN)J^CTwoT|)yBk`B ztP8o5Z%d%fAa%~dS;+%G7xN6(CjzAajjLiE6dekvaW!V9Jjn|8F z#$GU+iZg85r6u6nAso8-?3Wj)JerK#_C6C#R?F&sW=@lF-LLL^5))a6#YH~EGD@-Y z$y*_HLIIq#k00)fa*)gAo@(2=YIL!OeT|G0hLxmik8r42Z8g<#P|p)}703zhkyHES z^nE_Or+d_Xf#I$Ik2;xn;V6BOoH`(4acX1_x<1b9(AG??WtVkrh1CSADM+=^s7rZ^6YY$em8{yXJ668&xdw) zKHwudKK39QEmFgTy`je?^vYBd+P`p~>Nd7IU_ba^IHDGty(l}vzR8K=2kZv9VP^7V zvp-#kJw5j^7`+TpCKJ__n1YfC!f2MNE~Xb4%|$gFs*y~L%INcad2{7d3*&E_B5`dK zI%;a!Pb?_(bzmJhg^yK^8WqM7aJ3z2-cZ%^Oc)+Ft%4$xXdFO4ZUXqpjN)JVVEiy0 zEnW!L?F^cEcS3&ibL(u8T?tC^d9SRS3ev0qd(M3CS5F&kn#Dedw7x9 zS0QZMA`NPL_8C8H>z!iNyC(8hx}!07v*v*(t+ownZ;s*KcPLqF76W}nMECJmWZr33 z-l;fDr^v&+=j3iIu@YRh?`Xta&$JTl!c5=aXR*Fb;9_Z-YZ!aD4(f)SONNDmq;S9s z5^~5bB}_IOk@+|#(hA(orZmLtA`p{58|sN1o!W-ACe#Wk`v`&un43KEkWB1*88+mK zg9ER3Km9IP)4knW8q`{)6_(;miHpe%iTZdnM4HvziR|8WuM8;WMrwNtx7w^townGe zEOpA6FwOS7;?YX>r23`oW4f(hG%PTb)`utUw*o`m?8ue@pR{d6gIh$hoW3!&EPfN?rCh!qV$>1lzV1+0BM#VPvDBy2( MK(K$&Talmr3mLfhjQ{`u literal 5917 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 POu%m2{WiHjxnBAYUpMVf 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 dce0a93861a4c18e9b567037fa2e6d7cc0296aad..3c3f68e33f9c8f02e9756b2e34b93d4287be1004 100644 GIT binary patch literal 3326 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 literal 6189 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 literal 6802 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 literal 7377 zcmds5dpMNo-@j)(Wvb1g10szzyE@o1wOTr8rXn4&os?vzMUs_cVZ<|=ez6&JPzo8< zZjwrcoYG7yrYMwT-WC?8Ta?TzxVg_`5x}) zS0@Misgn&S0|2IOSZ})(06P4a4j2>R&p_y_QTQ_}*lt(wHoyJBA^QS7!D2Jr)Hm1b%PNyCA0Ed*9T7SiSu6no_Tn}2noVk0qR(L7-!VkL1W^SiE zwtPPJxFCh$cC0{Hc})6}>h-8k?^zUHmDX%S^+%NKubO?@CTq6!(Ux}n(AjAq@p1W< zgsMeVJ?0L+QABV^KvC$6KPDL8&Sa&$UjWi4mGiLenPocwcSBR;ab@*_(@u}qq-0g< z0#JeXa8lU^hzad)EsAWD2hLpQUR8z`Q~i?Y3Az1KiyUT?*23tc!PJ zuM+0c=uU=ZG40NDSDvEGX0a0hCiI{BbpZH%t0*Kq;QqnBU(#F_^}JOxmh^|?7e8=< z-KWv=)Mo-eid)FewV*_ndu?uY$ui^AC0XbFW{#?l&zIps2D}L~!jS@u<)kSW0G(jM zP6(xZRH>DzQR>s^K$xzupkn~6mhmtK0LS7bYDVM#IwL-SIFB1sPt-DP$RAlqXe7Z} z2c=3?nj&b4P^oPIr2++$jii=y_(-YI#euf2u} zwr-6428v_9Q!-8H6-?xl8!+CM_DHiEb8uZJ5-In}k~B?kLi$|~BkZ_gNJaJy=)*Q- zm-IUHU!vNRhi5^S+lZxHatJ!NVmiE1s*oc*?31+tD!x^KiR_p6B( zbow~S)I{wS$os!Vka{??gi62d5}N=)@{4e`afy#aK2W;3x&BgobIWNrL09q1%jdo8 z+>QzYmam18!gX~l=q?jrJoRbnXhBL1rh8{BF~s?Bp`Np`P-zh>t4h6;c4peTl>~;!fLM{vAY^~6^p$+(Os3&1{NMK1VD-LwfY8qCYEz3DNI5|?|SZ85!Drs z?BfCy!x2@&@n`@HpL~z!y`NGp5&SYo>w2Zit3?x}uMQRmz3r|&@|#gh?wC`j+k0|% z9{qtMx4E1LfMty;gY-I7O$=WChHfj}6)yqcz#W8;zZX(yxhemq3>`)prOQ6Auz;dM zq3$0U2DM6k2)9+FsfhS|s%V5;S!dZK@R#Gzs0k9kYC|z5!-` zjgD^39W@g)NP2u7cWnY<>Vr(vr_rWzAd`s{>Kza#+$w@Q3@C{2YMO3|c<6Bw=mEyO zI&>Q!+ED%teIt@%(h6CM)^X<}w_#9>Qxf9<-gqy1=ea@ZFA{q}H$EEAyt$y?0;U)S z6Hq4zny*G0Vnf*T|BncK0*Yl=LosX;1|ORZ-D!Z^a$%$(xBb6PxMI(JqUMG%ac2y3vH39STLIsl=iTAF%=20GILNi7~m+xY!cat7*IWPSBF3bBP>-J z;Uau~Zoy=sU{}ZpZ~XL{F}uLVd_T$1J)hfDS%9o~37h-@`=@dEfF~35(RJEDgT`%Mde z7oV7<_ZNZxZ$$9kb=GwPpUBL1=QSif46l~tiz&HJ9ET2J`b+L`#6Sc>1q7}(qKvyr z#9;sf&GtpS7cQx7wJ%A$?#I;S%%ZL;Ej7%!?k&^(%&XpvY7+jwHJa7QApDg%wz8>yMn(gx;7Rhz_hr0iKBLhb9yzq{1ZV2B>D}5E z5xq;)*GeCMMd`on)vj~S>OX$VvAHjL&z!=5<8MEInUZ#Qp07CQh8zn1Ay2E`=!tPfs)^kNS-cm6lU=-QPLX z>wR6b_~ct=XE;nhXr);w(Yt9aCoPT1;S|RRs|;m$L9Z{&RwvDcx9OcL$=zhKW1=s? zySVE6l3-#(c+n2tZZbLTYmV_UC_#JXvxx{0b=o+(OlO`Z$CE4r==SYM= z>Brs?`POoXG*}TYd|JT&s+{^@H%0&Ef-aTzh0%(K!Jp#WYvZRoY>0Mg+K!YGQT{t; zDyYnRutTxQ+Y`~r@io8LQR6a6XIwrufR(U!Ood+=w;Hh5rCbq39l zx}iB*A8mH{=$Q0Ri z8tb-YY%Y8dA}5y>hYRNZOlSuCHk#3q>kYb;O#gQQvTMyY;oBU0A5Kz-L6au6j;DnA z&sHrT`!J%vjVHhS_XeMk1rRys&z~QtFxT8VTp4br_Nf)TOGH`6QNo97BRU$oo2SFM zM5@xDOH~QADz7g?%=LSCp_yO9rfGn4;TRrZKz%2f#xG!jym+c<+1zW(WK3~iTk{8)fTzjT0cH-DF z2s}+1pD^MROu;~AuFt8p)YY&*NxeaD^2a2XQ8Z#dC`# zd9HVHgdo`}e1V;E+ zfwKvUF1-3LT*B>fTq3uPZ_}V&{NiwM#&6g+5v)4~?TyZO~V)5r3*&N5_;v;a1M94Ov_8U_xRnaLw96ng& zuQb&)p(Cd&n4U00pm{KwIO>5ghEd2;YjQ+M4!b@Gw&j`A*Q^_NSc)%!hxAfW-mjm{ zVvzz}odx5U$VCcqTR2sR2^oy(NFg})zgAZFsoImi?AU}^9)?Efz%0Ee<22^BC3PIP zVkUife#sp5AQXsg>G{JEpW7ume~YgZ;X`9l3p<@dyuTw84y6ri8Qul7_R#Bzo{H)l1qY5-8|E!k#R--k%E}pvbk11~)i{HAB zv`~Z3*>KlYy?fWUY2f3_1@;e?kR!~K;DXH?7Kno}Guf=(o-s^j%~Co;#rx{;u-h?0 z$7_aSjhYxJJ$aQ{t$W!K9h<6W>ax%1;?Gsu^XMj$b1g6T8^U=vSMB=GMOVR>lR1@H z#uRxKbd8Fqu-`fBB<_QQbFI2MNlol&bLN@yU+N6M=j;$s8PUbWHm^my@mJ3}tY&1e zR?Tp_21Bz&?OIT3D=jjm_f?J-^?jI`kyqBiDpP5Hnf%N7#)o^uw0~)LYibZYyE)7I zwWwpuQaGs(KqW38H!GZM*eF$Hlxk6uh?Wf?N&#J*ZtSsRC|%9bHTka0^1miN+(y@?5%e?}U<$}&r z97$wpX~)y5+eJk3(CouMziGPv4g7Rar;1_$Xk|`!bMdzNoW|O!=J1n`8k6pmL%$Ea z@=J4+ycu2dCZFPF%H*eoTa72F%TuUlSZ;+OJJZ}=UKE!uRUat?JG z_JvsCiG=Y2Z$U>BGAi);w9}^imgz>dOg{mC@U`p*o42ng!xjJ^ytdpqloiK0Y{DOH zb1inM>P+cFMRmtO`eTKdcPRf|b@qDwMFKC+*}OmRuEk=6^|sP|5YJYZ|8&Nr%ky1z zKw>b_-_`Bx8fKTgqWy8jd>L0uD0q0n4NF`>cM*TGK1%S$s%O&tWA|7vz{cfdR7pcs znAfHGQ1Z8Qhd+mclke8wUXGV>G0D{7ZS>0VmJsG`&9$Xz@37>87 z)wd?aMjK$@+ga4g&XI`jtPufBffIM!fDoi(uqfq!vN<>R%MPUM*G;Y%H)GFISU-;v zImA}UK@Bb>@1v(TE;Pc>YjO9Sd>77sXGkm!UKw0}NgPx;ktWsMTogEx6q{2yH;~9# z**cOabA=~j#Wkx!THaBKCsF1?` zFDLnJNLYEd@HKB;mz3Xk7U93Q;ad~>|93&&cetob<3?i4@SDDPdOP* zX^lkzJ~m8Bwsw_JO4W7_djF?=`gDVE%~MDUeK&L_<3mx=J%yW~-K~sa(s_oLx#3}$ zHV+t26bN)nv7}6kqkhR2cBscW!v7-26jF?$8mI3v1_FVcgz)z$(RjMo`pPIcl zEMDbqGuhN+CnoYIavW%YF8mSWE>ws)PkT%2zcb4JQHQqJH2tKqdS|#6fVMah6B>5g zO1BKorhb0LU7rhhGeb(M_O^XXOih zTlWbClBWrPnh5K%{v^ZmrV}*o>IvQDDB6loy>zS3UtbP+Q z5&C6aYg3!KOKi@UJ)@)7654Qk?D0wA(LbrO1zIEZ-bBAUWmUWbfzZM2&gT`GClWOD zXr}3DoosSsG38^^4?8QXcvxb&L+LFHdyrl5d2dIz-q`b?-9)75cy`Zoo%w>FeUT*%mcy zAz;h^VZr?hD(t4ZgG0hJu)F@gFVqXo43?{b>$cSAnID#znjFH2M~h{@QH`o?3T=ka zj3Jy=*3eex=72pG345B0Yqgo7HE?)Kp9H&?*XY!;%+7q89!+V=uRXu;T%{qrF0l#L z)b4K1mRjBh?54S?i6%D-nz1&H^4i(rGBT*%Ic*AF zUz6MD^Boo@>C>JY!RYgzXFoy$SK8D1*3u|5^(cVBFU+(@rhGVFbPR;u$|jRJU-Ghz zaQ+`#0%wX6f|9bIbVcwC-)3N1AFc%HWsKa)&>HFQvRavn%0@>xFaJ~L*PNu*!Jb{= zAw?3!>7u<>NvXKos49AuQ-7P^SiRrtfh9%otAUUO;xX%KPVZQ7T+sp0Wiha!FaUHn znn~1(TxOwp7VZeqgOx^uTiiQp&+E`kP0$w_E{FlTI^V%Dzy#RB*#|vvb->@}|Az@@ z?4%D0gKF`X{3@sxlslik|9R@^o(Jv4<5Jh_-tn%q_&b#Fz_)aPAiS1?yYJ|1$!i>E z`kLOS!eMLQ!J(mm16`+=YLmDahTcpJV9OOULIqu~g9Sc5Z4KmzOg#3cK8~aF@*C&X zLCZr%PCjTEo|$pqMIy6n|IVR*kE3y4d%a(%wM|3Aj@sdehZ^cSj@{2$H;@-{vt&iH zT)BR&Yj!!)9|QLEje>8WN7YV79)n&)4u*=NK?3>3Sy2p6ZyDPLe0so1h zVF0%N&cylWvYQ736O@`d!1h<6esOVaGLwKtGQbsEZf{FTN35LKSvO3^L0Rd)>7J5N z{aL-&M<)r9`_P5eDlr`H+pfk)26@8hF1Lob>DhB1T!7pSoz1ZuDV8g~+lzscEI;7LU)wAt%iTqkVr zx?J3I^DB>l8(}6119O1=^Hk5j>W2M0qNTW5pXYYw-X`m=}b;z;zo0PNKt$ZGXuqyyA(g&HAn@&_J#b*d;!wh5E@ z6D#64FbxtvuICz;ORnNX@mLRt2i!^V*sFbnvmp-b!Mys@!XBBtFt@nC(ruCtdeEZl2UU)?xHvW_x#;_ZCWG+%nKNg8=ggUNe{*J@9E_Y5 z-rcdUBLH-tIwkZQ01}HN*o$!YG$kuENK z`#E7!*gb>rv)bkSt@tGcK!H23f4V&Ieza!^yc@`bLW>BvV&t6lP7LW zD=0%n*ZI{W5km1>%lqK96q~-8H5(8AzaPZJcs2sK_)ZO-FemYCLq+4fcOf$RZ@Il#c1K7KQ8)x__@e+?TssJ4`;*jTk8Q5W*#t;>0ymr}l55HsB8qTPptZ@&>z z%eb5~p#Lajkr26viXvd}O1~8&x7hlamx1=6`fBOud%f&I!j-#1~`t4MHA)xB%Uk^~LaB{Cf;PA{1fxR5YJ}YnQ1MfH}p=Spapl@-HyF z?I?zyVZ$+eF8>s7B9;%w?Q;I9Bhb3_?OCtg0m6B;vltAsDG>qjrD`?6_O@uOU&-H< zfMFrE9IK7$Xn@ac(Y*ln^Fdg>l$wmyW$FlwZjbH>aFxG{ovfk0!0LDEaExw`b^&<$eppskJ8^Fr9;%Jbt0B?sDzQXNZp$~2k2%R{6hlMiS zCJCp-eh@hg`H|@mISY3X8HrQM!70)45_Op2&v#X(a4?ZS)&&eOSMe(sv;<`q3bmyF z_?>4Y?zYt-t=N67H~`PBryXxGP*Cw_?)_<^nC?=?1Nj zMz4fXoTr0FofJw1AF>;s&SrMef8*(<41aSd1##Bs-tjk-P{O4;hLp*H<$FoaHc4TR zu$m-Uk>Mh9j7}0@L~X`E#Wf{-#}2c%$BSJO$|T};J9;j9K~CgEhMUwp;y0eq7?qdk z77TCD%`_YkAhSU?)o==r8+DTn*YLPeH^GpD$4vqL=4wyCDe^NnbccOy?!87lP(lMH ztA|MrJ=jAI2Wq4s<@bsIz)3hH3?wh&Xu>IiSkDvY6e^2;CP2$8Wt8eW3UX?7-U@8~ z*%Bd>Ve=zwxYHDD9>EWiq+s(@A&NYP&7+Y$u?m~tL~+DH+^3-_#C6=KqrpTT?lbUw zE$%a^OxT6pzfnfgI7c}Ra&J|N60=JxQxwZ#7N>Jll*nNbr;{m4<*<&EI?8ZT7~W3& ztj&&oFFYp2I4K)Zf8rQ+_kg+zMc7>hGl|C0hBQ;&svIRevLYW9j=J2clqo9ZAaKnN zNzGE4WGRPdg>v}9g4~iWDdEQ&G(a((g5yOfR&`#0!71oXo19QlfI${J;Qzb0>9Ee& zz0J;06tZp0{dJfuW7vl>Jq|sV_p&dnlhXeY{vk_Pk^Uib3mywlQ>z`Sx}^k`^>i$3 zkkXaH3UUr7(lgA@P$7Uvjdq~wz7l?5J?#sdr1Wn>9a+MOG&xKb@B)MssprxYC^%b} zG1zcX3A6BN)Vz|zRc@F=X04RIo%C`+dTOI-9DO^p@pSr{M$;Jjc47Co>BAaLA@uE* z;e9je8%(3=+g~Q%SrlEytb&#A@2$#BzeVLkSbKqw;STi#!mI_}hI`aQ7|5=4(Uda_ zAc`MppV?#@iWyNwzTko#6%TMJEAqriA1MLwq^L1)E z?B&9p;!BwUP=y)RQD^EwJM)*sA@wF_+K(S3POLXM(lc;P9G;ac=hOK@cX(Ebsa7Dgi6U#{-iB4Ej<{x#%FK7EMmWyu+|2i= zk5IzuWaezD0^Bg);>(%Ua03%Au}M7=K4QkjSTj86t(bDToYBdk$DFI?Qze>Lm~>X* zE;*u(@;2;1(}+`T$j3mTF@#6EQWmK~Ly1%Erk;@#QD4Htid-W@kt1=+irgZ@5DOug zd${7267e?j4dK^_%qb|p%oFhGVnwt7xWfEjQMDna5P!RTV-WAzEpz*h%>(eo^HDT^ zXZeoXSEx(ytIldlt2KGkAB|o)YuI3W&HV(aa7K7Z%j7) z^+t=<-jMZv!RH`8naFI2`Dy7)6b63VUcX;Z%s4=PWv~0MEm|;a(9FyY;#2K1`^B8t zI1@c5{334VKfFSf!mqy3|12tImOwszx6^3J=xqp$OE?n550hj%m+Mx}#5wTWlTh<* zF*6bJUA9@$YSmu!$7``;FxDB@t4?uA3F8e*xoX$Qxo9^WZq|k4`zxQ26)X4C&sleo zBAt0ezhzI7ihIl*x{h@x6}ik!nq^NCipxw2?Z~+kiZtdd-IqHFio48ldMM`(ipR`h zdJMq+RhRh+QBtkWU;kRDB&M`Q^om?dRl^7Ns>8KxMhnOI+2T3nrXVO2K9Q@N zn)}e3@Krn4n&7O;rOwh1I8Ksui8@R_=N?LuuA)TZuGQ+Oh(@=_8_msbs$Ai{xWr`g zrO)zD?c?;!blAe}cZ$2fgu{Jqzhm4*W)gIA`yJvgF-qvmPZE!MW%7b^!d1zgS0+zL z6RwI!6`T6tD)EGTTifhJuRsHc?rjMYRW`-Yzp~p%eFYPtn$3F=h0Uhn`ZQrD%(pbV z7S5-F@wFj$ic4eMVII#o#icVn;d@>xxnnfhLB7C|eH)rddM%>JZyTBkdNX3kGY!o^ z??6iOZG%>7n1oDZ-$rc@LpU-Tby|qKkN*5`6ig8=)Hbfmet6D*yimVeI3YT9#vEFXw#7qo$DVkc7!ED+){>5MJTncOD?5GCx=?Hd#g|= zY~!uOUTbzQo6PZ6I9#h8LJJ4+#fFk4N9wh{{|*W+atj^w)Ij=)aHvBVjf1dPWitD0 zi^&=@0&JkY+!`m0fKS2$+q7a5HPg0Q(q-nn4fd==JstR-M89^lSs1@_rZ-z}|F6Sp z-TP@5nICMRnkH)Jc)5{k>&VHH*+SaE9~C>dGuzA8Wk+U9QWN2wTj{YBRdB z9N1=bwavNm^8WpAit$2pC^8M3Tv=w@3S1Y$Z0o4fnXZi|HE0#Kv@hdQyV71OJnx#$ zWo~q(yGR{nbN-<=!^Z~O&=%e0ypv7sO3Yclmu&@AC4L{-wzL?XPQ*{78l9s#p3gYF z9o@9anhiO^yNm-uLP5biJa*Qs&k;-+2RLfo`7~_qWhnidJ$m~9$DG&wBra52oLg=Q z)dQ;pX6zus;(kuFJspiXl!IE8JqgRn&fb|F_k?D@DnX6Ybn=nx4ug#2ss!y0jQzaO zAfV5mK94(^gVYOwZ-tKesB=aCPoMXv16xhBB=F5!{%BR6!C}}LjBK-4HCg zBQhBQjjQTZijQ|UYtVjeHJw?lbcqb4n>p%5xYg*IsgLaj;T+R&PV{^5?kJb|>0`G* zEQh`evqrlbhN$|ftU^Tc%xYHobpcxG8lN{rp`@cYs-msei$NL*-GDFiXt%=6-QKuF zxZz{;HPPw?fuaL-8DE%p+R-myAbvR1oLv`aClkvm)RPVGkF42snz(t98)lzaCoIV6 zp|I2ZQmAC;|MXGlU^iTu%5QY4IZrhC-03N58I(q?Afzv_$5U6@3x?{qbxWN3)mM=E z0YB->)!)m18KU=~52{cM`Oi`5*Wt^04DO~GTBQ7i7T%{#@s(%>y;4URbeDdf6kiAmfZ?b;%vxtrD6``T$XJlyE5 zxQ3)FY&m#*ecsVwYl#WGwgZyHg(jx?NcncMCPS*Hy2Uf4y;YP59_yfW*Tes5#pm7^ zPDU)mr#3-iUf+?|Z;MURY_6-_zKXC|CFY%}!)Re1_)Hq93L-4o4|~Lq9VMFMs-h;r zq2K!Ge*|_RSra08F}C;W{K2$v500R|mW~u$?EZoyl1=A`K!?P{oD?RAUP>I@N`7z2 zwlmKH{sa-yQ$=ltbQ~binAO3AISlxpAjCyQjRb$JmeC@tmT3HKt`18IP5tQWz>b9^ zwL!$kik|tKj|jQMhu-nw_?8TGG7C~GWI5_70s?XC0$I5741hq~ehFFla87_g+(yA4 zl0s$#{7%-W`#}_836RlxVBH;5Gk|xJD73xXVFT zNvOic9en4=nt-z(pYI8Kb7L$1dj+OW Mo)wxtQM3Dh0nYM}kN^Mx literal 7914 zcmds6do+~mzkl8_F}g9ky~`!W9_`KUhKM3&+L5l{n%5pRT;4eddhK^EJSF*Y*9g-&{Qw zbWvjS)6%wWpS3OwuAoo5*iL_M(sxm+_*l)}$1jSCDv&BlSShEglJ`cvpWeH*W~?{Q zN*k=&F8gSfy)!_^KRbB`5#b(DxVyT0lTYr|^GTuK00~WILHos~egojHoXgrygq>XR zZ2S2quRkyV_#hq>>Ws)1Zh4vtTmcBg?!LRgpTz}`>0sCWM20Q^XmoySCVve;JpShG1i-zzQ`U0&+>Jnoo7tHOMGo*R z4^MfFqNcWTuwWL`gTu#w$Lyoj`${_nt;4az(b0hyWWOk6pQCX0Knq3#_|M4#4$}kx zhAqj4+`9V-B;BM#l>jinl_+PRk3`TWIsi+{ed*AQ!^vW#D&0t+X0-qBGg2LDP6sm8 z6v;eDTwIQX5-Qh3C>2-g=s~GK#)JO z!13jYccX1vy;eO>L8@FcrefJ50_kxjh;ZQsJ3}2rAEB>4q#&>PKzCIbK@;#o=&>|A zWW^H$0n?)zSui;YvJA7iFzm+>%kPlIk1&M(7cD_YCQ^N=s<&*>oAE_F6ZF%#_K${GsnLlGG3!-8jKSUm?lc4C)^HX%^4dh zX016|ZtV5xFSDK5)^r%m8JLnS@_~^PFm+Kt5oMk(miJJxY@tuc^_;1ts*JzZ1-P%Y zSSxI2J^ktX-mc!tYR|g8ss5Q0pl4Q>=WD#X5LPK&;-gO`ofTG6=jQ6S$@&|x`)kyv z!jzNp;u%*GsXrU@55lZ~JkMJ&9*f#y+dJHXy+~Fnb4JTKlnzX~M6oG0Zvp9Qq#amO zZ9hWhH=o5YsG6ir<~s#u%h%5m1fkC}k_0`p79$v-nn)vnAMRL8wS6e>S!4H5cJj&9 zfaKiecIhU?T76V}33X_Rx`ATL1 zz(kZS^u%`6B!JUW#0-WJW@=B;fT$J5@W=n-I%a zNn%D|CqwVGas~k38A7%HU|M#mKU)Oxlf@SOpk6>JqVL$EAoXGiT+}fob!r|Kgkc3E za0Hz;9op@2jbZ*&z0ik?-x=nW>IFh;2gAHhy?}B&o-xedsuwfi;tIoDZNYR{0zBdw z=H2QYW2jCY!+b!!fN~@duLN2kz%E}%TV(|eHF*YY`{@||DiIpWup!xg&ZeUENYBI2 zHoa7ac?)8}A&Y?lmW7;&Y7%!DThu=`sXovAlhAj(_E^q6C)i)Fw2V@B`yW$V5Lo>P?MwZVB$hQq1wy8-8kxvdJBX!f zC?8QnSqp;&LG3$E84Gzfk@FuJB;{wZr9vG}iU06Z{>*~*59f-u=%Wxg>r+HJr95GF zc`oe_Mk!1TSnj@11>=9dfe}FQLAX@!p&?7ggLHb!du=Vuz44+;eEFcX0rO{aQ{TaKmf7q~PXSh@QrpecP z4awvZW$%?fY5|L?o=fvYz%~JAtO;K?N^qsbv&Ae#K;Dx!7j~x4e)4_~r!}9k@mrcD ziGxLt(SSYBGP@?c>a=5UxJEfu-`$=5K00}2o;cl2F`}r8XieHSCYk}uI~NcUs_cO& zt&cDALIbjc^L0B6%0BB1JUDAbvemkomQm+zK96+{kICh`m=YhkN@wWryOT?N%fxKz zlzYJ&l|226V?yiNm>rT%VF~mbcR;D|s*TTp(*mxvBe@U3l~BUFCmof}GFJQy5S}b> zPf#}yaDxS&-2Wi(o&@JLOA_2L1W)v+tgu}*o15FzP%J&W`C||ciQhD;_?r4u!Q{AX zh5^)GkJ_mIzyv}cU8PWv$~fw@*I~f>jk<(u(QzejNL6z>+|yoh-MY5dF{;+|tZi0r zkApJ|uP(T;gq#un=ZRgyp+f~kQ{j}9tbn3|W6)bcsN&xZ2KU?7NCOy+>f2Bo!w&G}47yGT8vk}Cj8gyv+ zoWJH^^$w)e-m+CC@2iPa5J!dg7k|@=3G#*pCfk3`$mG~n?dF8rxHtR0YrLb(^Y1XyhCZ4np!?aBalUA=4*1#!b!o3wb}sy4?lFR9*F`Zy?m7q6MD`6q7X2g>cU0(Pt^QcO_@e?vQL=k&)MRdI znPBWhO}0t<*!<<2)|Kzq<>2ou9-|!<2-N$s5H>u0>yq$5#%Y6&tqlNIw!Gbu9*9XR* z&1f`NT&cBP6V>5Erll$kD-Ad%;H8N96ow}0R>W8dixAsnFN}f7E>x>^7r_FmYYjF> z68#N>;?_U&Yr9WpXJznv*F+tRazVmy5cokU?kr5n{)_d%zRIcRPQ?!?hsuQq?FYM? z=Fyy?*fvdWP73u>(ZzqOQg*gI2>W*}(NhEx77d)y8v_z%s}?po$~o9bTyyfI|C$IL z^K>%qDYzzG+c4&TW=YWY*DQF2g6Rwm;{GZhmKTu0+JLG1{kXCh9zJt-B8O|oe)*2J z*?yeHE`BWic%vq3n$&S&)hQ<*I?M%~r&I8F3&m*1G|o2|M50EhhA2~66GOGW85nqf z3*Jb^HL^#!SNXex&b_+<@@Wy*44J#m~O?IQ* zUmqpcO1FH1+3uK=|C8V*yEseHBQ2TZsCsrW+$W;%>9I})ujGeG@j+8Y``QrGJDGI4eiy!~!VhKiIj@ip$n1w=mUg3rROC7)%A72Fpr zn7TN4=6_^sPrT0lH`F~(bi=%@?#+ACvS3LhkErU2FW!3~GF~80KQ{Qh2}o64UaaBv z?2t;|=K-Wu?5K64N)U3>ogf-Bu1Co)Y1{40{#H13<7K5nZiQtD)+KYFWi=FIb!U2UzTa-yR7x0byMxJb?$*zm7G^=8)BCD#J-;Z9pujAjS#n+ zYbRNR@ZJcs)4f%9XD+@nSWkMmPFl{Dk8I$J{Usbil}>b&?t`i03hKI^DHYUR>wrK1 zaEnnyy&Jmaz%S0sjNIlLgdS%WKqPRlDSPlw`qbMwjzzWiGPbVF>v?x}$~*GTn3veC zh9WXL1qI?ihs2?xI+-wx&ka)NT+&qhP?=bIe%aAEGNH6fdak97Q~x<56#6EJ?dNYx z2FJY6u(1Sj2GlPn!y)wtwy3kI+#1{o!1? zVG}*tF#(S$X~E+3LDfu(=!{6!|GyV*_$I!)=onEk~ zzI)*&I`Oz*2{#GzcnV7aTcoLTz|sUQ+ttfe-bTsFGjI6z9gwgQ@0R3hK21 zK3hZ7=ss8!`^a^U&QQ*8@Jx-3XC`n9c#-(GG!dhwzed>Q6%4x!({cqku-FfOL=%}? z^qolYF6QOWfu}GF>+=dn6OgG4+={hn;BI&KwOz@A`K-@GTFL`>;FHgb723ek8SE?| zdDYcU&D}~xMoIzNc{oUUmAu9@wegZ=)R)WBlIUM*VOZnwb#Bq_O#_wb2gQkm6BS$x zbW3$H3BQAS43mHnZ-njHv!~Onv0;7A5m{OGt)o+Y(ppCN9W1|_prdGzsu_GY!b;)~ zPJ@qEt&P2VPD*paiGuxG{Mt6xK89J+a;b2 zDn*Q3dsyK1oaUFs3&S62w5E4=C$i!60N?VOBOaz@Qo{*B{q^!VYx2QRw{&pNCVF@O zjj5Je2BFy_X z*!aoYs8tU;-ZE0d8W!)IRn8H(h=23ik~+U@k=bCcG$VGnA_u)W4XEUWx50tZ0{hZi z8lvJRG!yjm2a5W1I2-ko2pj&;^|<82jCg%)L!ux=s70eqwgz4Ll#5raxz8U-H7io! z<~P`eWI?Vse8n_Hd&D`y>DJyySCZfs6I%%RvdZC}E@;$w);pILvZq}opMTmxJvglA z>Hm(mxt5xq&AhVYmEh|#PJx^H+Zb1@&lIdPSV)fbX^kb-&s)ayNM>}jh{b(u&P7Y| zK~`kzh^Lm`B4B?zKY2NL+hUI z$USIoap7swA>IazCLEY&Nd0E2oG4(MM9?2L)t)HX*TDZ}#KGvJiXTuwI>aPstY-qb zd&uf_zOl-<)Ps?}OkE3FpDQ7k=NPMEn+8WRUm8$bvcJH`Vm26`&>fcyNzQAV4hzg_ z+g-S^Z>ZM>Q-|#WiT+VCV`NAZes{U*ObqXutOtkPH1LvUq99gC-8CN8@o+&&AkQ{< zH^q{r$gv>(?EA;W!l}mg>-*=y=VRLPKqBgP?SsY}l(Do4Y@kDkQtA8ag27~4Qm}yf zV<7kWiP}rr(|#Vkvu?JcWj@WBi>QDriZ+y>x~7T zQHP@U_m5Kjx2CHbX_^h_qpp4j_BBUz>#8YtxL|H&wOj6o!)%ifI#I1G&!!qx)8Ldy z!z`^Fimug9O2AJsCHYbOgP&7flQ`T-bS`7HzM329PZRJe?*h;H!BDUim?jEXW*g;^MXMug2 z!1a~ln7O6T2cs7-C5220hrAm-rHmz-1&fAPsd(Hv+5{XHok;qcOdG3nP8Y|j@`fi# zRhN}ofmS;-N0Lf17_sIXhUXtD-`6=DR$Do=PKy`|H#A;d6T!>*6y_^#J%dRzS_AnI zk7x$n=GF9J#@6E@g}t(HUvh{i-Oy0mQ2|Gev;hWgilIJ@mtrRBX^CL~#$7M^rwH58 zV8@sV*e1K+DFE^uM*UhB5;p!X5>`#etS`H}Q>0|GOoywa!7{T&?VXkwil4**e@%vE ze*yDmwoOB(PQ)9pJqH9$emb-WS-luiCATYG# z9s1YYK~8y=yk@O+v%AsfFAaMR%yn!ozL8rKGj=%mm5Y&l{ot*?he5X?N2w(`?h0n2 zs|$Uq8pz1sFcP{p0L`isspQV26$@DscBb!O(B5iK190ibi!V2vxVi20DKsEht2g|eJL zg#k9K5(IrXw~zxQDh(3=9XxDGnz^0wh@8`=Aj-#}^vi~|vwfeT{2PolcL=W=T|D1W z5c1*Wu*Rl>_qQtIQ@)q!;HKa^r>~&LV8o#rCTc6V;MZhm1r8YSDVhZ^UGxwFXrf=4 zH^ZCn4V<-mhn)TR4a2t`4pLs43mjfM9$!|hS?XZ6)|LbedC3Q&R-Q{1uq;qJZ0pgf7?`{0A`rx*$e^JrsSIjYp+0`ye) zpLKAtuHPUzmCZzY5^?{4b8V`E6(CBFxI>7vE1v@ZSk75;odZ=8N(WI)8`mM_6aI=hPg;+Pte zFQCQAz=jGez0ug1onM{cZ$2IyykQb3ywq|?LfD$IUH|HB3=UEG{=fAl;2Zyp<%4*&oF 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 0000000000000000000000000000000000000000..d6e3db3d53de111269ab895a4d884148d9685b18 GIT binary patch literal 15865 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 V-Ri@%dXGwlzP6E8frkCv{{?`wE2{ti literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7f958daf50d5d3d6d5f95c2b9e381239fcce9829 GIT binary patch literal 15210 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6d0820e63d012cf05e43d336e8020fcfeb424c47 GIT binary patch literal 9095 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f47ef3d3f67cb5c2c96d1a605353b8df8e817f74 GIT binary patch literal 12932 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 **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." %}