Thank you. I was able to get the flow working all the way to a dashboard table.
One problem that I experienced is that when one API node encounters a type of error/issue that interrupts the flow of msg (like a "timeout" in pulling data from a particular exchange) everything halts - I imagine (one reason) because the JOIN node is waiting to get all of its parts.
[
{
"id": "19925c368c4d96ea",
"type": "tab",
"label": "CRYPTO",
"disabled": false,
"info": "",
"env": []
},
{
"id": "4da7c11af13cd77b",
"type": "ccxt-api-v2",
"z": "19925c368c4d96ea",
"name": "Gate.io : get_tickers",
"exchange": [
"gateio"
],
"allexchanges": false,
"apitype": "customAPI",
"customapitype": "public_spot",
"api": "public_spot_get_tickers",
"apiprivate": "false",
"filtermarkets": "",
"filtermarketsType": "str",
"symbol": "",
"symbolType": "str",
"limit": "",
"limitType": "num",
"since": "",
"sinceType": "datepick",
"timeframe": "1m",
"timeframeType": "timeframeList",
"ordertype": "limit",
"orderside": "buy",
"amount": "",
"amountType": "num",
"orderprice": "",
"orderpriceType": "num",
"orderid": "",
"orderidType": "str",
"code": "",
"address": "",
"tag": "",
"apisecrets": "",
"apipayload": "",
"apipayloadType": "none",
"x": 430,
"y": 240,
"wires": [
[
"9eb6f69be4d296c5"
]
]
},
{
"id": "a06002f4195586c6",
"type": "function",
"z": "19925c368c4d96ea",
"name": "in",
"func": "var l_coin=msg.payload.currency;\nvar listed=msg.payload.exchange;\n\nflow.set('listed', listed);\nflow.set('l_coin', l_coin);\nflow.set('f_coin', l_coin + '/USD');\nflow.set('g_coin', l_coin + '_USDT');\nflow.set('b_coin', l_coin + 'USDT');\nflow.set('c_coin', l_coin + '-USD');\n\nmsgF={};\nmsgG={};\nmsgB={};\nmsgC={};\nreturn [msgF,msgG,msgB,msgC];",
"outputs": 4,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 170,
"y": 260,
"wires": [
[
"437e6d9c91e565dd"
],
[
"4da7c11af13cd77b"
],
[
"1feedb90268d108a"
],
[
"27d81f8b5ae5b04d"
]
]
},
{
"id": "21f4410c78d3927b",
"type": "inject",
"z": "19925c368c4d96ea",
"name": "alert",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "{\"type\":\"new_coin\",\"message\":\"ETC has been listed on Gate.io!\",\"currency\":\"GALA\",\"exchange\":\"Gate.io\"}",
"payloadType": "json",
"x": 130,
"y": 80,
"wires": [
[
"a06002f4195586c6"
]
]
},
{
"id": "437e6d9c91e565dd",
"type": "ccxt-api-v2",
"z": "19925c368c4d96ea",
"name": "FTX : get_markets",
"exchange": [
"ftx"
],
"allexchanges": false,
"apitype": "customAPI",
"customapitype": "public",
"api": "public_get_markets",
"apiprivate": "false",
"filtermarkets": "",
"filtermarketsType": "str",
"symbol": "",
"symbolType": "str",
"limit": "",
"limitType": "num",
"since": "",
"sinceType": "datepick",
"timeframe": "1m",
"timeframeType": "timeframeList",
"ordertype": "limit",
"orderside": "buy",
"amount": "",
"amountType": "num",
"orderprice": "",
"orderpriceType": "num",
"orderid": "",
"orderidType": "str",
"code": "",
"address": "",
"tag": "",
"apisecrets": "",
"apipayload": "",
"apipayloadType": "none",
"x": 420,
"y": 200,
"wires": [
[
"ab9f942c4f2f0686"
]
]
},
{
"id": "1feedb90268d108a",
"type": "ccxt-api-v2",
"z": "19925c368c4d96ea",
"name": "Binance : get_ticker",
"exchange": [
"binanceus"
],
"allexchanges": false,
"apitype": "customAPI",
"customapitype": "public",
"api": "public_get_ticker/24hr",
"apiprivate": "false",
"filtermarkets": "",
"filtermarketsType": "str",
"symbol": "",
"symbolType": "str",
"limit": "",
"limitType": "num",
"since": "",
"sinceType": "datepick",
"timeframe": "1m",
"timeframeType": "timeframeList",
"ordertype": "limit",
"orderside": "buy",
"amount": "",
"amountType": "num",
"orderprice": "",
"orderpriceType": "num",
"orderid": "",
"orderidType": "str",
"code": "",
"address": "",
"tag": "",
"apisecrets": "",
"apipayload": "",
"apipayloadType": "none",
"x": 430,
"y": 280,
"wires": [
[
"d7998371671437b3"
]
]
},
{
"id": "27d81f8b5ae5b04d",
"type": "ccxt-api-v2",
"z": "19925c368c4d96ea",
"name": "Coinbase: get_products",
"exchange": [
"coinbasepro"
],
"allexchanges": false,
"apitype": "customAPI",
"customapitype": "public",
"api": "public_get_products",
"apiprivate": "false",
"filtermarkets": "",
"filtermarketsType": "str",
"symbol": "",
"symbolType": "str",
"limit": "",
"limitType": "num",
"since": "",
"sinceType": "datepick",
"timeframe": "1m",
"timeframeType": "timeframeList",
"ordertype": "limit",
"orderside": "buy",
"amount": "",
"amountType": "num",
"orderprice": "",
"orderpriceType": "num",
"orderid": "",
"orderidType": "str",
"code": "",
"address": "",
"tag": "",
"apisecrets": "",
"apipayload": "",
"apipayloadType": "none",
"x": 430,
"y": 320,
"wires": [
[
"9b879d220b86ac80"
]
]
},
{
"id": "47a0bff56a4d9ccb",
"type": "join",
"z": "19925c368c4d96ea",
"name": "",
"mode": "custom",
"build": "object",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": "\\n",
"joinerType": "str",
"accumulate": false,
"timeout": "",
"count": "4",
"reduceRight": false,
"reduceExp": "",
"reduceInit": "",
"reduceInitType": "num",
"reduceFixup": "",
"x": 890,
"y": 260,
"wires": [
[
"11e8d8d04f29951e"
]
]
},
{
"id": "754311d56ac3585b",
"type": "function",
"z": "19925c368c4d96ea",
"name": "out",
"func": "msgTable = {};\nmsgAction = {};\n\n\nvar listed = flow.get('listed');\nvar l_coin = flow.get('l_coin');\nvar price; var change; var f_coin; var g_coin; var b_coin; var c_coin; var id; var token; var coin;\n\n if (msg.payload.ftx.price > 0) {\n price = msg.payload.ftx.price;\n change = (msg.payload.ftx.change * 100).toFixed(2); \n id = 739970400;\n token = \"bae88628-4c8a-4176-a43b-a8dbd5f4f82d00\";\n coin = msg.payload.ftx.coin;\n} \n else if (msg.payload.gate.price > 0) {\n price = msg.payload.gate.price; \n change = msg.payload.gate.change; \n id = 739970400;\n token = \"bae88628-4c8a-4176-a43b-a8dbd5f4f82d00\";\n coin = msg.payload.gate.coin;\n} \n else if (msg.payload.binance.price > 0) {\n price = msg.payload.binance.price; \n change = msg.payload.binance.change;\n id = 739970400;\n token = \"bae88628-4c8a-4176-a43b-a8dbd5f4f82d00\";\n coin = msg.payload.binance.coin;\n}\n\n\nif (msg.payload.ftx.price > 0) { f_coin = msg.payload.ftx.coin }\nif (msg.payload.gate.price > 0) { g_coin = msg.payload.gate.coin }\nif (msg.payload.binance.price > 0) { b_coin = msg.payload.binance.coin }\nc_coin = msg.payload.coinbase.coin;\n\n\nmsgTable.payload = { \"l_coin\": l_coin, \"listed\": listed, \"f_coin\": f_coin, \"g_coin\": g_coin, \"b_coin\": b_coin, \"c_coin\": c_coin, \"price\": price, \"change\": change, \"time\": msg.time };\n\nmsgAction = { \"message_type\": \"bot\", \"bot_id\": id, \"email_token\": token, \"delay_seconds\": \"0\", \"pair\": coin};\n\nreturn [msgTable,msgAction];",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1030,
"y": 340,
"wires": [
[
"05817e66364585d6"
],
[]
]
},
{
"id": "ab9f942c4f2f0686",
"type": "change",
"z": "19925c368c4d96ea",
"name": "",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "ftx",
"tot": "str"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "($found := $$.payload.payload.result[name = $flowContext('f_coin')];\t$exists($found) ? \t $found.${\t \"change\":change24h,\t \"price\": last,\t \"coin\": name,\t \"exchange\": $$.payload.exchange} : \t\"not found\")\t",
"tot": "jsonata"
},
{
"t": "set",
"p": "time",
"pt": "msg",
"to": "",
"tot": "date"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 670,
"y": 200,
"wires": [
[
"47a0bff56a4d9ccb"
]
]
},
{
"id": "d7998371671437b3",
"type": "change",
"z": "19925c368c4d96ea",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "($found := $$.payload.payload[symbol = $flowContext('b_coin')];\t$exists($found) ? \t $found.${\t \"change\":priceChangePercent,\t \"price\": lastPrice,\t \"coin\": symbol,\t \"exchange\": $$.payload.exchange} : \t0)\t\t",
"tot": "jsonata"
},
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "binance",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 670,
"y": 280,
"wires": [
[
"47a0bff56a4d9ccb"
]
]
},
{
"id": "9b879d220b86ac80",
"type": "change",
"z": "19925c368c4d96ea",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "($found := $$.payload.payload[id = $flowContext('c_coin')];\t$exists($found) ? \t $found.${\t \"coin\": id,\t \"price\": null,\t \"change\": null,\t \"exchange\": $$.payload.exchange} : \t\"not found\")\t\t",
"tot": "jsonata"
},
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "coinbase",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 670,
"y": 320,
"wires": [
[
"47a0bff56a4d9ccb"
]
]
},
{
"id": "11e8d8d04f29951e",
"type": "moment",
"z": "19925c368c4d96ea",
"name": "time",
"topic": "",
"input": "",
"inputType": "date",
"inTz": "America/New_York",
"adjAmount": 0,
"adjType": "days",
"adjDir": "add",
"format": "MM[/]DD[ - ]LTS",
"locale": "C",
"output": "time",
"outputType": "msg",
"outTz": "America/New_York",
"x": 950,
"y": 180,
"wires": [
[
"754311d56ac3585b"
]
]
},
{
"id": "b19403fafc4f81ab",
"type": "ui_table",
"z": "19925c368c4d96ea",
"group": "be72bda40c2b3554",
"name": "Listings",
"order": 1,
"width": "8",
"height": "10",
"columns": [
{
"field": "l_coin",
"title": "Coin",
"width": "8%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "listed",
"title": "Listed",
"width": "15%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "f_coin",
"title": "FTX",
"width": "10%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "g_coin",
"title": "Gate.io",
"width": "10%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "b_coin",
"title": "Binance",
"width": "10%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "c_coin",
"title": "Coinbase",
"width": "10%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "price",
"title": "Price",
"width": "10%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "change",
"title": "Change",
"width": "10%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
},
{
"field": "time",
"title": "Timestamp",
"width": "15%",
"align": "center",
"formatter": "plaintext",
"formatterParams": {
"target": "_blank"
}
}
],
"outputs": 1,
"cts": true,
"x": 1470,
"y": 300,
"wires": [
[]
]
},
{
"id": "05817e66364585d6",
"type": "function",
"z": "19925c368c4d96ea",
"name": "msg_events",
"func": "var msg_obj = msg.payload ;\nvar arr_msgs = flow.get(\"msg_events\", 'memoryOnly');\n\nif (arr_msgs===undefined ) {\n // Create an empty array if it does not exist yet\n arr_msgs = [];\n //arr_msgs.push(msg_obj) ; \n if (msg_obj !== \"1\") {\n arr_msgs.push(msg_obj);\n flow.set(\"msg_events\",arr_msgs, 'memoryOnly');\n }\n \n// return msg ;\n\n} else {\n // This is a new user, save and return in the first port\n if (msg_obj !== \"1\") {\n arr_msgs.push(msg_obj);\n flow.set(\"msg_events\",arr_msgs, 'memoryOnly');\n }\n} \nmsg.payload = flow.get(\"msg_events\", 'memoryOnly');\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1120,
"y": 240,
"wires": [
[
"b6eb52354b96d980",
"4af1cdb4a5ceb92c"
]
]
},
{
"id": "98d0cecfaa18a8c8",
"type": "function",
"z": "19925c368c4d96ea",
"name": "clear",
"func": "var cfg = undefined ;\nflow.set('msg_events', cfg, 'memoryOnly');\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1350,
"y": 380,
"wires": [
[
"b19403fafc4f81ab"
]
]
},
{
"id": "7e4d7d78076a8f33",
"type": "inject",
"z": "19925c368c4d96ea",
"name": "clear",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": "0.1",
"topic": "",
"payload": "1",
"payloadType": "str",
"x": 1230,
"y": 380,
"wires": [
[
"98d0cecfaa18a8c8"
]
]
},
{
"id": "b6eb52354b96d980",
"type": "delay",
"z": "19925c368c4d96ea",
"name": "1ms",
"pauseType": "delay",
"timeout": "1",
"timeoutUnits": "milliseconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 1310,
"y": 240,
"wires": [
[
"b19403fafc4f81ab"
]
]
},
{
"id": "4af1cdb4a5ceb92c",
"type": "change",
"z": "19925c368c4d96ea",
"name": "refresh",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "[]",
"tot": "json"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1290,
"y": 300,
"wires": [
[
"b19403fafc4f81ab"
]
]
},
{
"id": "36265ea3b12c0c9b",
"type": "http in",
"z": "19925c368c4d96ea",
"name": "Alert",
"url": "/crypto-alert",
"method": "post",
"upload": false,
"swaggerDoc": "",
"x": 130,
"y": 380,
"wires": [
[
"3e2acd2032de1cd3",
"a06002f4195586c6"
]
]
},
{
"id": "3e2acd2032de1cd3",
"type": "http response",
"z": "19925c368c4d96ea",
"name": "OK",
"statusCode": "200",
"headers": {},
"x": 130,
"y": 480,
"wires": []
},
{
"id": "9eb6f69be4d296c5",
"type": "change",
"z": "19925c368c4d96ea",
"name": "",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "gate",
"tot": "str"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "($found := $$.payload.payload[currency_pair = $flowContext('g_coin')];\t$exists($found) ? \t $found.${\t \"change\":change_percentage,\t \"price\": last,\t \"coin\": currency_pair,\t \"exchange\": $$.payload.exchange} : \t\"not found\")\t",
"tot": "jsonata"
},
{
"t": "set",
"p": "time",
"pt": "msg",
"to": "",
"tot": "date"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 670,
"y": 240,
"wires": [
[]
]
},
{
"id": "be72bda40c2b3554",
"type": "ui_group",
"name": "Alerts",
"tab": "7e13e6107768d821",
"order": 2,
"disp": false,
"width": "8",
"collapse": false,
"className": ""
},
{
"id": "7e13e6107768d821",
"type": "ui_tab",
"name": "CRYPTO",
"icon": "dashboard",
"order": 1,
"disabled": false,
"hidden": false
}
]