This workflow corresponds to n8n.io template #10306 — we link there as the canonical source.
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": "72lks0Gu4Zqi4t0i",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Stock Price Anomaly Detection and Related News Automatic Notification Workflow",
"tags": [],
"nodes": [
{
"id": "a69b05fc-16c6-4f68-bd14-717b647ad900",
"name": "Daily Check",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-544,
0
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
},
{}
]
}
},
"typeVersion": 1.2
},
{
"id": "7ab68dfb-380e-4880-a948-89ad068e7533",
"name": "Get Stock Data",
"type": "n8n-nodes-base.marketstack",
"position": [
-320,
0
],
"parameters": {
"filters": {
"dateTo": "2025-09-30T00:00:00",
"latest": false,
"dateFrom": "2025-09-01T00:00:00"
},
"symbols": "AMZN"
},
"credentials": {
"marketstackApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "e8b1795e-59dd-4c7a-9db0-78d3e3228b25",
"name": "Calculate Deviation",
"type": "n8n-nodes-base.code",
"position": [
-96,
0
],
"parameters": {
"jsCode": "// === \u30d1\u30e9\u30e1\u30fc\u30bf\u8a2d\u5b9a ===\nconst N = 20; // \u79fb\u52d5\u5e73\u5747\u306e\u671f\u9593\nconst k = 2; // \u03c3\u306e\u500d\u7387\uff08\u00b12\u03c3\uff09\n\n// === \u30c7\u30fc\u30bf\u53d6\u5f97 ===\nlet data = [];\n\n// Marketstack\u306e\u51fa\u529b\u69cb\u9020\u3092\u30c1\u30a7\u30c3\u30af\nif (items[0].json.data) {\n // \u901a\u5e38\u30b1\u30fc\u30b9\uff08Marketstack\u30ce\u30fc\u30c9\u304b\u3089\u305d\u306e\u307e\u307e\uff09\n data = items[0].json.data;\n} else {\n // \u4e88\u5099\u30d1\u30bf\u30fc\u30f3\uff1aitems\u914d\u5217\u306b\u30c7\u30fc\u30bf\u304c\u76f4\u63a5\u4e26\u3093\u3067\u3044\u308b\u30b1\u30fc\u30b9\n data = items.map(item => item.json);\n}\n\n// N\u4ef6\u3060\u3051\u62bd\u51fa\ndata = data.slice(0, N);\n\n// === \u7d42\u5024\u306e\u307f\u62bd\u51fa ===\nconst closes = data.map(d => d.close).filter(v => typeof v === 'number');\n\n// \u30c7\u30fc\u30bf\u304c\u306a\u3044\u5834\u5408\u306e\u5bfe\u7b56\nif (closes.length === 0) {\n return [{ json: { error: \"No valid close data found.\" } }];\n}\n\n// === \u79fb\u52d5\u5e73\u5747\u3068\u6a19\u6e96\u504f\u5dee\u3092\u8a08\u7b97 ===\nconst mean = closes.reduce((a, b) => a + b, 0) / closes.length;\nconst variance = closes.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / closes.length;\nconst sigma = Math.sqrt(variance);\n\n// === \u6700\u65b0\u5024\uff08\u5148\u982d\uff09 ===\nconst latestClose = closes[0] + 100;\n\n// === \u5224\u5b9a ===\nlet status = \"normal\";\nif (latestClose > mean + k * sigma) {\n status = \"high (above +2\u03c3)\";\n} else if (latestClose < mean - k * sigma) {\n status = \"low (below -2\u03c3)\";\n}\n\n// === \u51fa\u529b ===\nreturn [\n {\n json: {\n mean: mean.toFixed(2),\n sigma: sigma.toFixed(2),\n upper: (mean + k * sigma).toFixed(2),\n lower: (mean - k * sigma).toFixed(2),\n latest: latestClose.toFixed(2),\n status,\n count: closes.length\n }\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "5f5c9951-f936-495d-985f-7ae2108d2787",
"name": "Is Anomaly? (status != \"normal\")",
"type": "n8n-nodes-base.if",
"position": [
128,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f79f5888-e19f-4273-81b7-5b6f434fea65",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{$json[\"status\"]}}",
"rightValue": "=normal"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "830c1c3b-92d7-42c2-a698-ec213787ace9",
"name": "Get Related News",
"type": "n8n-nodes-base.hackerNews",
"position": [
800,
96
],
"parameters": {
"resource": "all",
"additionalFields": {
"keyword": "={{ $json.keyword }}"
}
},
"typeVersion": 1
},
{
"id": "31e2ca1d-9c30-4dbc-9a67-54e12ca974a3",
"name": "Translate News",
"type": "n8n-nodes-base.deepL",
"position": [
1248,
32
],
"parameters": {
"text": "={{ $json.message }}",
"translateTo": "JA",
"additionalFields": {}
},
"credentials": {
"deepLApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "478fdb62-dde4-40e3-9e0b-2676330f91c0",
"name": "Send Alert to Slack",
"type": "n8n-nodes-base.slack",
"position": [
1696,
96
],
"parameters": {
"text": "=\ud83c\udf10 *Original (English)* \n{{ $json.message }}\n\n---\n\n\ud83c\uddef\ud83c\uddf5 *Translated (Japanese)* \n{{ $json.text }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "CKUCBTG0H",
"cachedResultName": "general"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "7ea2b37e-e074-400d-9e3b-28ac0bc9c816",
"name": "Send Normal Report to Slack",
"type": "n8n-nodes-base.slack",
"position": [
352,
-96
],
"parameters": {
"text": "=\u2705 \u7570\u5e38\u306a\u3057 \u3053\u306e\u9298\u67c4\u306e\u7d42\u5024\u306f\u5b89\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002 \u73fe\u5728\u5024\uff1a{{ $('Calculate Deviation').item.json.latest }}\uff08\u5e73\u5747 {{ $('Calculate Deviation').item.json.mean }} \u00b1 {{ $('Calculate Deviation').item.json.sigma }})",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "CKUCBTG0H",
"cachedResultName": "general"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.3
},
{
"id": "34c1613d-306b-47fa-b982-db156c54e053",
"name": "Merge Original + Translated",
"type": "n8n-nodes-base.merge",
"position": [
1472,
96
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "be7b89c0-99ac-44cb-aec0-03b844bc50f5",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-640,
-224
],
"parameters": {
"color": 7,
"width": 224,
"height": 192,
"content": "## Daily Check (09:00 JST)\n\nStarts the workflow every morning at 09:00 JST. Adjust schedule/timezone as needed."
},
"typeVersion": 1
},
{
"id": "2ea66681-b507-4737-a6f4-0d0c2590646f",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-400,
208
],
"parameters": {
"color": 7,
"height": 192,
"content": "## Get Stock Data (Marketstack)\nRetrieves the latest EOD prices for the configured ticker. Edit symbol and date range. Keep the limit \u2265 20 for a stable mean/\u03c3."
},
"typeVersion": 1
},
{
"id": "e85baa53-b0ed-4017-98b0-e7a07a88c7aa",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1472,
304
],
"parameters": {
"color": 7,
"width": 624,
"height": 256,
"content": "## Customization Tips\n\n- Monitor multiple tickers: duplicate **Get Stock Data** and merge results.\n- Replace Hacker News with **NewsAPI** / **Google News RSS** for broader coverage.\n- Switch Slack to **Telegram / Discord / Teams / LINE Messaging API**.\n- Add a **Set (Fields)** node to centralize user-configurable variables:\n - `symbol` (ticker), `days` (N), `sigmaK` (k), `newsQuery`, `slackChannel`\n- Add an **IF** to translate only when language != JA (cost optimization).\n\n\n"
},
"typeVersion": 1
},
{
"id": "22cad535-6c1f-44c1-9018-910baa276cba",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-112,
496
],
"parameters": {
"color": 7,
"width": 400,
"height": 384,
"content": "## Node Structure Overview\n\nFlow:\n\ud83d\udd58 Daily Check (09:00) \n \u2192 \ud83d\udcca Get Stock Data (Marketstack)\n \u2192 \ud83e\uddee Calculate Deviation (\u00b12\u03c3)\n \u2192 \ud83d\udd00 IF: Is Anomaly? (status != \"normal\")\n\nTrue (Anomaly):\n \u2192 \ud83d\udcf0 Get Related News (Hacker News)\n \u2192 \u270d\ufe0f Format News (Title + Summary + URL)\n \u2192 \ud83c\udf10 Translate News (EN \u2192 JA, DeepL)\n \u2192 \ud83d\udd17 Merge Original + Translated\n \u2192 \ud83d\udcac Send Alert to Slack\n\nFalse (Normal):\n \u2192 \ud83d\udcac Send Normal Report to Slack\n"
},
"typeVersion": 1
},
{
"id": "fa231074-0537-4cc6-b33c-e90006605377",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
320,
352
],
"parameters": {
"color": 7,
"height": 176,
"content": "## Send Normal Report to Slack\n\nSends a concise \u201cno anomaly\u201d message with basic stats.\n"
},
"typeVersion": 1
},
{
"id": "2c8bbc35-0320-4350-b64f-e58eacd7c974",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-320,
-448
],
"parameters": {
"color": 7,
"width": 304,
"height": 272,
"content": "## \ud83d\udee0\ufe0f Setup Instructions\n\n1. Connect your **Marketstack**, **DeepL**, and **Slack** credentials.\n2. Update ticker symbol inside \u201cGet Stock Data\u201d.\n3. Customize Slack channel in both Slack nodes.\n4. Modify time or schedule in \u201cDaily Check\u201d as needed.\n"
},
"typeVersion": 1
},
{
"id": "7fbb2805-1ffd-4ce6-86a6-04d9ba827191",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1184,
-288
],
"parameters": {
"width": 480,
"height": 816,
"content": "## Price Anomaly Detection & News Alert (Marketstack + HN + DeepL + Slack)\n\n## Overview\nThis workflow monitors a stock\u2019s closing price via **Marketstack**. It computes a **20-day moving average** and **standard deviation (\u00b12\u03c3)**. If the latest close is outside \u00b12\u03c3, it flags an **anomaly**, fetches **related headlines from Hacker News**, **translates** them to Japanese with **DeepL**, and **posts both original and translated text to Slack**. When no anomaly is detected, it sends a concise \u201cnormal\u201d report.\n\n## How it works\n1) Daily trigger at 09:00 JST \n2) Marketstack: fetch EOD data \n3) Code: compute mean/\u03c3 and classify (normal/high/low) \n4) IF: anomaly? \u2192 yes = news path / no = normal report \n5) Hacker News: search related items \n6) DeepL: translate EN \u2192 JA \n7) Slack: send bilingual notification\n\n## Requirements\n- Marketstack API key\n- DeepL API key\n- Slack OAuth2 (bot token / channel permission)\n\n## Notes\n- Edit the ticker in **Get Stock Data**.\n- Adjust **N** (days) and **k** (sigma multiplier) in **Calculate Deviation**.\n- Keep credentials out of HTTP nodes (use n8n Credentials).\n"
},
"typeVersion": 1
},
{
"id": "4a60a88c-3f9d-4919-9f8e-769138eebad2",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-192,
-160
],
"parameters": {
"color": 7,
"width": 256,
"content": "## Calculate Deviation\n\nComputes 20-day mean and standard deviation; classifies the latest close as normal, high (+2\u03c3), or low (\u22122\u03c3)."
},
"typeVersion": 1
},
{
"id": "d3e597a0-0400-400d-970d-f179bf3c574c",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
32,
176
],
"parameters": {
"color": 7,
"width": 256,
"height": 176,
"content": "## Is Anomaly? (status != \"normal\")\n\nBranches to the news path only when the latest close exceeds \u00b12\u03c3."
},
"typeVersion": 1
},
{
"id": "cb2c48c9-349d-4c3e-9f17-ed4636ca0b82",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
720,
288
],
"parameters": {
"color": 7,
"width": 256,
"height": 176,
"content": "## Get Related News (Hacker News)\n\nSearches articles by the ticker/company keyword. You can switch to NewsAPI/Google RSS."
},
"typeVersion": 1
},
{
"id": "8a824d8c-3d66-4838-9c5e-a8708afcfa1d",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
944,
-208
],
"parameters": {
"color": 7,
"width": 256,
"height": 176,
"content": "## Format News (Title + Summary + URL)\n\nCleans HTML (e.g., <em>) and formats top items for translation/Slack."
},
"typeVersion": 1
},
{
"id": "1ef7949a-e396-449a-b21c-4e72e7031c38",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
1232,
-208
],
"parameters": {
"color": 7,
"width": 208,
"height": 176,
"content": "## Translate News (EN \u2192 JA)\n\nDeepL translation to Japanese. Change target_lang if needed."
},
"typeVersion": 1
},
{
"id": "4a410b17-9763-4723-a5f2-61fb6ab50fd7",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
1504,
-208
],
"parameters": {
"color": 7,
"width": 208,
"height": 176,
"content": "## Merge Original + Translated\n\nAppends original English and translated Japanese into one message payload."
},
"typeVersion": 1
},
{
"id": "0d5d6b48-6983-4136-80e3-0cf9de926972",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"position": [
1744,
-208
],
"parameters": {
"color": 7,
"width": 208,
"height": 176,
"content": "## Send Alert to Slack (Anomaly)\n\nSends bilingual alert with stats (latest, mean, \u03c3) and related news."
},
"typeVersion": 1
},
{
"id": "fe474a5c-9358-4f1b-ad07-b2ae6169c280",
"name": "Add Symbol Field",
"type": "n8n-nodes-base.set",
"position": [
352,
96
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a1de4889-f506-46c6-8189-4e173dad6c39",
"name": "symbol",
"type": "string",
"value": "={{$item(0).$node[\"Get Stock Data\"].json[\"symbol\"]}}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ad957b08-b108-427e-9843-b48be879cb3b",
"name": "Compose Slack Message",
"type": "n8n-nodes-base.code",
"position": [
1024,
96
],
"parameters": {
"jsCode": "// === Hacker News \u51fa\u529b\u304b\u3089\u30bf\u30a4\u30c8\u30eb\u30fb\u672c\u6587\u30fbURL\u3092\u6574\u5f62 ===\nconst results = items.map(item => {\n const highlight = item.json._highlightResult || {};\n\n const title =\n highlight.title?.value ||\n item.json.title ||\n \"No Title\";\n\n const story =\n highlight.story_text?.value ||\n item.json.story_text ||\n \"No summary available.\";\n\n const url =\n item.json.url ||\n highlight.url?.value ||\n \"No URL\";\n\n // === HTML\u30bf\u30b0\uff08<em>\u306a\u3069\uff09\u3092\u9664\u53bb ===\n const cleanTitle = title.replace(/<[^>]*>/g, \"\");\n const cleanStory = story.replace(/<[^>]*>/g, \"\");\n\n return `\ud83d\udcf0 ${cleanTitle}\\n${cleanStory}\\n\ud83d\udd17 ${url}`;\n});\n\n// === \u51fa\u529b ===\nreturn [\n {\n json: {\n message: results.slice(0, 3).join(\"\\n\\n---\\n\\n\"), // \u4e0a\u4f4d3\u4ef6\u3092\u533a\u5207\u3063\u3066\u7d50\u5408\n },\n },\n];\n"
},
"typeVersion": 2
},
{
"id": "a5ecf56c-8e7d-42da-9335-a2b1a08b71a1",
"name": "Build News Keyword",
"type": "n8n-nodes-base.code",
"position": [
576,
96
],
"parameters": {
"jsCode": "const map = {\n AMZN: 'Amazon',\n AAPL: 'Apple',\n GOOG: 'Google',\n MSFT: 'Microsoft',\n TSLA: 'Tesla',\n};\n\n// \u5165\u529b\u306e\u4e2d\u8eab\u3092\u78ba\u8a8d\nconsole.log('INPUT items:', items);\n\nconst first = (items && items[0] && items[0].json) ? items[0].json : {};\nconst symbol = String(first.symbol || first.ticker || '').toUpperCase();\n\nif (!symbol) {\n // symbol\u304c\u7121\u3044\u6642\u306f\u7406\u7531\u3068\u5165\u529b\u3092\u8fd4\u3057\u3066\u53ef\u8996\u5316\n return [\n {\n json: {\n error: 'symbol is missing',\n debugInput: first,\n },\n },\n ];\n}\n\nconst keyword = map[symbol] || symbol;\n\nreturn [\n {\n json: { keyword, symbol },\n },\n];\n"
},
"typeVersion": 2
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "e90eb5a4-58e8-4a2d-82c3-30862bc8832f",
"connections": {
"Daily Check": {
"main": [
[
{
"node": "Get Stock Data",
"type": "main",
"index": 0
}
]
]
},
"Get Stock Data": {
"main": [
[
{
"node": "Calculate Deviation",
"type": "main",
"index": 0
}
]
]
},
"Translate News": {
"main": [
[
{
"node": "Merge Original + Translated",
"type": "main",
"index": 0
}
]
]
},
"Add Symbol Field": {
"main": [
[
{
"node": "Build News Keyword",
"type": "main",
"index": 0
}
]
]
},
"Get Related News": {
"main": [
[
{
"node": "Compose Slack Message",
"type": "main",
"index": 0
}
]
]
},
"Build News Keyword": {
"main": [
[
{
"node": "Get Related News",
"type": "main",
"index": 0
}
]
]
},
"Calculate Deviation": {
"main": [
[
{
"node": "Is Anomaly? (status != \"normal\")",
"type": "main",
"index": 0
}
]
]
},
"Send Alert to Slack": {
"main": [
[]
]
},
"Compose Slack Message": {
"main": [
[
{
"node": "Translate News",
"type": "main",
"index": 0
},
{
"node": "Merge Original + Translated",
"type": "main",
"index": 1
}
]
]
},
"Merge Original + Translated": {
"main": [
[
{
"node": "Send Alert to Slack",
"type": "main",
"index": 0
}
]
]
},
"Is Anomaly? (status != \"normal\")": {
"main": [
[
{
"node": "Add Symbol Field",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Normal Report to Slack",
"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.
deepLApimarketstackApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow monitors a stock’s closing price via Marketstack. It computes a 20-day moving average and standard deviation (±2σ). If the latest close is outside ±2σ, it flags an anomaly, fetches related headlines from Hacker News, translates them to Japanese with DeepL, and…
Source: https://n8n.io/workflows/10306/ — 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.
This workflow is designed for engineering teams, project managers, and IT operations who need consistent visibility into team availability across multiple projects. It’s perfect for organizations that
This workflow is an automated system that tracks End-of-Life (EOL) dates for software and technologies used across your projects. It eliminates the need to manually monitor EOL dates in spreadsheets o
This workflow continuously monitors the Meta Ads Library for new creatives from a specific competitor pages, logs them into Google Sheets, and sends a concise Telegram notification with the number of
Enhance financial oversight with this automated n8n workflow. Triggered every 5 minutes, it fetches real-time bank transactions via an API, enriches and transforms the data, and applies smart logic to
This workflow automates competitive price intelligence using Bright Data's enterprise web scraping API. On a scheduled basis (default: daily at 9 AM), the system loops through configured competitor pr