{
  "id": "xN5mY6pjblqnzCpD",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Deploy crypto screener picks to a Gainium DCA bot with Telegram approval",
  "tags": [],
  "nodes": [
    {
      "id": "3c931a89-c40a-45ba-b1d6-8913441694e0",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        16
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Deploy crypto screener picks to a Gainium DCA bot with Telegram approval\n\n### How it works\n\nThis workflow runs each morning, reads configuration for a Gainium DCA bot, and fetches the top crypto screener results. It converts the screener output into a top-N pair list, sends the proposal to a trader on Telegram, and only updates and starts the bot if the trader approves. Deployment activity is logged to Google Sheets, while skipped runs and screener or workflow failures are reported through Telegram.\n\n### Setup steps\n\n- Configure the schedule trigger for the desired run time and timezone.\n- Add Gainium credentials and verify the configured bot_id, bot_type, top_n, and paper_trading values in the Config node.\n- Configure Telegram credentials, target chat IDs, and the approval message/response format used by the approval IF condition.\n- Connect Google Sheets credentials and select the spreadsheet/sheet used to log deployments.\n- Ensure the global error workflow is enabled in n8n so workflow-level failures trigger the Telegram error alert.\n\n### Customization\n\nAdjust top_n, paper_trading mode, screener criteria, approval wording, and the Google Sheets log columns to match the trading strategy and audit requirements."
      },
      "typeVersion": 1
    },
    {
      "id": "22c8c30c-cb12-4e4e-9f0e-09a7937ba002",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 960,
        "height": 304,
        "content": "## Schedule and screen pairs\n\nStarts the daily run, sets Gainium bot parameters, retrieves screener results, and formats the top-N trading pairs for review."
      },
      "typeVersion": 1
    },
    {
      "id": "37512f4b-fd59-4bc0-965f-088e834bcca9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1888,
        128
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 320,
        "content": "## Request trader approval\n\nSends the proposed pair list to Telegram and routes execution based on whether the trader approves the deployment."
      },
      "typeVersion": 1
    },
    {
      "id": "901cce19-32af-4f20-b433-74caa036e08f",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2368,
        16
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 304,
        "content": "## Deploy approved bot\n\nFor approved proposals, replaces the bot pair list, starts the Gainium bot, records the deployment in Google Sheets, and confirms deployment in Telegram."
      },
      "typeVersion": 1
    },
    {
      "id": "55bbd9d9-8ec8-411f-bd14-52d663c73df5",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2448,
        352
      ],
      "parameters": {
        "color": 7,
        "height": 384,
        "content": "## Notify skipped deployment\n\nHandles the rejection path by notifying the trader that the proposed bot update was skipped."
      },
      "typeVersion": 1
    },
    {
      "id": "5c270f3c-0268-409e-9420-72b24eef2916",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1376,
        560
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 496,
        "content": "## Failure alert handling\n\nContains the lower alert cluster for screener-specific failures and global workflow errors, both reported to Telegram."
      },
      "typeVersion": 1
    },
    {
      "id": "3ff205d7-c4f2-4458-8118-66daf65329a5",
      "name": "Every Day at 8 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        848,
        288
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "8c6662f7-a0b7-4b14-b03e-45397c9c9be6",
      "name": "Set Bot Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        1104,
        288
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c1",
              "name": "bot_id",
              "type": "string",
              "value": "PUT_YOUR_DCA_BOT_ID_HERE"
            },
            {
              "id": "c2",
              "name": "bot_type",
              "type": "string",
              "value": "dca"
            },
            {
              "id": "c3",
              "name": "top_n",
              "type": "number",
              "value": 5
            },
            {
              "id": "c4",
              "name": "paper_trading",
              "type": "boolean",
              "value": true
            },
            {
              "id": "c5",
              "name": "telegram_chat_id",
              "type": "string",
              "value": "PUT_YOUR_TELEGRAM_CHAT_ID"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "5249b05f-e7f8-45ff-b831-6c2464b778f1",
      "name": "Fetch Top Screener Coins",
      "type": "n8n-nodes-gainium.Gainium",
      "onError": "continueErrorOutput",
      "position": [
        1392,
        288
      ],
      "parameters": {
        "pageSize": 25,
        "resource": "general",
        "sortType": "desc",
        "sortField": "totalVolume",
        "filterModel": "{\n  \"items\": [\n    { \"field\": \"marketCap\", \"operator\": \">=\", \"value\": \"2+1234567890\", \"id\": 1 },\n    { \"field\": \"exchanges\", \"operator\": \"contains\", \"value\": \"binance\", \"id\": 2 },\n    { \"field\": \"priceChangePercentage24h\", \"operator\": \">\", \"value\": \"0\", \"id\": 3 }\n  ],\n  \"linkOperator\": \"and\"\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "f746018a-d2f4-4ff7-99cf-1787fc39a91e",
      "name": "Build Top Pairs String",
      "type": "n8n-nodes-base.code",
      "position": [
        1616,
        288
      ],
      "parameters": {
        "jsCode": "// Build the top-N pairs string from screener output.\n// Screener is already sorted by total volume desc; we clean + cap it.\nconst STABLE = new Set(['USDT','USDC','DAI','TUSD','FDUSD','BUSD','USDE','USD','PYUSD']);\nconst QUOTE = 'USDT';\nconst TOP_N = Number($('Set Bot Configuration').first().json.top_n) || 5;\n\nlet rows = $input.all().map(i => i.json);\n// Unwrap if the API returned a single item wrapping an array\nif (rows.length === 1 && Array.isArray(rows[0]?.data)) rows = rows[0].data;\nif (rows.length === 1 && Array.isArray(rows[0]?.result)) rows = rows[0].result;\nif (rows.length === 1 && Array.isArray(rows[0]?.data?.result)) rows = rows[0].data.result;\nif (rows.length === 1 && Array.isArray(rows[0]?.items)) rows = rows[0].items;\n\nconst pairs = [];\nfor (const r of rows) {\n  let sym = String(r.symbol || r.baseAsset || r.coin || r.ticker || '').toUpperCase().trim();\n  if (!sym) continue;\n  sym = sym.replace(/[_/-]?USDT$/, '');\n  if (!sym || STABLE.has(sym)) continue;\n  const pair = sym + '_' + QUOTE;\n  if (!pairs.includes(pair)) pairs.push(pair);\n  if (pairs.length >= TOP_N) break;\n}\n\nif (pairs.length === 0) {\n  throw new Error('Screener returned no usable pairs \u2014 check filter/credentials/field names.');\n}\n\nreturn [{ json: { pairs: pairs.join(','), list: pairs, count: pairs.length } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "5e0e8189-9d39-4629-957f-e5128957e50e",
      "name": "Send Pairs Proposal to Trader",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1936,
        288
      ],
      "parameters": {
        "chatId": "={{ $('Set Bot Configuration').item.json.telegram_chat_id }}",
        "message": "=\ud83d\udcc8 Gainium daily deploy \u2014 your approval needed\n\nMode: {{ $('Set Bot Configuration').item.json.paper_trading ? '\ud83e\uddea PAPER' : '\ud83d\udd34 LIVE' }}\nBot: {{ $('Set Bot Configuration').item.json.bot_id }}\n\nTop {{ $json.count }} pairs from today's screener:\n{{ $json.list.join('\\n') }}\n\nDeploy these to the bot and start it?",
        "options": {},
        "operation": "sendAndWait",
        "approvalOptions": {
          "values": {
            "approvalType": "double",
            "approveLabel": "\u2705 Deploy",
            "disapproveLabel": "\u274c Skip today"
          }
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7d46f938-e676-4824-afdc-24589c5130c6",
      "name": "Check if Approved",
      "type": "n8n-nodes-base.if",
      "position": [
        2112,
        288
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.data.approved }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a369748c-f8a7-4bb4-83f6-6faed2e3f01b",
      "name": "Update Bot Pairs",
      "type": "n8n-nodes-gainium.Gainium",
      "position": [
        2416,
        144
      ],
      "parameters": {
        "botId": "={{ $('Set Bot Configuration').item.json.bot_id }}",
        "botType": "={{ $('Set Bot Configuration').item.json.bot_type }}",
        "operation": "changePairs",
        "pairsToSet": "={{ $('Build Top Pairs String').item.json.pairs }}",
        "paperContext": "={{ $('Set Bot Configuration').item.json.paper_trading }}"
      },
      "typeVersion": 2
    },
    {
      "id": "968143e0-e293-4200-a8ce-d34c3c16151c",
      "name": "Activate Trading Bot",
      "type": "n8n-nodes-gainium.Gainium",
      "position": [
        2640,
        144
      ],
      "parameters": {
        "botId": "={{ $('Set Bot Configuration').item.json.bot_id }}",
        "botType": "={{ $('Set Bot Configuration').item.json.bot_type }}",
        "operation": "start",
        "paperContext": "={{ $('Set Bot Configuration').item.json.paper_trading }}"
      },
      "typeVersion": 2
    },
    {
      "id": "e50c077c-2992-4a78-9a8b-45e385ce136e",
      "name": "Log Deployment in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2864,
        144
      ],
      "parameters": {
        "columns": {
          "value": {
            "date": "={{ $now.format('yyyy-LL-dd HH:mm') }}",
            "mode": "={{ $('Set Bot Configuration').item.json.paper_trading ? 'paper' : 'live' }}",
            "count": "={{ $('Build Top Pairs String').item.json.count }}",
            "pairs": "={{ $('Build Top Pairs String').item.json.pairs }}",
            "bot_id": "={{ $('Set Bot Configuration').item.json.bot_id }}",
            "status": "deployed"
          },
          "schema": [
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "bot_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "bot_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "mode",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "mode",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "pairs",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "pairs",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Deployments"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "PUT_YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "7ba9029e-734f-4eec-aa8e-0e12d091af88",
      "name": "Confirm Deployment to Trader",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3072,
        144
      ],
      "parameters": {
        "text": "=\u2705 *Deployed to {{ $('Set Bot Configuration').item.json.bot_id }}*\n\n*Mode:* {{ $('Set Bot Configuration').item.json.paper_trading ? '\ud83e\uddea paper' : '\ud83d\udd34 live' }}\n*Pairs ({{ $('Build Top Pairs String').item.json.count }}):* `{{ $('Build Top Pairs String').item.json.pairs }}`\n*Time:* {{ $now.format('MMM d, yyyy HH:mm') }}\n\nBot pairs replaced and started. Logged to Sheets.",
        "chatId": "={{ $('Set Bot Configuration').item.json.telegram_chat_id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "82de9b89-e75f-49ab-b236-81b7f6768a3d",
      "name": "Inform Trader \u2013 Skipped",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2496,
        560
      ],
      "parameters": {
        "text": "=\u23ed\ufe0f *Skipped today*\n\nNo changes made to *{{ $('Set Bot Configuration').item.json.bot_id }}*.\nProposed pairs were: `{{ $('Build Top Pairs String').item.json.pairs }}`\n*Time:* {{ $now.format('MMM d, yyyy HH:mm') }}",
        "chatId": "={{ $('Set Bot Configuration').item.json.telegram_chat_id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "725520c7-a68e-4511-8515-9058a909937f",
      "name": "Notify Screener Failure",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1648,
        720
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *Gainium screener call failed*\n\n*Error:* {{ $json.error?.message || $json.error || 'unknown' }}\n*Time:* {{ $now.format('MMM d, yyyy HH:mm') }}\n\nNo pairs were deployed. Check your Gainium API credential / filter.",
        "chatId": "PUT_YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "04462ae4-775c-4cf4-9df6-ae49879cf606",
      "name": "On Workflow Error",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        1424,
        896
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "83546b37-4bad-46a9-af2d-aefbcc1c6187",
      "name": "Send Workflow Error Alert",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1648,
        896
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *Gainium deploy workflow error*\n\n*Workflow:* {{ $workflow.name }}\n*Failed node:* {{ $execution.lastNodeExecuted }}\n*Execution:* {{ $execution.id }}\n*Time:* {{ $now.format('MMM d, yyyy HH:mm') }}\n\nCheck the n8n execution log.",
        "chatId": "PUT_YOUR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "cebaad78-dd33-4d67-a075-7f302fcc8750",
  "connections": {
    "Update Bot Pairs": {
      "main": [
        [
          {
            "node": "Activate Trading Bot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Approved": {
      "main": [
        [
          {
            "node": "Update Bot Pairs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Inform Trader \u2013 Skipped",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every Day at 8 AM": {
      "main": [
        [
          {
            "node": "Set Bot Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On Workflow Error": {
      "main": [
        [
          {
            "node": "Send Workflow Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Activate Trading Bot": {
      "main": [
        [
          {
            "node": "Log Deployment in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Bot Configuration": {
      "main": [
        [
          {
            "node": "Fetch Top Screener Coins",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Top Pairs String": {
      "main": [
        [
          {
            "node": "Send Pairs Proposal to Trader",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Top Screener Coins": {
      "main": [
        [
          {
            "node": "Build Top Pairs String",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Screener Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Deployment in Sheets": {
      "main": [
        [
          {
            "node": "Confirm Deployment to Trader",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Pairs Proposal to Trader": {
      "main": [
        [
          {
            "node": "Check if Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}