Live Trading working tested at Binance

This commit is contained in:
Luis Fernando Molina committed 2020-02-07 16:50:11 +01:00
1 parent 1ba29a7bad
commit 15b8a9044d
63 files changed
+1675 -1970

No files matched your search

+2 -4
View File
@@ -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)
}
}
+2 -2
View File
@@ -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()
@@ -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) {
+3 -3
View File
@@ -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)
}
}
}
+7 -1
View File
@@ -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++) {
+6 -6
View File
@@ -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->"
},
@@ -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);
}
}
@@ -668,7 +668,7 @@
let imagePosition = {
x: recordPoint4.x + imageOffset.x,
y: recordPoint4.y + imageOffset.y
y: 0
}
imagePosition = canvas.chartSpace.viewport.fitIntoVisibleArea(imagePosition)
+69 -307
View File
@@ -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);
}
}
+1 -1
View File
@@ -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));
+160
View File
@@ -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)
}
};
+2 -2
View File
@@ -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);
+25 -15
View File
@@ -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
+4 -4
View File
@@ -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)
+24 -260
View File
@@ -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 {
+98 -155
View File
@@ -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
+2 -738
View File
@@ -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"
}
}
}
}
}
}
+6 -10
View File
@@ -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"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

+17
View File
@@ -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."
+17
View File
@@ -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."
+1 -1
View File
@@ -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."
+1 -1
View File
@@ -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."
+15 -15
View File
@@ -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&mdash;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"
+15 -15
View File
@@ -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&mdash;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"
+1 -1
View File
@@ -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."
+1 -1
View File
@@ -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."
+210 -210
View File
@@ -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
+21 -21
View File
@@ -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
#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
+2 -2
View File
@@ -47,11 +47,11 @@
{% endif %}
{% endfor %}
{% endfor %}
<!--
<p class="external">
<a href="#" id="collapseAll">Collapse All</a> | <a href="#" id="expandAll">Expand All</a>
</p>
-->
</ul>
<!-- this highlights the active parent class in the navgoco sidebar. this is critical so that the parent expands when you're viewing a page. This must appear below the sidebar code above. Otherwise, if placed inside customscripts.js, the script runs before the sidebar code runs and the class never gets inserted.-->
@@ -89,14 +89,3 @@ Select *Configure Base Asset* on the menu to access the configuration.
{% endif %}
{% if include.starting != "" %}
{{include.starting}} Starting {{preposition}} {{title}}
<!-- STARTING starts -->
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<!-- STARTING ends -->
{% endif %}
+1 -14
View File
@@ -1,7 +1,7 @@
<!-- TITLE AND DEFINITION starts -->
{% assign title = "Fee Structure" %}
{% assign definition = site.data.trading_system.free_structure %}
{% assign definition = site.data.trading_system.fee_structure %}
{% assign preposition = "a" %}
<!-- TITLE AND DEFINITION ends -->
@@ -40,8 +40,6 @@
{% if include.content != "n" %}
**{{ definition }}**
<!-- CONTENT starts -->
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}}
<!-- STARTING starts -->
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<!-- STARTING ends -->
{% endif %}
@@ -1,7 +1,7 @@
<!-- TITLE AND DEFINITION starts -->
{% assign title = "Quoted Asset" %}
{% assign definition = site.data.trading_system.quote_asset %}
{% assign definition = site.data.trading_system.quoted_asset %}
{% assign preposition = "a" %}
<!-- TITLE AND DEFINITION ends -->
+1 -1
View File
@@ -1,7 +1,7 @@
<!-- TITLE AND DEFINITION starts -->
{% assign title = "Slippage" %}
{% assign definition = site.data.trading_system.slipage %}
{% assign definition = site.data.trading_system.slippage %}
{% assign preposition = "a" %}
<!-- TITLE AND DEFINITION ends -->
+1 -1
View File
@@ -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;
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

