diff --git a/AppSchema.json b/AppSchema.json
index 4c0de40c7..204c68aab 100644
--- a/AppSchema.json
+++ b/AppSchema.json
@@ -4426,8 +4426,6 @@
"menuItems": [
{
"action": "Add UI Object",
- "disableIfPropertyIsDefined": true,
- "propertyToCheckFor": "cryptoExchanges",
"label": "Add Crypto Exchanges",
"relatedUiObject": "Crypto Exchanges",
"actionFunction": "payload.onMenuItemClick"
@@ -4449,8 +4447,8 @@
"properties": [
{
"name": "cryptoExchanges",
- "type": "node",
- "childType": "Network Node"
+ "type": "array",
+ "childType": "Crypto Exchanges"
}
]
},
@@ -4463,6 +4461,15 @@
"relatedUiObject": "Crypto Exchange",
"actionFunction": "payload.onMenuItemClick"
},
+ {
+ "action": "Add Missing Crypto Exchanges",
+ "label": "Add Missing Exchanges",
+ "askConfirmation": true,
+ "confirmationLabel": "Confirm to Proceed",
+ "workDoneLabel": "Done",
+ "relatedUiObject": "Crypto Exchange",
+ "actionFunction": "payload.onMenuItemClick"
+ },
{
"action": "Delete UI Object",
"askConfirmation": true,
@@ -4473,6 +4480,7 @@
"actionFunction": "payload.onMenuItemClick"
}
],
+ "isTitleAllwaysVisible": true,
"addLeftIcons": true,
"level": 1,
"icon": "crypto-exchanges",
@@ -4569,43 +4577,43 @@
],
"alternativeIcons": [
{
- "codeName": "Bitfinex",
+ "codeName": "bitfinex",
"iconName": "bitfinex"
},
{
- "codeName": "Binance",
+ "codeName": "binance",
"iconName": "binance"
},
{
- "codeName": "Kucoin",
+ "codeName": "kucoin",
"iconName": "kucoin"
},
{
- "codeName": "Bittrex",
+ "codeName": "bittrex",
"iconName": "bittrex"
},
{
- "codeName": "Kraken",
+ "codeName": "kraken",
"iconName": "kraken"
},
{
- "codeName": "Poloniex",
+ "codeName": "poloniex",
"iconName": "poloniex"
},
{
- "codeName": "Upbit",
+ "codeName": "upbit",
"iconName": "upbit"
},
{
- "codeName": "Bitmex",
+ "codeName": "bitmex",
"iconName": "bitmex"
},
{
- "codeName": "Liquid",
+ "codeName": "liquid",
"iconName": "liquid"
},
{
- "codeName": "HitBTC",
+ "codeName": "hitbtc",
"iconName": "hitbtc"
}
]
@@ -4613,12 +4621,28 @@
{
"type": "Exchange Assets",
"menuItems": [
+ {
+ "action": "Config Exchange Assets",
+ "actionFunction": "uiObject.configEditor.activate",
+ "label": "Config Exchange Assets",
+ "iconPathOn": "html",
+ "iconPathOff": "html"
+ },
{
"action": "Add UI Object",
"label": "Add Asset",
"relatedUiObject": "Asset",
"actionFunction": "payload.onMenuItemClick"
},
+ {
+ "action": "Add Missing Assets",
+ "label": "Add Missing Assets",
+ "askConfirmation": true,
+ "confirmationLabel": "Confirm to Proceed",
+ "workDoneLabel": "Done",
+ "relatedUiObject": "Asset",
+ "actionFunction": "payload.onMenuItemClick"
+ },
{
"action": "Delete UI Object",
"askConfirmation": true,
@@ -4629,6 +4653,12 @@
"actionFunction": "payload.onMenuItemClick"
}
],
+ "editors": {
+ "config": true
+ },
+ "initialValues": {
+ "code": "{ \n\"addMissingAssets\": { \"quotedAsset\": \"USDT\"} \n}"
+ },
"addLeftIcons": true,
"level": 3,
"icon": "exchange-assets",
@@ -4640,8 +4670,7 @@
{
"name": "assets",
"type": "array",
- "childType": "Asset",
- "autoAdd": true
+ "childType": "Asset"
}
]
},
@@ -4749,12 +4778,28 @@
{
"type": "Exchange Markets",
"menuItems": [
+ {
+ "action": "Config Market",
+ "actionFunction": "uiObject.configEditor.activate",
+ "label": "Config Market",
+ "iconPathOn": "html",
+ "iconPathOff": "html"
+ },
{
"action": "Add UI Object",
"label": "Add Market",
"relatedUiObject": "Market",
"actionFunction": "payload.onMenuItemClick"
},
+ {
+ "action": "Add Missing Markets",
+ "label": "Add Missing Markets",
+ "askConfirmation": true,
+ "confirmationLabel": "Confirm to Proceed",
+ "workDoneLabel": "Done",
+ "relatedUiObject": "Market",
+ "actionFunction": "payload.onMenuItemClick"
+ },
{
"action": "Delete UI Object",
"askConfirmation": true,
@@ -4765,6 +4810,12 @@
"actionFunction": "payload.onMenuItemClick"
}
],
+ "editors": {
+ "config": true
+ },
+ "initialValues": {
+ "code": "{ \n\"codeName\": \"Write here the code name of the Market here.\"\n}"
+ },
"addLeftIcons": true,
"level": 3,
"icon": "exchange-markets",
@@ -4776,14 +4827,20 @@
{
"name": "markets",
"type": "array",
- "childType": "Market",
- "autoAdd": true
+ "childType": "Market"
}
]
},
{
"type": "Market",
"menuItems": [
+ {
+ "action": "Config Market",
+ "actionFunction": "uiObject.configEditor.activate",
+ "label": "Config Market",
+ "iconPathOn": "html",
+ "iconPathOff": "html"
+ },
{
"action": "Add UI Object",
"disableIfPropertyIsDefined": true,
@@ -4806,13 +4863,6 @@
"relatedUiObject": "Super Action",
"actionFunction": "payload.onMenuItemClick"
},
- {
- "action": "Config Market",
- "actionFunction": "uiObject.configEditor.activate",
- "label": "Config Market",
- "iconPathOn": "html",
- "iconPathOff": "html"
- },
{
"action": "Delete UI Object",
"askConfirmation": true,
@@ -4975,8 +5025,7 @@
{
"name": "assets",
"type": "array",
- "childType": "Exchange Account Asset",
- "autoAdd": true
+ "childType": "Exchange Account Asset"
}
],
"isPersonalData": true
diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/CCXTFunctions.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/CCXTFunctions.js
new file mode 100644
index 000000000..385d6550b
--- /dev/null
+++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/CCXTFunctions.js
@@ -0,0 +1,196 @@
+function newCCXTFunctions () {
+ thisObject = {
+ addMissingExchanges: addMissingExchanges,
+ addMissingAssets: addMissingAssets,
+ addMissingMarkets: addMissingMarkets
+ }
+
+ return thisObject
+
+ function addMissingExchanges (node, functionLibraryUiObjectsFromNodes) {
+ currentExchanges = new Map()
+ let parent = node.payload.parentNode
+ if (parent !== undefined) {
+ for (let i = 0; i < parent.cryptoExchanges.length; i++) {
+ let cryptoExchanges = parent.cryptoExchanges[i]
+ for (let j = 0; j < cryptoExchanges.exchanges.length; j++) {
+ let exchange = cryptoExchanges.exchanges[j]
+ let codeName = loadPropertyFromNodeConfig(exchange.payload, 'codeName')
+ currentExchanges.set(codeName, exchange)
+ }
+ }
+ }
+
+ let params = {
+ method: 'listExchanges',
+ has: {
+ fetchOHLCV: true,
+ fetchMarkets: true
+ }
+ }
+
+ callServer(JSON.stringify(params), 'CCXT', onResponse)
+
+ function onResponse (err, data) {
+ if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
+ node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
+ return
+ }
+
+ let exchanges = JSON.parse(data)
+ for (let i = 0; i < exchanges.length; i++) {
+ let exchange = exchanges[i]
+ let existingExchange = currentExchanges.get(exchange.id)
+ if (existingExchange === undefined) {
+ let newExchange = functionLibraryUiObjectsFromNodes.addUIObject(node, 'Crypto Exchange')
+ newExchange.name = exchange.name
+ newExchange.code = '{ \n\"codeName\": \"' + exchange.id + '\"\n}'
+ newExchange.payload.floatingObject.collapseToggle()
+ newExchange.exchangeAssets.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
+ newExchange.exchangeMarkets.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
+ newExchange.exchangeAccounts.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
+ newExchange.exchangeAssets.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_050X
+ newExchange.exchangeMarkets.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_100X
+ newExchange.exchangeAccounts.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_025X
+ }
+ }
+ }
+ }
+
+ function addMissingAssets (node, functionLibraryUiObjectsFromNodes) {
+ if (node.payload.parentNode === undefined) { return }
+
+ let currentAssets = new Map()
+ for (let j = 0; j < node.assets.length; j++) {
+ let asset = node.assets[j]
+ let codeName = loadPropertyFromNodeConfig(asset.payload, 'codeName')
+ currentAssets.set(codeName, asset)
+ }
+
+ let exchangeId = loadPropertyFromNodeConfig(node.payload.parentNode.payload, 'codeName')
+
+ try {
+ let params = {
+ exchangeId: exchangeId,
+ method: 'fetchMarkets'
+ }
+ callServer(JSON.stringify(params), 'CCXT', onResponse)
+
+ function onResponse (err, data) {
+ if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
+ node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
+ return
+ }
+ let queryParams = loadPropertyFromNodeConfig(node.payload, 'addMissingAssets')
+
+ let markets = JSON.parse(data)
+ for (let i = 0; i < markets.length; i++) {
+ let market = markets[i]
+
+ if (queryParams !== undefined) {
+ if (queryParams.baseAsset !== undefined) {
+ if (market.baseId.indexOf(queryParams.baseAsset) >= 0) {
+ continue
+ }
+ }
+ if (queryParams.quotedAsset !== undefined) {
+ if (market.quoteId.indexOf(queryParams.quotedAsset) >= 0) {
+ continue
+ }
+ }
+ }
+ if (currentAssets.get(market.baseId) === undefined) {
+ addAsset(market.baseId)
+ currentAssets.set(market.baseId, market.baseId)
+ }
+ if (currentAssets.get(market.quoteId) === undefined) {
+ addAsset(market.quoteId)
+ currentAssets.set(market.quoteId, market.quoteId)
+ }
+
+ function addAsset (name) {
+ let newAsseet = functionLibraryUiObjectsFromNodes.addUIObject(node, 'Asset')
+ newAsseet.name = name
+ newAsseet.code = '{ \n\"codeName\": \"' + name + '\"\n}'
+ newAsseet.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_360
+ newAsseet.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_200X
+ }
+ }
+ }
+ } catch (err) {
+ node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
+ console.log(err.stack)
+ }
+ }
+
+ function addMissingMarkets (node, functionLibraryUiObjectsFromNodes) {
+ if (node.payload.parentNode === undefined) { return }
+ if (node.payload.parentNode.exchangeAssets === undefined) { return }
+
+ let currentAssets = new Map()
+ let exchangeAssets = node.payload.parentNode.exchangeAssets
+ for (let j = 0; j < exchangeAssets.assets.length; j++) {
+ let asset = exchangeAssets.assets[j]
+ let codeName = loadPropertyFromNodeConfig(asset.payload, 'codeName')
+ currentAssets.set(codeName, asset)
+ }
+
+ let currentMarkets = new Map()
+ let exchangeMarkets = node
+ for (let j = 0; j < exchangeMarkets.markets.length; j++) {
+ let asset = exchangeMarkets.markets[j]
+ let codeName = loadPropertyFromNodeConfig(asset.payload, 'codeName')
+ currentMarkets.set(codeName, asset)
+ }
+
+ let exchangeId = loadPropertyFromNodeConfig(node.payload.parentNode.payload, 'codeName')
+
+ try {
+ let params = {
+ exchangeId: exchangeId,
+ method: 'fetchMarkets'
+ }
+ callServer(JSON.stringify(params), 'CCXT', onResponse)
+
+ function onResponse (err, data) {
+ if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
+ node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
+ return
+ }
+
+ let markets = JSON.parse(data)
+ for (let i = 0; i < markets.length; i++) {
+ let market = markets[i]
+ let baseAsset = currentAssets.get(market.baseId)
+ let quotedAsset = currentAssets.get(market.quoteId)
+
+ if (baseAsset === undefined) {
+ continue
+ }
+ if (quotedAsset === undefined) {
+ continue
+ }
+
+ if (currentMarkets.get(market.symbol) === undefined) {
+ addMarket(market.symbol, baseAsset, quotedAsset)
+ }
+
+ function addMarket (name, baseAsset, quotedAsset) {
+ let newMarket = functionLibraryUiObjectsFromNodes.addUIObject(node, 'Market')
+ newMarket.name = name
+ newMarket.code = '{ \n\"codeName\": \"' + name + '\"\n}'
+ newMarket.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
+ newMarket.baseAsset.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_45
+ newMarket.quotedAsset.payload.floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_45
+ newMarket.payload.floatingObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_050X
+ newMarket.baseAsset.payload.referenceParent = baseAsset
+ newMarket.quotedAsset.payload.referenceParent = quotedAsset
+ }
+ }
+ }
+ } catch (err) {
+ node.payload.uiObject.setErrorMessage('Failed to Fetch Assets from the Exchange')
+ console.log(err.stack)
+ }
+ }
+}
diff --git a/CanvasApp/DesignSpace/Workspace/Workspace.js b/CanvasApp/DesignSpace/Workspace/Workspace.js
index a844763d3..136cce8fe 100644
--- a/CanvasApp/DesignSpace/Workspace/Workspace.js
+++ b/CanvasApp/DesignSpace/Workspace/Workspace.js
@@ -57,6 +57,7 @@ function newWorkspace () {
let functionLibraryShortcutKeys = newShortcutKeys()
let functionLibraryOnFocus = newOnFocus()
let functionLibrarySuperScripts = newSuperScriptsFunctions()
+ let functionLibraryCCXTFunctions = newCCXTFunctions()
thisObject.nodeChildren = newNodeChildren()
@@ -88,7 +89,7 @@ function newWorkspace () {
functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode)
thisObject.enabled = true
- setInterval(saveWorkspace, 30000)
+ setInterval(saveWorkspace, 10000)
} catch (err) {
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
}
@@ -370,6 +371,22 @@ function newWorkspace () {
functionLibraryTaskFunctions.stopAllExchangeTasks(payload.node, functionLibraryProtocolNode)
}
break
+ case 'Add Missing Crypto Exchanges':
+ {
+ functionLibraryCCXTFunctions.addMissingExchanges(payload.node, functionLibraryUiObjectsFromNodes)
+ }
+ break
+ case 'Add Missing Assets':
+ {
+ functionLibraryCCXTFunctions.addMissingAssets(payload.node, functionLibraryUiObjectsFromNodes)
+ }
+ break
+ case 'Add Missing Markets':
+ {
+ functionLibraryCCXTFunctions.addMissingMarkets(payload.node, functionLibraryUiObjectsFromNodes)
+ }
+ break
+
case 'Run Session':
{
functionLibrarySessionFunctions.runSession(payload.node, functionLibraryProtocolNode, callBackFunction)
diff --git a/CanvasApp/FloatingSpace/UiObjectConstructor.js b/CanvasApp/FloatingSpace/UiObjectConstructor.js
index 9d6f5bb78..82077883e 100644
--- a/CanvasApp/FloatingSpace/UiObjectConstructor.js
+++ b/CanvasApp/FloatingSpace/UiObjectConstructor.js
@@ -84,7 +84,7 @@ function newUiObjectConstructor () {
for (let j = 0; j < menuItems.length; j++) {
let item = menuItems[j]
item.angle = undefined
- if (item.action.indexOf('Add ') >= 0 && item.action.indexOf('Missing') >= 0) {
+ if (item.action.indexOf('Add Missing Children') >= 0) {
item.action = 'Add Missing Children'
}
if (item.action.indexOf('Delete ') >= 0) {
diff --git a/WebServer/.env b/WebServer/.env
index 7402606ae..f7a8d4bc0 100644
--- a/WebServer/.env
+++ b/WebServer/.env
@@ -18,4 +18,6 @@ INTER_PROCESS_FILES_PATH=../Inter-Process-Files
STORAGE_PATH=../Data-Storage
PLOTTERS_PATH=../Legacy-Code
PATH_TO_WEB_SERVER =./
+APP_SCHEMA_PATH=../
+WORKSPACE_PATH=../
diff --git a/WebServer/Scripts/AppLoader.js b/WebServer/Scripts/AppLoader.js
index f2d156069..6cd4f916d 100644
--- a/WebServer/Scripts/AppLoader.js
+++ b/WebServer/Scripts/AppLoader.js
@@ -29,7 +29,6 @@ function newAppLoader() {
'AppSchema.js',
'Workspace.js',
- 'MQService',
'ChartingSpace/EdgeEditor.js',
'ChartingSpace/ChartUtilities.js',
@@ -51,7 +50,8 @@ function newAppLoader() {
'DesignSpace/Workspace/FunctionLibraries/ShortcutKeys.js',
'DesignSpace/Workspace/FunctionLibraries/OnFocus.js',
'DesignSpace/Workspace/FunctionLibraries/SuperScripts.js',
-
+ 'DesignSpace/Workspace/FunctionLibraries/CCXTFunctions.js',
+
'Utilities/RoundedCornersBackground.js',
'Panels/LayersPanel.js',
diff --git a/WebServer/index.html b/WebServer/index.html
index e0254bd0c..ec4180c4c 100644
--- a/WebServer/index.html
+++ b/WebServer/index.html
@@ -19,7 +19,6 @@
-