AutomationFlowsAI & RAG › Send AI Stock Risk Alerts From Google Sheets with Twelve Data and Groq

Send AI Stock Risk Alerts From Google Sheets with Twelve Data and Groq

ByWeblineIndia @weblineindia on n8n.io

> Smart Stock Risk Alerts in Minutes

Event trigger★★★★☆ complexityAI-powered17 nodesGoogle SheetsGmailAgentGroq ChatHTTP Request
AI & RAG Trigger: Event Nodes: 17 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #15515 — we link there as the canonical source.

This workflow follows the Agent → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "4Z7beGBJ3MSo6W4X",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Risk Alert Generator",
  "tags": [],
  "nodes": [
    {
      "id": "709245f2-e7a2-4627-accb-ab6e9408cf95",
      "name": "Read Portfolio from Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        208,
        0
      ],
      "parameters": {
        "range": "Portfolio!A:E",
        "options": {},
        "sheetId": "1KA65Fzm1uP4kP8UiCN7rlQxUmcCv5ESxhivfxfDui2s"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "fd7fd453-1605-40eb-b211-bd33b1c0cb18",
      "name": "Edit Fields \u2013 Config & Thresholds",
      "type": "n8n-nodes-base.set",
      "position": [
        400,
        0
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "drop_threshold",
              "value": -5
            },
            {
              "name": "volatility_threshold",
              "value": 3
            }
          ],
          "boolean": [
            {
              "name": "alert_enabled",
              "value": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "bfd44777-cb5a-4b27-aea9-ce0cb544d2de",
      "name": "Filter \u2013 Only Flagged Risks",
      "type": "n8n-nodes-base.if",
      "position": [
        1200,
        -208
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.risks.length}}",
              "value2": "0",
              "operation": "notEqual"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4c93a63b-bfe3-474e-93c0-d9de5f17ccf1",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "89ec7fd7-41a1-42e6-875d-41d3767b4124",
      "name": "Send Risk Alert Notification",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1872,
        -224
      ],
      "parameters": {
        "sendTo": "",
        "message": "={{ $json.output }}",
        "options": {},
        "subject": "Daily Stock Risk Alert"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ca20c72d-0721-41ef-8209-c88871391e81",
      "name": "Wait 8 Seconds Before request",
      "type": "n8n-nodes-base.wait",
      "position": [
        784,
        176
      ],
      "parameters": {
        "amount": 8
      },
      "typeVersion": 1.1
    },
    {
      "id": "d3915664-c776-4817-b42f-9980bd7bc1ce",
      "name": "Compute Risk Logic",
      "type": "n8n-nodes-base.function",
      "position": [
        992,
        -208
      ],
      "parameters": {
        "functionCode": "// First, collect all input items from the previous node\nconst allItems = $items(); // $items() gives all input items as an array\n\n// Separate stocks (first half) and prices (second half)\nconst stocks = allItems.slice(0, 5).map(item => item.json);\nconst prices = allItems.slice(5, 10).map(item => parseFloat(item.json.price));\n\n// Combine stock with its corresponding price\nreturn stocks.map((stock, i) => {\n    const currentPrice = prices[i];\n    const avgPrice = parseFloat(stock.avg_price);\n    const changePercent = ((currentPrice - avgPrice) / avgPrice) * 100;\n    const volatility = Math.abs(changePercent);\n\n    const risks = [];\n    if (changePercent <= stock.drop_threshold) risks.push(\"Large Price Drop\");\n    if (volatility >= stock.volatility_threshold) risks.push(\"High Volatility\");\n\n    return {\n        json: {\n            ...stock,\n            current_price: currentPrice,\n            change_percent: changePercent.toFixed(2),\n            volatility: volatility.toFixed(2),\n            risks\n        }\n    };\n});"
      },
      "typeVersion": 1
    },
    {
      "id": "874debab-4fbd-4685-9ad4-e5163c15b93f",
      "name": "Generate Risk Summary",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1584,
        -224
      ],
      "parameters": {
        "text": "=You are a stock risk assistant. \n\nBelow is the list of flagged stocks, if any. Generate a **clear, concise plain-text summary** for an email alert. Use natural sentences, not just raw tables or code. Include only relevant info: symbol, current price, average price, change %, volatility %, quantity, sector, notes and risks.\n\n- If there are no flagged stocks, output: \"No stocks meet the risk criteria today. No email alert is necessary.\"\n- Otherwise, generate a **single, readable paragraph per stock**.\n- Do not use Markdown, symbols like ** or _ or HTML.\n\nFlagged Stocks:\n{{$json.output}}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "16fec3d5-8fe4-49ff-aedb-2473ac806ee7",
      "name": "Groq Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        1584,
        -32
      ],
      "parameters": {
        "model": "openai/gpt-oss-120b",
        "options": {}
      },
      "credentials": {
        "groqApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2e78d5d2-fe9d-43dd-aa2a-b3a1c5446f2d",
      "name": "Collect Flagged Stocks",
      "type": "n8n-nodes-base.code",
      "position": [
        1392,
        -224
      ],
      "parameters": {
        "jsCode": "// Collect all input items\nconst allItems = $items(); // gives array of all input items\n\n// Map over items to format each stock\nconst flaggedStocksText = allItems.map(item => {\n  const stock = item.json;\n  return `Symbol: ${stock.symbol}\nCurrent Price: ${stock.current_price}\nAverage Price: ${stock.avg_price}\nChange Percent: ${stock.change_percent}%\nVolatility: ${stock.volatility}%\nQuantity: ${stock.quantity}\nSector: ${stock.sector}\nNotes: ${stock.notes}\nRisks: ${stock.risks.join(\", \")}`;\n}).join(\"\\n\\n\"); // double newline for readability\n\n// Return as single output\nreturn [{ json: { output: flaggedStocksText } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "8a3c1984-5e31-4c24-bb56-2de9a1394901",
      "name": "Loop: Fetch Current Prices",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        608,
        0
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "ffe6e92a-4b02-4b51-bbbf-9d5afb9ff726",
      "name": "Fetch Stock Prices",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        992,
        176
      ],
      "parameters": {
        "url": "=https://api.twelvedata.com/price?symbol={{$json.symbol}}",
        "options": {}
      },
      "typeVersion": 4.4
    },
    {
      "id": "79f13e81-092d-4be1-9018-b388a45847a8",
      "name": "Merge Portfolio & Prices",
      "type": "n8n-nodes-base.merge",
      "position": [
        784,
        -208
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "f63ddc9e-8294-404a-ab1f-1e6515e6a782",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -720
      ],
      "parameters": {
        "width": 368,
        "height": 416,
        "content": "## Risk Alert Generator\n\n### How It Works:\nThis workflow reads your stock portfolio from Google Sheets, fetches current prices via API, calculates risk metrics (price drops & volatility), identifies flagged stocks and generates a concise AI-written summary. Only flagged stocks trigger an email alert, keeping notifications relevant.\n\n### Setup Steps:\n1. Connect your Google Sheets account and specify the portfolio sheet and range.\n2. Connect your Gmail account to send alerts.\n3. Configure alert thresholds in the Set Config & Thresholds node.\n4. Provide a stock price API key in the HTTP Request node.\n5. Test by executing the workflow manually or schedule it as needed."
      },
      "typeVersion": 1
    },
    {
      "id": "fb047815-2223-4df3-ae6f-56c4de0ba3f7",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        -416
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 816,
        "content": "## Portfolio Input & Thresholds\nReads portfolio data from Google Sheets, adds configuration parameters like drop thresholds and volatility limits and enables/disables alerts. Prepares a structured dataset for the subsequent risk computation steps. Ensures that only relevant fields are passed downstream."
      },
      "typeVersion": 1
    },
    {
      "id": "89d519d7-3f80-4c4e-a6b2-049d289c4552",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        544,
        -416
      ],
      "parameters": {
        "color": 7,
        "width": 608,
        "height": 816,
        "content": "## Fetch Prices & Compute Risks\nLoops through each stock, retrieves live prices via API with a small delay to respect rate limits, merges original portfolio data and calculates risk metrics (percentage change, volatility). Flags stocks exceeding thresholds, producing a clean dataset for flagged risk analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "ceb2a706-b5f1-448e-9556-7b048be5af2e",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1168,
        -416
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 816,
        "content": "## Summarize & Notify\nFilters only stocks with flagged risks, formats them into a readable summary and passes the data to the AI node for generating plain-text email content. Sends email notifications only when risks exist, ensuring alerts are meaningful and avoiding unnecessary emails."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "21c59cee-37ad-4273-bf3e-2854448a59d0",
  "connections": {
    "Groq Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Risk Summary",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Compute Risk Logic": {
      "main": [
        [
          {
            "node": "Filter \u2013 Only Flagged Risks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Stock Prices": {
      "main": [
        [
          {
            "node": "Loop: Fetch Current Prices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Risk Summary": {
      "main": [
        [
          {
            "node": "Send Risk Alert Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Flagged Stocks": {
      "main": [
        [
          {
            "node": "Generate Risk Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Portfolio & Prices": {
      "main": [
        [
          {
            "node": "Compute Risk Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop: Fetch Current Prices": {
      "main": [
        [
          {
            "node": "Merge Portfolio & Prices",
            "type": "main",
            "index": 1
          }
        ],
        [
          {
            "node": "Wait 8 Seconds Before request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter \u2013 Only Flagged Risks": {
      "main": [
        [
          {
            "node": "Collect Flagged Stocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 8 Seconds Before request": {
      "main": [
        [
          {
            "node": "Fetch Stock Prices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Portfolio from Google Sheets": {
      "main": [
        [
          {
            "node": "Edit Fields \u2013 Config & Thresholds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields \u2013 Config & Thresholds": {
      "main": [
        [
          {
            "node": "Loop: Fetch Current Prices",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Portfolio & Prices",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Read Portfolio from Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

&gt; Smart Stock Risk Alerts in Minutes

Source: https://n8n.io/workflows/15515/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This n8n workflow automates your entire B2B outreach pipeline from lead discovery to personalized cold email delivery. Submit a form, let Apollo find and enrich your leads, review AI-generated emails

Output Parser Structured, Agent, Groq Chat +4
AI & RAG

This workflow automates the tracking of stock market sector rotation. It fetches a list of active stocks from Google Sheets, pulls their last 5 days of market data from Yahoo Finance and calculates mo

Groq Chat, Google Sheets, HTTP Request +2
AI & RAG

This workflow is designed for marketers, content creators, agencies, and solo founders who want to publish long‑form posts with visuals on autopilot using n8n and AI agents. ​

Tool Http Request, Agent, HTTP Request +27
AI & RAG

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

HTTP Request, Google Sheets, OpenRouter Chat +5
AI & RAG

Want to skip the manual work and instantly generate SWOT analyses for your business plans, investor decks, or strategy docs? 🚀 This workflow lets you automate the entire SWOT (Strengths, Weaknesses, O

Google Sheets, Agent, Output Parser Structured +4