This workflow corresponds to n8n.io template #14059 — we link there as the canonical source.
This workflow follows the Googlegemini → Google Sheets 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 →
{
"id": "nNjotMFYRKbGYBkO",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets V10",
"tags": [
{
"id": "MrLFqPuujEL6vkPD",
"name": "Institutional-Grade Stoc",
"createdAt": "2026-02-24T19:56:14.685Z",
"updatedAt": "2026-02-24T19:56:14.685Z"
},
{
"id": "T6peYIB3ixkHZuR5",
"name": "Structured Targets. Quan",
"createdAt": "2026-02-24T19:56:06.627Z",
"updatedAt": "2026-02-24T19:56:06.627Z"
}
],
"nodes": [
{
"id": "a3d2e9f0-323a-48cc-9d8f-7b03dc294628",
"name": "loop_over_tickers",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-576,
1296
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "a42ffbce-a597-4c14-a529-8a9843b8cf06",
"name": "Read_tickers_from_Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-800,
1296
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fHwDfRMnMZAIhE6LIo3yofVX2hwcAnBkfE7ibyBT-z4/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1fHwDfRMnMZAIhE6LIo3yofVX2hwcAnBkfE7ibyBT-z4",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fHwDfRMnMZAIhE6LIo3yofVX2hwcAnBkfE7ibyBT-z4/edit?usp=drivesdk",
"cachedResultName": "List of stocks"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "eb1cd019-3eda-419b-9777-ab8b40a2588c",
"name": "write_sentiment_to_sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
7744,
1392
],
"parameters": {
"columns": {
"value": {
"date": "={{ $json.date }}",
"ma_50": "={{ $json.ma_50 || '' }}",
"model": "={{ $json.model }}",
"stock": "={{ $json.stock }}",
"ma_200": "={{ $json.ma_200 || '' }}",
"rsi_14": "={{ $json.rsi_14 || '' }}",
"f_score": "={{ $json.f_score || '' }}",
"gap_pct": "={{ $json.gap_pct }}",
"pt_base": "={{ $json.pt_base }}",
"pt_bear": "={{ $json.pt_bear }}",
"pt_bull": "={{ $json.pt_bull }}",
"ma_signal": "={{ $json.ma_signal || '' }}",
"rationale": "={{ $json.rationale }}",
"confidence": "={{ $json.confidence }}",
"conviction": "={{ $json.conviction }}",
"rsi_signal": "={{ $json.rsi_signal || '' }}",
"trend_tier": "={{ $json.trend_tier || '' }}",
"current_price": "={{ $json.current_price || '' }}",
"verdict_gemini": "={{ $json.verdict_gemini || '' }}",
"thesis_reversal": "={{ $json.thesis_reversal || false }}",
"verdict_chatgpt": "={{ $json.verdict_chatgpt || '' }}",
"previous_verdict": "={{ $json.previous_verdict || '' }}",
"resolution_method": "={{ $json.resolution_method }}",
"next_earnings_date": "={{ $json.next_earnings_date || '' }}"
},
"schema": [
{
"id": "stock",
"type": "string",
"display": true,
"required": false,
"displayName": "stock",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "date",
"type": "string",
"display": true,
"required": false,
"displayName": "date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "current_price",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "current_price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "pt_bear",
"type": "string",
"display": true,
"required": false,
"displayName": "pt_bear",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "pt_base",
"type": "string",
"display": true,
"required": false,
"displayName": "pt_base",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "pt_bull",
"type": "string",
"display": true,
"required": false,
"displayName": "pt_bull",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "f_score",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "f_score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "confidence",
"type": "string",
"display": true,
"required": false,
"displayName": "confidence",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "gap_pct",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "gap_pct",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "resolution_method",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "resolution_method",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "rationale",
"type": "string",
"display": true,
"required": false,
"displayName": "rationale",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "conviction",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "conviction",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "model",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "model",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "verdict_chatgpt",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "verdict_chatgpt",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "verdict_gemini",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "verdict_gemini",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "ma_50",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "ma_50",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "ma_signal",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "ma_signal",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "rsi_14",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "rsi_14",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "rsi_signal",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "rsi_signal",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "trend_tier",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "trend_tier",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "ma_200",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "ma_200",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "next_earnings_date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "next_earnings_date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "previous_verdict",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "previous_verdict",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "thesis_reversal",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "thesis_reversal",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE/edit?usp=drivesdk",
"cachedResultName": "Sentiments of my stocks"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "ac80fd3d-5d01-41b8-8897-7a32cbbeaf24",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1024,
1296
],
"parameters": {
"rule": {
"interval": [
{
"daysInterval": 3,
"triggerAtHour": 16
}
]
}
},
"typeVersion": 1.2
},
{
"id": "8d041788-4107-48be-9c1f-1107b4e549e4",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-656,
192
],
"parameters": {
"width": 480,
"height": 440,
"content": "# Workflow Overview \n**This workflow automates the process of analyzing the sentiment of stock market news.**\n\n- retrieves a list of stock tickers from a Google Sheet \n- fetchs recent news articles for each ticker\n- uses a 2 large language model to perform sentiment analysis on the articles\n- records the sentiment scores and rationale back into a Google Sheet."
},
"typeVersion": 1
},
{
"id": "35eff45b-2fe7-477e-9bb5-592a12db4d5d",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1056,
704
],
"parameters": {
"color": 4,
"width": 1152,
"height": 992,
"content": "# 1. Daily Trigger and Stock Ticker Retrieval\n- **Schedule Trigger:** This workflow is set to run automatically every three day at 4:00 PM (Asia/Jerusalem time). This ensures that the script runs just before the markets open and you get a daily update on the sentiment of the stocks you are tracking.\n\n- **Read_tickers_from_Sheet:** This node connects to a Google Sheet named \"Stock Sentiment\" and reads the list of stock tickers from the \"stocks\" sheet. This is the source of the stocks that the workflow will analyze. I have make every three days to avoid extra fees from Alphavintage since, and you can modify it if you need.\n\n- **loop_over_tickers:** This node takes the list of tickers from the Google Sheet and processes them one by one. This allows the workflow to perform the same set of actions for each stock ticker individually."
},
"typeVersion": 1
},
{
"id": "4ebc103a-c5b8-4825-b2de-2629d007c762",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
128,
-48
],
"parameters": {
"color": 5,
"width": 2760,
"height": 1420,
"content": "# 2. Financial and News Data Retrieval & Validation\n2.1 Financial Data Fetching (Alpha Vantage APIs)\nFetch Financial Data (Alpha Vantage):\nFor each ticker, the workflow sends multiple HTTP requests to Alpha Vantage to retrieve structured financial data, including:\nCompany overview (EPS, BVPS, margins, shares, revenue).\nIncome statement (quarterly revenue and net income).\nBalance sheet (debt and cash position).\nCash flow statement (operating cash flow and capital expenditures for FCF calculation).\nCurrent stock price.\nData Normalization and Processing:\nThe responses from the APIs are processed using code nodes to:\nExtract required financial fields.\nCalculate derived metrics such as revenue growth, gross margins, total debt, cash position, and free cash flow.\nStandardize numeric values and prepare structured financial data for analysis.\nFinancial Cache Validation:\nThe workflow checks Google Sheets to determine whether financial data for the ticker already exists and whether it is still fresh (within a defined time window).\nIf the data is missing or outdated \u2192 financial APIs are called.\nIf the data is still valid \u2192 cached data is reused to avoid unnecessary API calls.\nUpdate or Insert Financial Records:\nBased on the cache result:\nNew records are inserted for first-time tickers.\nExisting records are updated when refreshed data is retrieved."
},
"typeVersion": 1
},
{
"id": "5c6bf418-f4d5-49c8-911a-6c93d3e0f636",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2928,
576
],
"parameters": {
"color": 6,
"width": 2004,
"height": 1536,
"content": "# 3. Sentiment Analysis with AI\n\n- **AI Agent & Google Gemini Chat Model/ChatGPT:** This is the core of the sentiment analysis. The \"AI Agent\" node is configured with a detailed prompt that instructs the \"Google Gemini Chat Model & ChatGPT\" to act as a stock sentiment analyzer. The prompt specifies the input format (stock symbol, price targets,confidance, rational), and the desired JSON output format. The combined text of the news articles and the current stock ticker are passed to the model.l analyze all the articles at once."
},
"typeVersion": 1
},
{
"id": "ff55f562-a968-41c4-be97-f8493a9b8612",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
4944,
576
],
"parameters": {
"color": 2,
"width": 544,
"height": 1532,
"content": "# 4. Output Formatting and Error Handling\n\n- **format_output_as_json:** The output from the AI models is a raw string that includes a JSON object. This code node extracts the clean JSON from the string and prepares it for the next steps.\n\n- **if_format_succesful:** This conditional node checks if the previous step of formatting the AI's output into a clean JSON was successful. If there was an error, it sends the workflow back to the \"AI Agent\" to try again."
},
"typeVersion": 1
},
{
"id": "a0e69d33-1308-40ed-92be-ba9100f08133",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
7600,
576
],
"parameters": {
"color": 3,
"width": 520,
"height": 1528,
"content": "# 5. Storing the Results\n\n\n- **write_sentiment_to_sheets:** Once a valid sentiment analysis result is obtained and formatted, this node appends the data to \"Sheet1\" of the \"Stock Sentiment\" Google Sheet. It records the current date, the stock ticker, the sentiment score, and the rationale provided by the AI. After this step, the workflow loops back to process the next ticker from the initial list.\n\n\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "5de75ec0-9b5d-499a-9599-1ea79e3821be",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
4064,
1344
],
"parameters": {
"jsCode": "// \u2500\u2500 TIEBREAKER ENRICHMENT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst needs_tiebreaker = $input.first().json?.needs_tiebreaker || false;\nconst gap_pct = $input.first().json?.gap_pct || 0;\nconst verdict_chatgpt = $input.first().json?.verdict_chatgpt || null;\nconst verdict_gemini = $input.first().json?.verdict_gemini || null;\n\nfunction pickValid(x) {\n if (!x) return null;\n const base = Number(x.pt_base);\n const bear = Number(x.pt_bear);\n const bull = Number(x.pt_bull);\n const conf = Number(x.confidence);\n if (!x.stock) return null;\n if (!isFinite(base) || base <= 0) return null;\n if (!isFinite(bear) || bear <= 0) return null;\n if (!isFinite(bull) || bull <= 0) return null;\n if (!isFinite(conf) || conf <= 0) return null;\n return x;\n}\n\nconst byStock = new Map();\n\nfor (const it of $input.all()) {\n const j = it.json || {};\n\n const candidates = [];\n\n if (j.chatgpt_result && typeof j.chatgpt_result === \"object\") {\n candidates.push({ ...j.chatgpt_result, model: j.chatgpt_result.model || \"CHATGPT\" });\n }\n if (j.gemini_result && typeof j.gemini_result === \"object\") {\n candidates.push({ ...j.gemini_result, model: j.gemini_result.model || \"GEMINI\" });\n }\n\n // Fallback: already flattened\n if (candidates.length === 0 && j.stock && (j.pt_base || j.pt_bear || j.pt_bull)) {\n candidates.push({ ...j, model: j.model || \"UNKNOWN\" });\n }\n\n for (const cand of candidates) {\n const stock = cand.stock || j.stock || \"\";\n const normalized = {\n stock,\n date: cand.date || null,\n pt_bear: Number(cand.pt_bear ?? NaN),\n pt_base: Number(cand.pt_base ?? NaN),\n pt_bull: Number(cand.pt_bull ?? NaN),\n f_score: cand.f_score ?? null,\n confidence: Number(cand.confidence ?? NaN),\n rationale: cand.rationale || \"\",\n model: cand.model || \"UNKNOWN\"\n };\n\n const valid = pickValid(normalized);\n if (!valid) continue;\n\n if (!byStock.has(stock)) byStock.set(stock, []);\n byStock.get(stock).push(valid);\n }\n}\n\nconst today = new Date().toISOString().slice(0, 10);\n\n// \u2500\u2500 Conviction label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction getConviction(gap) {\n if (gap < 10) return \"HIGH\";\n if (gap < 20) return \"MEDIUM\";\n return \"LOW\";\n}\n\n// \u2500\u2500 Build output rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst out = [];\n\nfor (const [stock, arr] of byStock.entries()) {\n\n // Keep highest confidence per model\n const bestByModel = new Map();\n for (const r of arr) {\n const key = r.model;\n const prev = bestByModel.get(key);\n if (!prev || (r.confidence ?? 0) > (prev.confidence ?? 0)) {\n bestByModel.set(key, r);\n }\n }\n\n for (const r of bestByModel.values()) {\n const date = r.date || today;\n\n out.push({\n json: {\n stock,\n date,\n pt_bear: Number(r.pt_bear.toFixed(2)),\n pt_base: Number(r.pt_base.toFixed(2)),\n pt_bull: Number(r.pt_bull.toFixed(2)),\n f_score: r.f_score,\n confidence: Math.max(20, Math.min(90, Number(r.confidence.toFixed(0)))),\n rationale: r.rationale || \"No rationale provided\",\n model: r.model,\n row_key: `${stock}_${date}_${r.model}`,\n skip_row: false,\n // \u2500\u2500 Hybrid enrichment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n resolution_method: needs_tiebreaker ? \"TIEBREAKER\" : \"CONSENSUS\",\n conviction: getConviction(gap_pct),\n gap_pct,\n verdict_chatgpt,\n verdict_gemini\n }\n });\n }\n}\n\n// \u2500\u2500 Fallback if nothing valid came through \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nif (out.length === 0) {\n return [{\n json: {\n stock: \"\",\n date: today,\n pt_bear: 0,\n pt_base: 0,\n pt_bull: 0,\n confidence: 0,\n rationale: \"No valid analyst data\",\n model: \"ERROR\",\n row_key: `ERROR_${today}`,\n skip_row: true,\n resolution_method: \"ERROR\",\n conviction: \"NONE\",\n gap_pct: 0,\n verdict_chatgpt: null,\n verdict_gemini: null\n }\n }];\n}\n\nreturn out;"
},
"typeVersion": 2
},
{
"id": "9ab1cf9e-5ea9-4d72-b59a-0cd37c20962f",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
3440,
1120
],
"parameters": {
"mode": "combine",
"options": {},
"joinMode": "keepEverything",
"fieldsToMatchString": "stock"
},
"typeVersion": 3.2
},
{
"id": "00b14e09-f606-48a3-a026-ca8953c96fbe",
"name": "XML",
"type": "n8n-nodes-base.xml",
"position": [
1360,
1632
],
"parameters": {
"options": {},
"dataPropertyName": "sa_xml"
},
"typeVersion": 1
},
{
"id": "bd8b7af5-003c-40cc-b895-77d78ccf476f",
"name": "Merge1",
"type": "n8n-nodes-base.merge",
"position": [
2720,
1152
],
"parameters": {
"mode": "combine",
"options": {},
"joinMode": "keepEverything",
"fieldsToMatchString": "stock"
},
"typeVersion": 3.2
},
{
"id": "f4fcca1b-bc7a-4d58-8bfc-1da7e3648e5e",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
5008,
1328
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "8659bdf4-3891-4cf4-9187-ea800a3810f8",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{$json.skip_row}}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "9760091c-547a-4501-bd4f-1d43a1a91250",
"name": "If1",
"type": "n8n-nodes-base.if",
"position": [
912,
1776
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "bf9993a5-39a5-4344-a1a6-5ba38e67e47c",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "sa_xml",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "7ae74b4b-33c2-4cf6-999e-c3c49f96f5a6",
"name": "Edit Fields2",
"type": "n8n-nodes-base.set",
"position": [
1552,
1632
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "44ff26a3-5901-4e3b-9b15-9844a2d207b5",
"name": "stock",
"type": "string",
"value": "={{ $('Clean the news').item.json.stock }}"
},
{
"id": "fca99ba0-11fd-40a3-acf7-ea25579a653e",
"name": "rss",
"type": "object",
"value": "={{ $json.rss }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f7a5b448-14cc-4204-8541-24ded9f9a1b9",
"name": "Merge2",
"type": "n8n-nodes-base.merge",
"position": [
1552,
432
],
"parameters": {
"mode": "combine",
"options": {},
"joinMode": "keepEverything",
"fieldsToMatchString": "stock"
},
"typeVersion": 3.2
},
{
"id": "70216662-4f36-432f-a5c9-2e479495227f",
"name": "Is Cache Valid?",
"type": "n8n-nodes-base.if",
"position": [
384,
768
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "8579a7cf-3060-4026-b94a-5e5f370c63ae",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{$json.shouldFetch}}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "a1929b6f-2930-4d9f-a040-310c94e9a123",
"name": "Cache Lookup",
"type": "n8n-nodes-base.googleSheets",
"maxTries": 3,
"position": [
-320,
1184
],
"parameters": {
"options": {
"returnFirstMatch": true
},
"filtersUI": {
"values": [
{
"lookupValue": "={{ $json.stock }}",
"lookupColumn": "stock"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk",
"cachedResultName": "Financial_Data_Cache"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"retryOnFail": false,
"typeVersion": 4.7,
"alwaysOutputData": true
},
{
"id": "705966f9-7e33-45d1-9cf0-35ddad297aa2",
"name": "alphavantage - Balance Sheet",
"type": "n8n-nodes-base.httpRequest",
"position": [
976,
416
],
"parameters": {
"url": "=https://www.alphavantage.co/query?function=BALANCE_SHEET&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"typeVersion": 4.4
},
{
"id": "8682d4b9-a044-4565-a0c5-4c114c8303a9",
"name": "alphavantage - Profile",
"type": "n8n-nodes-base.httpRequest",
"position": [
976,
576
],
"parameters": {
"url": "=https://www.alphavantage.co/query?function=OVERVIEW&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"typeVersion": 4.4
},
{
"id": "6f43cf2e-7258-4f4e-b907-101d0a07eda4",
"name": "alphavantage - Income Statement",
"type": "n8n-nodes-base.httpRequest",
"position": [
976,
736
],
"parameters": {
"url": "=https://www.alphavantage.co/query?function=INCOME_STATEMENT&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"typeVersion": 4.4
},
{
"id": "819fcbdc-7ba3-498e-903b-3aac8257aed6",
"name": "Merge3",
"type": "n8n-nodes-base.merge",
"position": [
1392,
704
],
"parameters": {
"mode": "combine",
"options": {},
"joinMode": "keepEverything",
"fieldsToMatchString": "stock"
},
"typeVersion": 3.2
},
{
"id": "3abc3ce3-087b-439c-8bed-b0645971d84e",
"name": "Get row(s) in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1792,
1216
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{ $('loop_over_tickers').item.json.stock }}",
"lookupColumn": "stock"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk",
"cachedResultName": "Financial_Data_Cache"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "f35dc3c3-73ea-4e14-bd74-48f21425bf3a",
"name": "If2",
"type": "n8n-nodes-base.if",
"position": [
2080,
448
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "5ac312fa-a607-405d-9ab0-6993e2f45b19",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{$json.cacheHit}}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "069ebe17-84c4-485e-b767-ee898685af07",
"name": "Update row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
2288,
432
],
"parameters": {
"columns": {
"value": {
"stock": "={{ $json.stock }}",
"sector": "={{ $('Clean Read Financial').item.json.sector }}",
"f_score": "={{ $('Clean Read Financial').item.json.f_score }}",
"fcf_ttm": "={{ $('Clean Read Financial').item.json.fcf_ttm }}",
"row_number": 0,
"eps_current": "={{ $('Clean Read Financial').item.json.eps_current }}",
"revenue_ttm": "={{ $('Clean Read Financial').item.json.revenue_ttm }}",
"bvps_current": "={{ $('Clean Read Financial').item.json.bvps_current }}",
"cash_last_4q": "={{ $('Clean Read Financial').item.json.cash_last_4q }}",
"last_updated": "={{ new Date().toISOString().split(\"T\")[0] }}",
"current_price": "={{ $('Clean Read Financial').item.json.current_price }}",
"net_cash_flag": "={{ $('Clean Read Financial').item.json.net_cash_flag }}",
"net_income_ttm": "={{ $('Clean Read Financial').item.json.net_income_ttm }}",
"total_debt_ttm": "={{ $('Clean Read Financial').item.json.total_debt_ttm }}",
"f_score_data_ok": "={{ $('Clean Read Financial').item.json.f_score_data_ok }}",
"net_debt_latest": "={{ $('Clean Read Financial').item.json.net_debt_latest }}",
"revenue_last_4q": "={{ $('Clean Read Financial').item.json.revenue_last_4q }}",
"gross_margin_ttm": "={{ $('Clean Read Financial').item.json.gross_margin_ttm }}",
"net_income_last_4q": "={{ $('Clean Read Financial').item.json.net_income_last_4q }}",
"revenue_growth_yoy": "={{ $('Clean Read Financial').item.json.revenue_growth_yoy }}",
"shares_outstanding": "={{ $('Clean Read Financial').item.json.shares_outstanding }}",
"total_debt_last_4q": "={{ $('Clean Read Financial').item.json.total_debt_last_4q }}",
"gross_margin_last_4q": "={{ $('Clean Read Financial').item.json.gross_margin_last_4q }}",
"operating_margin_ttm": "={{ $('Clean Read Financial').item.json.operating_margin_ttm }}"
},
"schema": [
{
"id": "stock",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "stock",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cache_key",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "cache_key",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "data_source",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "data_source",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "last_updated",
"type": "string",
"display": true,
"required": false,
"displayName": "last_updated",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "eps_current",
"type": "string",
"display": true,
"required": false,
"displayName": "eps_current",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "bvps_current",
"type": "string",
"display": true,
"required": false,
"displayName": "bvps_current",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "shares_outstanding",
"type": "string",
"display": true,
"required": false,
"displayName": "shares_outstanding",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "revenue_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "revenue_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_debt_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "total_debt_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "gross_margin_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "gross_margin_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "operating_margin_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "operating_margin_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "fcf_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "fcf_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "revenue_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "revenue_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_income_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "net_income_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "gross_margin_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "gross_margin_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_debt_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "total_debt_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cash_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "cash_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_income_ttm",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "net_income_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "current_price",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "current_price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "revenue_growth_yoy",
"type": "string",
"display": true,
"required": false,
"displayName": "revenue_growth_yoy",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "f_score",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "f_score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "f_score_data_ok",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "f_score_data_ok",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "sector",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "sector",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_debt_latest",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "net_debt_latest",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_cash_flag",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "net_cash_flag",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"stock"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk",
"cachedResultName": "Financial_Data_Cache"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "308167a0-30f0-4216-894c-1e6abfeb4a22",
"name": "Insert Row",
"type": "n8n-nodes-base.googleSheets",
"position": [
2304,
896
],
"parameters": {
"columns": {
"value": {
"stock": "={{ $json.stock }}",
"sector": "={{ $json.sector }}",
"f_score": "={{ $json.f_score }}",
"fcf_ttm": "={{ $json.fcf_ttm }}",
"eps_current": "={{ $json.eps_current }}",
"revenue_ttm": "={{ $json.revenue_ttm }}",
"bvps_current": "={{ $json.bvps_current }}",
"cash_last_4q": "={{ $json.cash_last_4q }}",
"last_updated": "={{ new Date().toISOString().split(\"T\")[0] }}",
"current_price": "={{ $json.current_price }}",
"net_cash_flag": "={{ $json.net_cash_flag }}",
"net_income_ttm": "={{ $json.net_income_ttm }}",
"total_debt_ttm": "={{ $json.total_debt_ttm }}",
"f_score_data_ok": "={{ $json.f_score_data_ok }}",
"net_debt_latest": "={{ $json.net_debt_latest }}",
"revenue_last_4q": "={{ $json.revenue_last_4q }}",
"gross_margin_ttm": "={{ $json.gross_margin_ttm }}",
"net_income_last_4q": "={{ $json.net_income_last_4q }}",
"revenue_growth_yoy": "={{ $json.revenue_growth_yoy }}",
"shares_outstanding": "={{ $json.shares_outstanding }}",
"total_debt_last_4q": "={{ $json.total_debt_last_4q }}",
"gross_margin_last_4q": "={{ $json.gross_margin_last_4q }}",
"operating_margin_ttm": "={{ $json.operating_margin_ttm }}"
},
"schema": [
{
"id": "stock",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "stock",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cache_key",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "cache_key",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "data_source",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "data_source",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "last_updated",
"type": "string",
"display": true,
"required": false,
"displayName": "last_updated",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "eps_current",
"type": "string",
"display": true,
"required": false,
"displayName": "eps_current",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "bvps_current",
"type": "string",
"display": true,
"required": false,
"displayName": "bvps_current",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "shares_outstanding",
"type": "string",
"display": true,
"required": false,
"displayName": "shares_outstanding",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "revenue_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "revenue_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_debt_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "total_debt_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "gross_margin_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "gross_margin_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "operating_margin_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "operating_margin_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "fcf_ttm",
"type": "string",
"display": true,
"required": false,
"displayName": "fcf_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "revenue_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "revenue_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_income_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "net_income_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "gross_margin_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "gross_margin_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "total_debt_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "total_debt_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cash_last_4q",
"type": "string",
"display": true,
"required": false,
"displayName": "cash_last_4q",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_income_ttm",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "net_income_ttm",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "current_price",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "current_price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "revenue_growth_yoy",
"type": "string",
"display": true,
"required": false,
"displayName": "revenue_growth_yoy",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "f_score",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "f_score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "f_score_data_ok",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "f_score_data_ok",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "sector",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "sector",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_debt_latest",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "net_debt_latest",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "net_cash_flag",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "net_cash_flag",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"stock"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk",
"cachedResultName": "Financial_Data_Cache"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "31efd328-56ed-42fb-995f-0af16f09f28e",
"name": "alphavantage - CashFlow",
"type": "n8n-nodes-base.httpRequest",
"position": [
976,
912
],
"parameters": {
"url": "=https://www.alphavantage.co/query?function=CASH_FLOW&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"typeVersion": 4.4
},
{
"id": "95fe5434-3334-41f3-8f71-0115b01774ac",
"name": "Merge4",
"type": "n8n-nodes-base.merge",
"position": [
1728,
800
],
"parameters": {
"mode": "combine",
"options": {},
"fieldsToMatchString": "stock"
},
"typeVersion": 3.2
},
{
"id": "724d38b0-4b1a-4df7-900f-905217fa24b9",
"name": "Check the cache",
"type": "n8n-nodes-base.code",
"position": [
208,
768
],
"parameters": {
"jsCode": "const now = Date.now();\n\nconst row = items?.[0]?.json ?? null;\n\n// Read the manual refresh flag from Google Sheet or input\nconst rawNeedFreshFinancial =\n row?.need_fresh_financial ??\n $json.need_fresh_financial ??\n false;\n\n// Convert different formats (true/false, 1/0, yes/no) safely to boolean\nfunction toBoolean(value) {\n if (typeof value === 'boolean') return value;\n if (typeof value === 'number') return value === 1;\n if (typeof value === 'string') {\n const v = value.trim().toLowerCase();\n return ['true','1','yes','y'].includes(v);\n }\n return false;\n}\n\nconst needFreshFinancial = toBoolean(rawNeedFreshFinancial);\n\n// Check if cache exists\nconst cacheHit = !!(row && row.stock);\n\nlet shouldFetch = false;\nlet reason = '';\n\nif (!cacheHit) {\n shouldFetch = true;\n reason = 'cache_miss';\n} \nelse if (needFreshFinancial) {\n shouldFetch = true;\n reason = 'manual_financial_refresh';\n} \nelse {\n shouldFetch = false;\n reason = 'use_cached_financials';\n}\n\nreturn [{\n json: {\n stock: row?.stock || $json.stock,\n cacheHit,\n needFreshFinancial,\n shouldFetch,\n reason,\n cacheRow: row || null,\n nowISO: new Date(now).toISOString(),\n }\n}];"
},
"typeVersion": 2
},
{
"id": "7bc7610f-155f-4a3f-8087-ac00d61116c8",
"name": "Seekingalpha Articles",
"type": "n8n-nodes-base.httpRequest",
"position": [
496,
1760
],
"parameters": {
"url": "=https://seekingalpha.com/api/sa/combined/{{ $json.stock }}.xml",
"options": {
"response": {
"response": {
"responseFormat": "text",
"outputPropertyName": "sa_xml"
}
}
}
},
"typeVersion": 4.4
},
{
"id": "d2a2d325-97a4-40c4-8f73-a310cab253cd",
"name": "Clean balance sheet",
"type": "n8n-nodes-base.code",
"position": [
1184,
416
],
"parameters": {
"jsCode": "return {\n json: {\n stock: $json.stock || $json.Symbol || $json.symbol || $input.item.json.stock || \"UNKNOWN\",\n balance_data: $json\n }\n};"
},
"typeVersion": 2
},
{
"id": "e30294e8-4a3c-4a63-a468-54b623fea606",
"name": "Clean Profile",
"type": "n8n-nodes-base.code",
"position": [
1184,
576
],
"parameters": {
"jsCode": "return {\n json: {\n stock: $json.stock || $json.Symbol || $json.symbol || $input.item.json.stock || \"UNKNOWN\",\n overview_data: $json\n }\n};"
},
"typeVersion": 2
},
{
"id": "52731b5a-c076-497a-9e36-2a2f5aed8595",
"name": "Clean Income statement",
"type": "n8n-nodes-base.code",
"position": [
1184,
736
],
"parameters": {
"jsCode": "const inJson = $json || {};\nconst stock =\n inJson.stock ||\n inJson.Symbol ||\n inJson.symbol ||\n (items && items[0] && items[0].json && items[0].json.stock) ||\n \"UNKNOWN\";\n\nreturn [\n {\n json: {\n stock: stock,\n income_data: inJson\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "0d1e825c-fa1f-49fc-b119-e809b8dec76e",
"name": "Clean Ccashflow",
"type": "n8n-nodes-base.code",
"position": [
1168,
896
],
"parameters": {
"jsCode": "const inJson = $json || {};\nconst stock =\n inJson.stock ||\n inJson.Symbol ||\n inJson.symbol ||\n (items && items[0] && items[0].json && items[0].json.stock) ||\n \"UNKNOWN\";\n\nret
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.
googlePalmApigoogleSheetsOAuth2ApiopenAiApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
How this works
Investors and portfolio managers receive daily institutional-grade stock valuations with scenario price targets and clear buy, hold or sell signals. The workflow pulls tickers from Google Sheets, runs dual-model AI analysis through ChatGPT and Gemini to score risk and generate targets, then writes results back to the sheet before sending concise alerts via Telegram. This keeps research consistent and removes hours of manual modelling each day.
Use it for routine coverage of 20-50 holdings where daily updates add value; avoid it for illiquid names or single-stock deep dives that need bespoke fundamental work. Variations include changing the cron schedule for weekly runs or swapping Telegram for email delivery when sharing with larger teams.
About this workflow
AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets
Source: https://n8n.io/workflows/14059/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Overview This is a production-grade, fully automated stock analysis system built entirely in n8n. It combines institutional-level financial analysis, dual AI model consensus, and a self-improving back
A professional AI equity analysis automation built on n8n that transforms structured financial data and real-time news into disciplined, risk-adjusted price targets and actionable BUY/HOLD/SELL signal
Takes a product image from Google Sheets, adds frozen effect with Gemini, generates ASMR video with Veo3, writes captions with GPT-4o, and posts to 4 platforms automatically. Schedule trigger picks fi
Automatically captures a screenshot of a tech news homepage, extracts headlines into structured JSON, logs them in Google Sheets, and posts a daily trend report (7–10 bullet points) to Telegram at 07:
Beydigital Media – Lead Generation & AI Email Automation. Uses httpRequest, openAi, gmail, googleSheets. Scheduled trigger; 18 nodes.