N
n8n Store
Workflow Market
Automated Daily Stock Market Report with Bright Data, GPT, Airtable, and Gmail

Automated Daily Stock Market Report with Bright Data, GPT, Airtable, and Gmail

by baptistefort1 views

説明

Categories

🤖 AI & Machine Learning

Nodes Used

n8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.waitn8n-nodes-base.switchn8n-nodes-base.airtablen8n-nodes-base.splitOutn8n-nodes-base.aggregaten8n-nodes-base.gmailTooln8n-nodes-base.stickyNoten8n-nodes-base.stickyNote
Price無料
Views1
最終更新2/20/2026
workflow.json
{
  "id": "dnw1cvGPaYS9dCog",
  "meta": {
    "instanceId": "18b0d04ad5030054be4ab49562e84ea4a079ab4cb036003697394140227c7e76",
    "templateId": "5305",
    "templateCredsSetupCompleted": true
  },
  "name": "Automated Daily Stock Market Report with Bright Data, GPT, Airtable, and Gmail",
  "tags": [],
  "nodes": [
    {
      "id": "0a0def72-9c6f-43fb-8db5-268da98f82dd",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        3904,
        656
      ],
      "parameters": {},
      "typeVersion": 1.3
    },
    {
      "id": "e13cbed2-0837-4b4b-82b4-49c61510dff5",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        3760,
        656
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "eDzFw4LYXVKJQcfH",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cf744498-62bf-4e6a-8570-dda7fea35fe5",
      "name": "Daily Run Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        208,
        576
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "d0ee4823-0536-415e-8fa9-861c1701665d",
      "name": "Set Stock List",
      "type": "n8n-nodes-base.set",
      "position": [
        528,
        576
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8d1bd0c8-37bf-4028-a20c-ac214ec06d7b",
              "name": "json",
              "type": "array",
              "value": "=[\n  {\n    \"ticker\": \"SHEL\",\n    \"name\": \"Shell plc\",\n    \"market_cap\": \"≈ $250B\"\n  },\n  {\n    \"ticker\": \"NESN.SW\",\n    \"name\": \"Nestlé S.A.\",\n    \"market_cap\": \"≈ $320B\"\n  },\n  {\n    \"ticker\": \"SAP\",\n    \"name\": \"SAP SE\",\n    \"market_cap\": \"≈ $210B\"\n  },\n  {\n    \"ticker\": \"ASML\",\n    \"name\": \"ASML Holding N.V.\",\n    \"market_cap\": \"≈ $470B\"\n  },\n  {\n    \"ticker\": \"SONY\",\n    \"name\": \"Sony Group Corporation\",\n    \"market_cap\": \"≈ $110B\"\n  },\n  {\n    \"ticker\": \"TCEHY\",\n    \"name\": \"Tencent Holdings Ltd.\",\n    \"market_cap\": \"≈ $420B\"\n  },\n  {\n    \"ticker\": \"BABA\",\n    \"name\": \"Alibaba Group Holding Ltd.\",\n    \"market_cap\": \"≈ $190B\"\n  },\n  {\n    \"ticker\": \"7203.T\",\n    \"name\": \"Toyota Motor Corporation\",\n    \"market_cap\": \"≈ $270B\"\n  },\n  {\n    \"ticker\": \"RHHBY\",\n    \"name\": \"Roche Holding AG\",\n    \"market_cap\": \"≈ $210B\"\n  },\n  {\n    \"ticker\": \"NFLX\",\n    \"name\": \"Netflix Inc.\",\n    \"market_cap\": \"≈ $260B\"\n  }\n]\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "88b7e912-af0c-4527-baa3-4f8cf0b072d1",
      "name": "Split Stocks",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        816,
        576
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "json"
      },
      "typeVersion": 1
    },
    {
      "id": "2aab292f-0287-4da6-86ee-83c5a9aee30a",
      "name": "Prepare Stock Keyword",
      "type": "n8n-nodes-base.set",
      "position": [
        1104,
        576
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8d9e80d9-16dd-4658-928f-d94735209bfd",
              "name": "keyword",
              "type": "string",
              "value": "={{ $json.ticker }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "13f81727-f586-4fb6-be73-4880c9efd165",
      "name": "Bright Data Scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1456,
        576
      ],
      "parameters": {
        "url": "https://api.brightdata.com/datasets/v3/trigger",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $('Prepare Stock Keyword').all().map(item => item.json)}}",
        "sendBody": true,
        "sendQuery": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "queryParameters": {
          "parameters": [
            {
              "name": "dataset_id",
              "value": "gd_lmrpz3vxmz972ghd7"
            },
            {
              "name": "include_errors",
              "value": "true"
            },
            {
              "name": "type",
              "value": "discover_new"
            },
            {
              "name": "discover_by",
              "value": "keyword"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer YOUR_BRIGHTDATA_API_KEY"
            }
          ]
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "2cc8d35f-f255-4adc-b1d7-7f9ab34dde66",
      "name": "Check Scraper Progress",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1936,
        576
      ],
      "parameters": {
        "url": "=https://api.brightdata.com/datasets/v3/progress/{{ $json.snapshot_id }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_BRIGHTDATA_API_KEY"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "eb6ab86c-3e8b-4dc6-8d70-3346531905fa",
      "name": "Scraper Status Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        2368,
        576
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "ready",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "4712e222-2bad-4d91-be87-ab0e0693c0c6",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "ready"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "running",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "9a473960-6ba5-4f1b-afc6-396348bd338d",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "=running"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "e27afbc7-8cae-44b6-b506-474bb2692f08",
      "name": "Wait for Data",
      "type": "n8n-nodes-base.wait",
      "position": [
        2480,
        1088
      ],
      "webhookId": "cf27222d-20db-4ba5-bd4c-e6ca05efd4d7",
      "parameters": {
        "amount": 20
      },
      "typeVersion": 1.1
    },
    {
      "id": "5e828381-8268-4114-b16c-a60c4ca23e80",
      "name": "Fetch Scraper Results",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2848,
        560
      ],
      "parameters": {
        "url": "=https://api.brightdata.com/datasets/v3/snapshot/{{ $json.snapshot_id }}",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "format",
              "value": "json"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_BRIGHTDATA_API_KEY"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4c2826ee-30bb-4438-a531-c370edbe2103",
      "name": "Aggregate Stock Data",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        3312,
        560
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "ef5165a4-0452-4b20-b2fa-994429f3bc5b",
      "name": "Generate Daily Summary (AI)",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3792,
        416
      ],
      "parameters": {
        "text": "=## 🎯 ROLE\nYou are an **AI-powered financial analyst agent** integrated inside an n8n workflow.  \nYour responsibilities are to:  \n- Parse incoming JSON datasets of U.S. equities (typically top 10 by market cap).  \n- Detect **market trends, anomalies, and investor-relevant signals** from the data.  \n- Convert raw JSON into a **professionally written HTML email digest**, with clear structure, styling, and actionable insights.  \n- Act as if you were a **senior analyst writing for institutional investors**, keeping the tone sharp, concise, and technical.  \n- Output only **clean HTML content** ready for use inside an email body (no markdown, no plaintext, no metadata).  \n\n---\n\n## 📥 INPUT FORMAT (JSON)\n\nYou will receive daily input structured like this:\n\n```\nThe day and date today is {{ $now.format('cccc yyyy-MM-dd t') }}\n\nHere is today’s stock market data: =\n\n{{ $json.data.toJsonString() }}\n```\n\nWhere each stock object may contain:  \n- `ticker` → Stock symbol (e.g., AAPL, NVDA)  \n- `name` → Company name (e.g., Apple Inc.)  \n- `market_cap` → Market capitalization  \n- `price` → Current price or last close price  \n- `change` → Daily percentage change (can be positive or negative)  \n- `sentiment` → Qualitative sentiment (optional: 🟢 Positive, 🟡 Neutral, 🔴 Negative)  \n- `news` → Associated news snippets (optional)  \n\n---\n\n## ✅ TASKS\n\n1. **Market Context**  \n   - Start with a headline summary of the day (general trend: bullish, bearish, or mixed).  \n   - Mention notable **macro events** or **sector-specific highlights** if derivable from input.  \n\n2. **Top Movers**  \n   - Identify and highlight the **2 biggest gainers** and **2 biggest losers**.  \n   - Give context if available (based on percentage change or attached news).  \n\n3. **Stock Data Table**  \n   - Generate a professional `<table>` with the following columns:  \n     - Ticker  \n     - Company Name  \n     - Daily % Change (with ↑/↓ and colored formatting)  \n     - Market Cap  \n     - Sentiment Icon (🟢 🟡 🔴)  \n\n4. **Insights Section**  \n   - Provide **3–5 key takeaways**:  \n     - Example: “Tech continues outperforming with NVDA +X% and AAPL +Y%”  \n     - Highlight any **sector rotation, unusual volatility, or outlier performance**.  \n     - Mention if the **trend diverges** from broader market sentiment.  \n\n5. **Upcoming Catalysts**  \n   - If `news` mentions future events (earnings calls, product launches, Fed meetings), list them in a dedicated section.  \n   - If no events are available, skip gracefully.  \n\n6. **Professional Formatting**  \n   - Use inline CSS to ensure email compatibility.  \n   - Use clear hierarchy:  \n     - `<h2>` for main title (“Daily U.S. Stock Market Digest – {Date}”)  \n     - `<h3>` for sub-sections (e.g., “Top Gainers & Losers”, “Market Insights”, “Upcoming Events”)  \n     - `<ul>` for bullet point insights  \n     - `<table>` for stock performance data  \n   - Add color indicators for sentiment:  \n     - 🟢 Positive = green text  \n     - 🟡 Neutral = gold text  \n     - 🔴 Negative = red text  \n\n---\n\n## ✨ OUTPUT FORMAT (HTML Only)\n\n- Produce **only** HTML, no markdown.  \n- Must be **email-client safe**:  \n  - Inline styles only (no external CSS).  \n  - Avoid JavaScript or advanced CSS.  \n- Structure example:  \n\n```html\n<h2>Daily U.S. Stock Market Digest – Thursday 2025-09-18</h2>\n\n<h3>📈 Top Gainers & Losers</h3>\n<ul>\n  <li>NVDA +4.2% 🟢 — driven by strong chip demand</li>\n  <li>TSLA -3.1% 🔴 — impacted by regulatory concerns</li>\n</ul>\n\n<h3>📊 Market Overview</h3>\n<table style=\"border-collapse:collapse;width:100%;\">\n  <tr style=\"background:#f2f2f2;\">\n    <th>Ticker</th><th>Company</th><th>Change %</th><th>Market Cap</th><th>Sentiment</th>\n  </tr>\n  <tr>\n    <td>NVDA</td><td>NVIDIA Corporation</td><td style=\"color:green;\">+4.2%</td><td>$3.6T</td><td>🟢</td>\n  </tr>\n  <tr>\n    <td>TSLA</td><td>Tesla Inc.</td><td style=\"color:red;\">-3.1%</td><td>$830B</td><td>🔴</td>\n  </tr>\n</table>\n\n<h3>💡 Key Insights</h3>\n<ul>\n  <li>Tech leads with NVDA and MSFT posting strong gains.</li>\n  <li>Healthcare sector showed weakness with JNJ down 2%.</li>\n  <li>Overall sentiment remained mixed ahead of Fed decision.</li>\n</ul>\n\n<h3>📅 Upcoming Events</h3>\n<ul>\n  <li>Fed interest rate decision — Sept 20</li>\n  <li>Apple iPhone launch event — Sept 21</li>\n</ul>\n\n<footer style=\"margin-top:20px;font-size:12px;color:#888;\">\n  Generated automatically by your AI-powered stock monitor.\n</footer>\n```\n\n---\n\n## ⚠️ CONSTRAINTS\n\n- Do **not** include email subject or headers.  \n- Do **not** output explanations, reasoning, or markdown.  \n- Always output **clean HTML only**.  \n- Ensure the HTML is **ready-to-send** in an email body without manual edits.  \n- If data is missing, skip the section gracefully without placeholders.  \n\n---\n\n## 📬 GOAL\n\nDeliver a **polished, mobile-friendly, and insight-rich stock market digest email**.  \nFinal output must require zero manual edits, look professional for finance users, and be directly usable in n8n’s Gmail/SMTP nodes.  \n",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2
    },
    {
      "id": "3f953ffd-4faa-4cd5-bd3a-b3a6d021eebf",
      "name": "Send Report via Gmail",
      "type": "n8n-nodes-base.gmailTool",
      "position": [
        4064,
        624
      ],
      "webhookId": "fe4a416a-43f3-4ad1-80c8-51f1de4c2343",
      "parameters": {
        "sendTo": "[email protected]",
        "message": "={{ $('Generate Daily Summary (AI)').first().json.output }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "=Daily Stock Market Digest "
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "RpkU3PW6jAmCX8ih",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "ae4e0ac6-1afb-463a-a273-7dac2dc975e3",
      "name": "Save to Airtable (Daily Stocks)",
      "type": "n8n-nodes-base.airtable",
      "position": [
        2944,
        -288
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appqPdjbR45flhdgT",
          "cachedResultUrl": "https://airtable.com/appqPdjbR45flhdgT",
          "cachedResultName": "Untitled Base"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblvIkbZriGaCrfzP",
          "cachedResultUrl": "https://airtable.com/appqPdjbR45flhdgT/tblvIkbZriGaCrfzP",
          "cachedResultName": "Daily Stocks"
        },
        "columns": {
          "value": {
            "Price": "={{ $json.price }}",
            "Ticker": "={{ $json.ticker }}",
            "Company": "={{ $json.name }}",
            "Change %": "={{ $json.change }}",
            "Sentiment": "={{ $json.sentiment }}",
            "Market Cap": "={{ $json.market_cap }}"
          },
          "schema": [
            {
              "id": "Ticker",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Ticker",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Company",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Company",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Market Cap",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Market Cap",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Price",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Change %",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Change %",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Sentiment",
              "type": "options",
              "display": true,
              "options": [
                {
                  "name": "🟢 Positive",
                  "value": "🟢 Positive"
                },
                {
                  "name": "🟡 Neutral",
                  "value": "🟡 Neutral"
                },
                {
                  "name": "🔴 Negative",
                  "value": "🔴 Negative"
                }
              ],
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Sentiment",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date",
              "type": "dateTime",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "create"
      },
      "credentials": {
        "airtableTokenApi": {
          "id": "vKZ5Wl43rmNFe0My",
          "name": "Airtable Personal Access Token account 3"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "ae35f4c3-6d12-440e-9cf5-c465a7c7d3ee",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 592,
        "content": "## ⏰ Daily Run Trigger (Schedule Trigger)\n\n### Purpose\nStarts the workflow automatically at fixed intervals.  \nThis ensures the stock digest runs daily without manual input.\n\n### Parameters\n- **Trigger Type**: `Time Interval` or `Cron`\n- **Every X**: `1 Day` (or custom interval)\n- **Timezone**: `UTC` or your local timezone\n- **Start Time**: optional (e.g., `09:00`)"
      },
      "typeVersion": 1
    },
    {
      "id": "faa08484-c83e-49dd-bc8a-c29a0c9a95ce",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        96
      ],
      "parameters": {
        "color": 6,
        "width": 288,
        "height": 656,
        "content": "## 📝 Set Stock List (Set Node – SAMPLE DATA)\n\n### Purpose\nDefines the list of stocks to track.  \nThis acts as the **seed data** for the scraping step.\n\n### Parameters\n- **Values to Set**: `Fixed JSON`\n- **Keep Only Set**: `true`\n- **JSON Example**:\n  ```json\n  [\n    { \"ticker\": \"AAPL\", \"name\": \"Apple Inc.\", \"market_cap\": \"≈ $3.0T\" },\n    { \"ticker\": \"MSFT\", \"name\": \"Microsoft Corporation\", \"market_cap\": \"≈ $3.6T\" }\n  ]"
      },
      "typeVersion": 1
    },
    {
      "id": "57ff48b7-fb7a-4c8e-b122-ff8878a46a97",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        720,
        208
      ],
      "parameters": {
        "color": 4,
        "width": 288,
        "height": 544,
        "content": "## 🔀 Split Stocks (Split Out)\n\n### Purpose\nTakes the stock list (array) and splits it into **individual items**,  \nso each stock can be processed separately in the workflow.\n\n### Parameters\n- **Operation**: `Split Out Items`\n- **Field to Split**: `stocks[]` (array field from Set node)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "04a19b0a-1d0f-4fc3-9b5a-a1dbe39daf8e",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        160
      ],
      "parameters": {
        "color": 6,
        "width": 288,
        "height": 592,
        "content": "## 🏷 Prepare Stock Keyword (Set Node)\n\n### Purpose\nAdds a `keyword` field for each stock item.  \nThis keyword is used by Bright Data’s scraper to identify which stock to fetch.\n\n### Parameters\n- **Values to Set**: `Add Field`\n- **Field Name**: `keyword`\n- **Value**: `{{ $json[\"ticker\"] }}`"
      },
      "typeVersion": 1
    },
    {
      "id": "a24344d1-ef9c-4333-83b8-b8e1055667ed",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        160
      ],
      "parameters": {
        "color": 2,
        "width": 480,
        "height": 592,
        "content": "## 🕸 Bright Data Scraper (HTTP Request)\n\n### Purpose\nSends a request to **Bright Data API** to launch a stock scraping job.  \nIt returns a `snapshot_id` used later to fetch results.\n\n### Parameters\n- **Method**: `POST`\n- **Endpoint**: `https://api.brightdata.com/datasets/v1/trigger`\n- **Authentication**: `API Token`  \n  (Header: `Authorization: Bearer <token>`)\n- **Body Fields**:\n  - `dataset_id`: Bright Data dataset ID\n  - `discover_by`: `keyword`\n  - `keyword`: stock ticker\n"
      },
      "typeVersion": 1
    },
    {
      "id": "608e6709-2b53-4b85-8a6f-04339d4f7029",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1776,
        160
      ],
      "parameters": {
        "color": 4,
        "width": 432,
        "height": 592,
        "content": "## 🔄 Check Scraper Progress (HTTP Request)\n\n### Purpose\nChecks if the Bright Data scraping job is finished or still running.  \nLoops until results are ready.\n\n### Parameters\n- **Method**: `GET`\n- **Endpoint**: `https://api.brightdata.com/datasets/v1/snapshots/{snapshot_id}`\n- **Authentication**: `API Token`\n- **Query Params**:\n  - `snapshot_id`: returned from Bright Data Scraper\n  - `status`: current job status (`running`, `ready`)"
      },
      "typeVersion": 1
    },
    {
      "id": "8e82b0a8-1682-4b7f-b12d-a5569ac1e70a",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2208,
        752
      ],
      "parameters": {
        "color": 6,
        "width": 432,
        "height": 512,
        "content": "## ⏳ Wait for Data (Wait Node)\n\n### Purpose\nPauses the workflow for a fixed time before checking the scraper progress again.  \nThis prevents API rate limits and ensures Bright Data has time to finish the job.\n\n### Parameters\n- **Mode**: `Wait a fixed amount of time`\n- **Time**: e.g., `30 seconds`"
      },
      "typeVersion": 1
    },
    {
      "id": "6350cebc-4794-49df-a637-a982ab96a7fe",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2208,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 592,
        "content": "## 🔀 Scraper Status Switch (Switch Node)\n\n### Purpose\nRoutes the workflow based on the scraper job status.  \nIf the status is `running`, it loops back to **Wait for Data**.  \nIf the status is `ready`, it continues to fetch the snapshot.\n\n### Parameters\n- **Value to Check**: `status`\n- **Rules**:\n  - Equals `running` → go to Wait\n  - Equals `ready` → go to Fetch Scraper Results\n"
      },
      "typeVersion": 1
    },
    {
      "id": "93becc4b-e2ea-42e8-8eca-9e9e0f79d0c8",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2640,
        160
      ],
      "parameters": {
        "width": 512,
        "height": 608,
        "content": "## 📥 Fetch Scraper Results (HTTP Request)\n\n### Purpose\nRetrieves the completed stock market data from Bright Data once the job is finished.  \nThe results are returned as JSON and include price, change %, sentiment, and more.\n\n### Parameters\n- **Method**: `GET`\n- **Endpoint**: `https://api.brightdata.com/datasets/v1/snapshots/{snapshot_id}/data`\n- **Authentication**: `API Token`\n- **Query Params**:\n  - `snapshot_id`: ID from Bright Data Scraper\n  - `format`: `json`"
      },
      "typeVersion": 1
    },
    {
      "id": "867cf6ea-2a5f-45fa-98a7-68d194df1dfc",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3152,
        112
      ],
      "parameters": {
        "color": 3,
        "width": 512,
        "height": 656,
        "content": "## 📊 Aggregate Stock Data (Aggregate Node)\n\n### Purpose\nMerges the stock data from multiple items (each ticker) into one consolidated dataset.  \nThis is used as input for the AI to generate a summary.\n\n### Parameters\n- **Mode**: `Aggregate`\n- **Fields to Include**:  \n  - `ticker`  \n  - `name`  \n  - `price`  \n  - `change`  \n  - `sentiment`  \n- **Output**: Single JSON object with all stock records\n"
      },
      "typeVersion": 1
    },
    {
      "id": "8e741c22-987f-4f7a-9e2b-d1d2158ec074",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3664,
        -240
      ],
      "parameters": {
        "color": 6,
        "width": 880,
        "height": 1008,
        "content": "## 🤖 Generate Daily Summary (AI Node – OpenAI/Gemini)\n\n### Purpose\nUses an AI model to analyze the aggregated stock data.  \nGenerates an HTML-formatted email with trends, top movers, insights, and upcoming events.\n\n### Parameters\n- **Model**: `gemini-2.0-flash-lite` (or `gpt-4.1` if using OpenAI)\n- **Input**: JSON data from Aggregate node\n- **Prompt**:  \n  - Analyze market trends  \n  - Identify top gainers/losers  \n  - Generate stock performance table  \n  - Add 3–5 insights  \n  - Format everything as **HTML email body**\n\n## 📧 Send Report via Gmail (Gmail Node)\n\n### Purpose\nDelivers the generated HTML stock market digest directly to the recipient’s inbox.  \nThis is the final step that sends the daily summary email.\n\n### Parameters\n- **Operation**: `Send Email`\n- **Send To**: recipient email (e.g., `[email protected]`)\n- **Subject**:   \n"
      },
      "typeVersion": 1
    },
    {
      "id": "e03b89d9-d530-4c7f-8b9e-b85a39a1fa11",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2784,
        -752
      ],
      "parameters": {
        "color": 6,
        "width": 512,
        "height": 656,
        "content": "## 🗂 Save to Airtable (Airtable – Create Record)\n\n### Purpose\nStores each day’s stock data in Airtable for historical tracking.  \nActs as a database of all reports.\n\n### Parameters\n- **Operation**: `Create Record`\n- **Base ID**: `appXXXXXX` (from Airtable URL)\n- **Table**: `Daily Stocks`\n- **Field Mapping**:\n  - `Ticker` → `{{ $json.ticker }}`\n  - `Company` → `{{ $json.name }}`\n  - `Price` → `{{ $json.price }}`\n  - `Change %` → `{{ $json.change }}`\n  - `Sentiment` → `{{ $json.sentiment }}`\n  - `Date` → `{{ $now.toISO() }}`\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ccdfd109-9fd2-40e4-981e-f85eef7cfba6",
  "connections": {
    "Split Stocks": {
      "main": [
        [
          {
            "node": "Prepare Stock Keyword",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "Generate Daily Summary (AI)",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Data": {
      "main": [
        [
          {
            "node": "Check Scraper Progress",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Stock List": {
      "main": [
        [
          {
            "node": "Split Stocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Run Trigger": {
      "main": [
        [
          {
            "node": "Set Stock List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Daily Summary (AI)",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Bright Data Scraper": {
      "main": [
        [
          {
            "node": "Check Scraper Progress",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Stock Data": {
      "main": [
        [
          {
            "node": "Generate Daily Summary (AI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Scraper Results": {
      "main": [
        [
          {
            "node": "Aggregate Stock Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Save to Airtable (Daily Stocks)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Stock Keyword": {
      "main": [
        [
          {
            "node": "Bright Data Scraper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scraper Status Switch": {
      "main": [
        [
          {
            "node": "Fetch Scraper Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait for Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Report via Gmail": {
      "ai_tool": [
        [
          {
            "node": "Generate Daily Summary (AI)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Check Scraper Progress": {
      "main": [
        [
          {
            "node": "Scraper Status Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Daily Summary (AI)": {
      "main": [
        []
      ]
    }
  }
}

相关工作流