+4 -20
View File
@@ -4,32 +4,16 @@ sidebar: suite_sidebar
permalink: index.html
---
The Superalgos Platform provides a visual environment for developing, testing and automating crypto-trading strategies.
## About this Documentation
**The platform's vocation is to become the most comprehensive data processing and trading automation system available to all traders, data scientists, analysts and crypto holders.**
This documentation is currently work in progress and corresponds to an unreleased version of the Superalgos Suite, still in development. The system has experienced a major shift on multiple fronts, and changes are currently being documented.
The platform allows designing and automating a wide variety of strategies, as well as augmenting your *intelligence* by handling and creating your own datasets, with great flexibility.
This is not a simple plug-and-play bot. However, we try our best to make light use of the platform as easy as possible, for instance, for loading existing strategies and trading live right off the bat, with minimal user input.
[![Illustration](https://user-images.githubusercontent.com/13994516/63528460-4550ae80-c503-11e9-8db6-22995e0b9c16.gif)](https://user-images.githubusercontent.com/13994516/63528460-4550ae80-c503-11e9-8db6-22995e0b9c16.gif)
## About this Manual
This manual is arranged in three sections:
* **Basic Use** helps you set up the platform, teaches you the very basic operation, and takes you by the hand until you are ready to start trading live with one of the open-source strategies currently available.
* **Power Use** takes you through the learning curve to modify strategies and design your own, and shows you what's under the hood of the platform so that you may take control of all the resources that make up a successful trader.
* **Data Mining** pumps the potential for growing your *intelligence* through the roof by teaching you how to process data and build your own datasets. Analysing and processing data is what turns data into *information*. Using information effectively is what realises *intelligence*.
You may access each topic by using the menu on the right. We try our best to organize the content in a logical order, so that you are exposed to information incrementally, as you need it while you go through the learning curve.
The Hierarchies Directory feature extensive information about all the data structures handled by the designer, including the new, unreleased ones.
## Beta Stage Notice
This is a pre-release in beta stage. The platform is under heavy development. We appreciate your help in testing the platform and reporting any errors you may experience. We will keep improving user experience with your feedback.
> **WARNING:** The Superalgos Platform is at a very early stage of development. As such, errors may occur at any point, including errors that may cause you to lose money. You are responsible for taking all precautions before starting trading live. Make sure you forward-test with small amounts of money, the kind you can afford losing. Trade live at your own risk.
{% include important.html content="The Superalgos Platform is at a very early stage of development. As such, errors may occur at any point, including errors that may cause you to lose money. You are responsible for taking all precautions before starting trading live. Make sure you forward-test with small amounts of money, the kind you can afford losing. Trade live at your own risk." %}
@@ -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." %}
<table class='hierarchyTable'>
<thead>
<tr>
<th>
<a href='#charting-space' data-toggle='tooltip' data-original-title='{{site.data.charting_space.charting_space}}'><img src='images/icons/charting-space.png' /><br />Charting Space</a>
</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<img src='images/icons/tree-connector-fork.png' />
</td>
<td>
<a href='#viewport' data-toggle='tooltip' data-original-title='{{site.data.charting_space.viewport}}'><img src='images/icons/viewport.png' /><br />Viewport</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>
<img src='images/icons/tree-connector-elbow.png' />
</td>
<td>
<a href='#time-machine' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_machine}}'><img src='images/icons/time-machine.png' /><br />Time Machine</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<img src='images/icons/tree-connector-fork.png' />
</td>
<td>
<a href='#time-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_scale}}'><img src='images/icons/time-scale.png' /><br />Time Scale</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<img src='images/icons/tree-connector-fork.png' />
</td>
<td>
<a href='#rate-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.rate_scale}}'><img src='images/icons/rate-scale.png' /><br />Rate Scale</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<img src='images/icons/tree-connector-fork.png' />
</td>
<td>
<a href='#time-frame-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_frame_scale}}'><img src='images/icons/time-frame-scale.png' /><br />Time Frame Scale</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>
<img src='images/icons/tree-connector-elbow.png' />
</td>
<td>
<a href='#timeline-chart' data-toggle='tooltip' data-original-title='{{site.data.charting_space.timeline_chart}}'><img src='images/icons/timeline-chart.png' /><br />Timeline Chart</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<img src='images/icons/tree-connector-fork.png' />
</td>
<td>
<a href='#rate-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.rate_scale}}'><img src='images/icons/rate-scale.png' /><br />Rate Scale</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<img src='images/icons/tree-connector-fork.png' />
</td>
<td>
<a href='#time-frame-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_frame_scale}}'><img src='images/icons/time-frame-scale.png' /><br />Time Frame Scale</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<img src='images/icons/tree-connector-elbow.png' />
</td>
<td>
<a href='#layers-manager' data-toggle='tooltip' data-original-title='{{site.data.charting_space.layers_manager}}'><img src='images/icons/layers-manager.png' /><br />Layers Manager</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>
<img src='images/icons/tree-connector-elbow.png' />
</td>
<td>
<a href='#layer' data-toggle='tooltip' data-original-title='{{site.data.charting_space.layer}}'><img src='images/icons/layer.png' /><br />Layer</a>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
## Charting Space
<img src='images/icons/150-charting-space.png' />
**{{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
<img src='images/icons/150-viewport.png' />
**{{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&mdash;in general&mdash;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
<img src='images/icons/150-time-machine.png' />
**{{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
<img src='images/icons/150-time-scale.png' />
**{{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
<img src='images/icons/150-rate-scale.png' />
**{{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&mdash;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&mdash;synchronized in datetime by a shared time machine&mdash;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 <kbd>Shift</kbd> 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
<img src='images/icons/150-time-frame-scale.png' />
**{{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
<img src='images/icons/150-timeline-chart.png' />
**{{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&mdash;the main and foremost resource&mdash;as well as any other indicator, study or&mdash;in general&mdash;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
<img src='images/icons/150-layers-manager.png' />
**{{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
<img src='images/icons/150-layer.png' />
**{{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 <a href="suite-references.html" data-toggle="tooltip" data-original-title="{{site.data.concepts.reference}}">reference</a> with the <a href="suite-network-hierarchy.html#data-product" data-toggle="tooltip" data-original-title="{{site.data.network.data_product}}">data product</a> 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.
@@ -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." %}
<table class='hierarchyTable'>
<thead>
<tr>
@@ -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." %}
+454 -82
View File
@@ -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." %}
<table class='hierarchyTable'>
<thead>
@@ -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&mdash;while autonomous in their particular jobs&mdash;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&mdash;while autonomous in their particular jobs&mdash;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&mdash; 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&mdash;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&mdash;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&mdash;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&mdash;by default&mdash;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 | <span style="display: block; background: RGB(48, 48, 54); border: 1px solid black;">&nbsp;</span> |
| ```LIGHT``` | 234, 226, 222 | <span style="display: block; background: RGB(234, 226, 222); border: 1px black; border: 1px solid black;">&nbsp;</span> |
| ```GREY``` | 150, 150, 150 | <span style="display: block; background: RGB(150, 150, 150); border: 1px solid black;">&nbsp;</span> |
| ```LIGHT_GREY``` | 247, 247, 247 | <span style="display: block; background: RGB(247, 247, 247); border: 1px solid black;">&nbsp;</span> |
| ```WHITE``` | 255, 255, 255 | <span style="display: block; background: RGB(255, 255, 255); border: 1px solid black;">&nbsp;</span> |
| ```BLACK``` | 0, 0, 0 | <span style="display: block; background: RGB(0, 0, 0); border: 1px solid black;">&nbsp;</span> |
| ```GOLDEN_ORANGE``` | 240, 162, 2 | <span style="display: block; background: RGB(240, 162, 2); border: 1px solid black;">&nbsp;</span> |
| ```RUSTED_RED``` | 204, 88, 53 | <span style="display: block; background: RGB(204, 88, 53); border: 1px solid black;">&nbsp;</span> |
| ```GREEN``` | 188, 214, 67 | <span style="display: block; background: RGB(188, 214, 67); border: 1px solid black;">&nbsp;</span> |
| ```RED``` | 223, 70, 60 | <span style="display: block; background: RGB(223, 70, 60); border: 1px solid black;">&nbsp;</span> |
| ```PATINATED_TURQUOISE``` | 27, 153, 139 | <span style="display: block; background: RGB(27, 153, 139); border: 1px solid black;">&nbsp;</span> |
| ```TITANIUM_YELLOW``` | 244, 228, 9 | <span style="display: block; background: RGB(244, 228, 9); border: 1px solid black;">&nbsp;</span> |
| ```MANGANESE_PURPLE``` | 91,80, 122 | <span style="display: block; background: RGB(91,80, 122); border: 1px solid black;">&nbsp;</span> |
| ```TURQUOISE``` | 74, 207, 217 | <span style="display: block; background: RGB(74, 207, 217); border: 1px solid black;">&nbsp;</span> |
| ```DARK_TURQUOISE``` | 2, 149, 170 | <span style="display: block; background: RGB(2, 149, 170); border: 1px solid black;">&nbsp;</span> |
* ```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&mdash;in such cases&mdash;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.
@@ -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
+4 -4
View File
@@ -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
+4 -2
View File
@@ -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." %}
<table class='hierarchyTable'><thead><tr><th><a href='#network' data-toggle='tooltip' data-original-title='{{site.data.network.network}}'><img src='images/icons/network.png' /><br />Network</a></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#network-node' data-toggle='tooltip' data-original-title='{{site.data.network.network_node}}'><img src='images/icons/network-node.png' /><br />Network Node</a></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#task-manager' data-toggle='tooltip' data-original-title='{{site.data.network.task_manager}}'><img src='images/icons/task-manager.png' /><br />Task Manager</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
@@ -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.
+6 -8
View File
@@ -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="" %}
{% include /trading_system/fee-structure.md heading="###" icon="" adding="" configuring="" starting="" content="" definition="regular" table="y" %}
+11 -11
View File
@@ -164,14 +164,14 @@ permalink: suite-test.html
</table>
<table class='hierarchyTable'><thead><tr><th><a href='#charting-system' data-toggle='tooltip' data-original-title='{{site.data.charting_system.charting_system}}'><img src='images/icons/charting-system.png' /><br />Charting System</a></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#viewport' data-toggle='tooltip' data-original-title='{{site.data.charting_system.viewport}}'><img src='images/icons/viewport.png' /><br />Viewport</a></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#time-machine' data-toggle='tooltip' data-original-title='{{site.data.charting_system.time_machine}}'><img src='images/icons/time-machine.png' /><br />Time Machine</a></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#time-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_system.time_scale}}'><img src='images/icons/time-scale.png' /><br />Time Scale</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#rate-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_system.rate_scale}}'><img src='images/icons/rate-scale.png' /><br />Rate Scale</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#time-frame-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_system.time_frame_scale}}'><img src='images/icons/time-frame-scale.png' /><br />Time Frame Scale</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#timeline-chart' data-toggle='tooltip' data-original-title='{{site.data.charting_system.timeline_chart}}'><img src='images/icons/timeline-chart.png' /><br />Timeline Chart</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#rate-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_system.rate_scale}}'><img src='images/icons/rate-scale.png' /><br />Rate Scale</a></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#time-frame-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_system.time_frame_scale}}'><img src='images/icons/time-frame-scale.png' /><br />Time Frame Scale</a></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#layers-manager' data-toggle='tooltip' data-original-title='{{site.data.charting_system.layers_manager}}'><img src='images/icons/layers-manager.png' /><br />Layers Manager</a></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#layer' data-toggle='tooltip' data-original-title='{{site.data.charting_system.layer}}'><img src='images/icons/layer.png' /><br />Layer</a></td><td></td><td></td><td></td><td></td></tr></tbody></table>
<table class='hierarchyTable'><thead><tr><th><a href='#charting-space' data-toggle='tooltip' data-original-title='{{site.data.charting_space.charting_space}}'><img src='images/icons/charting-space.png' /><br />Charting Space</a></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#viewport' data-toggle='tooltip' data-original-title='{{site.data.charting_space.viewport}}'><img src='images/icons/viewport.png' /><br />Viewport</a></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#time-machine' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_machine}}'><img src='images/icons/time-machine.png' /><br />Time Machine</a></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#time-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_scale}}'><img src='images/icons/time-scale.png' /><br />Time Scale</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#rate-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.rate_scale}}'><img src='images/icons/rate-scale.png' /><br />Rate Scale</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#time-frame-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_frame_scale}}'><img src='images/icons/time-frame-scale.png' /><br />Time Frame Scale</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#timeline-chart' data-toggle='tooltip' data-original-title='{{site.data.charting_space.timeline_chart}}'><img src='images/icons/timeline-chart.png' /><br />Timeline Chart</a></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#rate-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.rate_scale}}'><img src='images/icons/rate-scale.png' /><br />Rate Scale</a></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td><img src='images/icons/tree-connector-fork.png' /></td><td><a href='#time-frame-scale' data-toggle='tooltip' data-original-title='{{site.data.charting_space.time_frame_scale}}'><img src='images/icons/time-frame-scale.png' /><br />Time Frame Scale</a></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#layers-manager' data-toggle='tooltip' data-original-title='{{site.data.charting_space.layers_manager}}'><img src='images/icons/layers-manager.png' /><br />Layers Manager</a></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td><img src='images/icons/tree-connector-elbow.png' /></td><td><a href='#layer' data-toggle='tooltip' data-original-title='{{site.data.charting_space.layer}}'><img src='images/icons/layer.png' /><br />Layer</a></td><td></td><td></td><td></td><td></td></tr></tbody></table>
@@ -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." %}
<table class='hierarchyTable'>
<thead>