Détection d'anomalies des prix d'actions et workflow de notification automatique des actualités connexes

Avancé

Ceci est unCrypto Trading, Multimodal AIworkflow d'automatisation du domainecontenant 26 nœuds.Utilise principalement des nœuds comme If, Set, Code, DeepL, Merge. Détection des anomalies boursières et envoi d'alertes d'actualités avec Marketstack, HackerNews et DeepL

Prérequis
  • Token Bot Slack ou URL Webhook
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "id": "72lks0Gu4Zqi4t0i",
  "meta": {
    "instanceId": "15d6057a37b8367f33882dd60593ee5f6cc0c59310ff1dc66b626d726083b48d",
    "templateCredsSetupCompleted": true
  },
  "name": "Stock Price Anomaly Detection and Related News Automatic Notification Workflow",
  "tags": [],
  "nodes": [
    {
      "id": "a69b05fc-16c6-4f68-bd14-717b647ad900",
      "name": "Vérification Quotidienne",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -544,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            },
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7ab68dfb-380e-4880-a948-89ad068e7533",
      "name": "Obtenir les Données Boursières",
      "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": {
          "id": "fQQ77wILan88cfRN",
          "name": "Marketstack account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e8b1795e-59dd-4c7a-9db0-78d3e3228b25",
      "name": "Calculer l'Écart",
      "type": "n8n-nodes-base.code",
      "position": [
        -96,
        0
      ],
      "parameters": {
        "jsCode": "// === パラメータ設定 ===\nconst N = 20;   // 移動平均の期間\nconst k = 2;    // σの倍率(±2σ)\n\n// === データ取得 ===\nlet data = [];\n\n// Marketstackの出力構造をチェック\nif (items[0].json.data) {\n  // 通常ケース(Marketstackノードからそのまま)\n  data = items[0].json.data;\n} else {\n  // 予備パターン:items配列にデータが直接並んでいるケース\n  data = items.map(item => item.json);\n}\n\n// N件だけ抽出\ndata = data.slice(0, N);\n\n// === 終値のみ抽出 ===\nconst closes = data.map(d => d.close).filter(v => typeof v === 'number');\n\n// データがない場合の対策\nif (closes.length === 0) {\n  return [{ json: { error: \"No valid close data found.\" } }];\n}\n\n// === 移動平均と標準偏差を計算 ===\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// === 最新値(先頭) ===\nconst latestClose = closes[0] + 100;\n\n// === 判定 ===\nlet status = \"normal\";\nif (latestClose > mean + k * sigma) {\n  status = \"high (above +2σ)\";\n} else if (latestClose < mean - k * sigma) {\n  status = \"low (below -2σ)\";\n}\n\n// === 出力 ===\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": "Anomalie ? (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": "Obtenir les Actualités Associées",
      "type": "n8n-nodes-base.hackerNews",
      "position": [
        800,
        96
      ],
      "parameters": {
        "resource": "all",
        "additionalFields": {
          "keyword": "={{ $json.keyword }}"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "31e2ca1d-9c30-4dbc-9a67-54e12ca974a3",
      "name": "Traduire les Actualités",
      "type": "n8n-nodes-base.deepL",
      "position": [
        1248,
        32
      ],
      "parameters": {
        "text": "={{ $json.message }}",
        "translateTo": "JA",
        "additionalFields": {}
      },
      "credentials": {
        "deepLApi": {
          "id": "S5c8ArKkbeIQQU5Z",
          "name": "DeepL account 3"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "478fdb62-dde4-40e3-9e0b-2676330f91c0",
      "name": "Envoyer une Alerte à Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1696,
        96
      ],
      "webhookId": "e0a002a8-b1df-41ff-a431-459120a37e77",
      "parameters": {
        "text": "=🌐 *Original (English)*  \n{{ $json.message }}\n\n---\n\n🇯🇵 *Translated (Japanese)*  \n{{ $json.text }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "CKUCBTG0H",
          "cachedResultName": "general"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "2kyw7TSL5VT4S3md",
          "name": "Slack account 8"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "7ea2b37e-e074-400d-9e3b-28ac0bc9c816",
      "name": "Envoyer un Rapport Normal à Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        352,
        -96
      ],
      "webhookId": "b0f44cfc-ae30-4339-9428-c4fb468f900e",
      "parameters": {
        "text": "=✅ 異常なし この銘柄の終値は安定しています。 現在値:{{ $('Calculate Deviation').item.json.latest }}(平均 {{ $('Calculate Deviation').item.json.mean }} ± {{ $('Calculate Deviation').item.json.sigma }})",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "CKUCBTG0H",
          "cachedResultName": "general"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "2kyw7TSL5VT4S3md",
          "name": "Slack account 8"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "34c1613d-306b-47fa-b982-db156c54e053",
      "name": "Fusionner Original + Traduit",
      "type": "n8n-nodes-base.merge",
      "position": [
        1472,
        96
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "be7b89c0-99ac-44cb-aec0-03b844bc50f5",
      "name": "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": "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 ≥ 20 for a stable mean/σ."
      },
      "typeVersion": 1
    },
    {
      "id": "e85baa53-b0ed-4017-98b0-e7a07a88c7aa",
      "name": "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": "Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -112,
        496
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 384,
        "content": "## Node Structure Overview\n\nFlow:\n🕘 Daily Check (09:00) \n → 📊 Get Stock Data (Marketstack)\n → 🧮 Calculate Deviation (±2σ)\n → 🔀 IF: Is Anomaly? (status != \"normal\")\n\nTrue (Anomaly):\n  → 📰 Get Related News (Hacker News)\n  → ✍️ Format News (Title + Summary + URL)\n  → 🌐 Translate News (EN → JA, DeepL)\n  → 🔗 Merge Original + Translated\n  → 💬 Send Alert to Slack\n\nFalse (Normal):\n  → 💬 Send Normal Report to Slack\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fa231074-0537-4cc6-b33c-e90006605377",
      "name": "Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        320,
        352
      ],
      "parameters": {
        "color": 7,
        "height": 176,
        "content": "## Send Normal Report to Slack\n\nSends a concise “no anomaly” message with basic stats.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2c8bbc35-0320-4350-b64f-e58eacd7c974",
      "name": "Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 272,
        "content": "## 🛠️ Setup Instructions\n\n1. Connect your **Marketstack**, **DeepL**, and **Slack** credentials.\n2. Update ticker symbol inside “Get Stock Data”.\n3. Customize Slack channel in both Slack nodes.\n4. Modify time or schedule in “Daily Check” as needed.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7fbb2805-1ffd-4ce6-86a6-04d9ba827191",
      "name": "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’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 **posts both original and translated text to Slack**. When no anomaly is detected, it sends a concise “normal” report.\n\n## How it works\n1) Daily trigger at 09:00 JST  \n2) Marketstack: fetch EOD data  \n3) Code: compute mean/σ and classify (normal/high/low)  \n4) IF: anomaly? → yes = news path / no = normal report  \n5) Hacker News: search related items  \n6) DeepL: translate EN → 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": "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σ), or low (−2σ)."
      },
      "typeVersion": 1
    },
    {
      "id": "d3e597a0-0400-400d-970d-f179bf3c574c",
      "name": "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 ±2σ."
      },
      "typeVersion": 1
    },
    {
      "id": "cb2c48c9-349d-4c3e-9f17-ed4636ca0b82",
      "name": "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": "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": "Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1232,
        -208
      ],
      "parameters": {
        "color": 7,
        "width": 208,
        "height": 176,
        "content": "## Translate News (EN → JA)\n\nDeepL translation to Japanese. Change target_lang if needed."
      },
      "typeVersion": 1
    },
    {
      "id": "4a410b17-9763-4723-a5f2-61fb6ab50fd7",
      "name": "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": "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, σ) and related news."
      },
      "typeVersion": 1
    },
    {
      "id": "fe474a5c-9358-4f1b-ad07-b2ae6169c280",
      "name": "Ajouter un Champ Symbole",
      "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": "Composer le Message pour Slack",
      "type": "n8n-nodes-base.code",
      "position": [
        1024,
        96
      ],
      "parameters": {
        "jsCode": "// === Hacker News 出力からタイトル・本文・URLを整形 ===\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タグ(<em>など)を除去 ===\n  const cleanTitle = title.replace(/<[^>]*>/g, \"\");\n  const cleanStory = story.replace(/<[^>]*>/g, \"\");\n\n  return `📰 ${cleanTitle}\\n${cleanStory}\\n🔗 ${url}`;\n});\n\n// === 出力 ===\nreturn [\n  {\n    json: {\n      message: results.slice(0, 3).join(\"\\n\\n---\\n\\n\"), // 上位3件を区切って結合\n    },\n  },\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a5ecf56c-8e7d-42da-9335-a2b1a08b71a1",
      "name": "Construire le Mot-clé d'Actualité",
      "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// 入力の中身を確認\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が無い時は理由と入力を返して可視化\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,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "e90eb5a4-58e8-4a2d-82c3-30862bc8832f",
  "connections": {
    "a69b05fc-16c6-4f68-bd14-717b647ad900": {
      "main": [
        [
          {
            "node": "7ab68dfb-380e-4880-a948-89ad068e7533",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7ab68dfb-380e-4880-a948-89ad068e7533": {
      "main": [
        [
          {
            "node": "e8b1795e-59dd-4c7a-9db0-78d3e3228b25",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "31e2ca1d-9c30-4dbc-9a67-54e12ca974a3": {
      "main": [
        [
          {
            "node": "34c1613d-306b-47fa-b982-db156c54e053",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fe474a5c-9358-4f1b-ad07-b2ae6169c280": {
      "main": [
        [
          {
            "node": "a5ecf56c-8e7d-42da-9335-a2b1a08b71a1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "830c1c3b-92d7-42c2-a698-ec213787ace9": {
      "main": [
        [
          {
            "node": "ad957b08-b108-427e-9843-b48be879cb3b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a5ecf56c-8e7d-42da-9335-a2b1a08b71a1": {
      "main": [
        [
          {
            "node": "830c1c3b-92d7-42c2-a698-ec213787ace9",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "e8b1795e-59dd-4c7a-9db0-78d3e3228b25": {
      "main": [
        [
          {
            "node": "5f5c9951-f936-495d-985f-7ae2108d2787",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "478fdb62-dde4-40e3-9e0b-2676330f91c0": {
      "main": [
        []
      ]
    },
    "ad957b08-b108-427e-9843-b48be879cb3b": {
      "main": [
        [
          {
            "node": "31e2ca1d-9c30-4dbc-9a67-54e12ca974a3",
            "type": "main",
            "index": 0
          },
          {
            "node": "34c1613d-306b-47fa-b982-db156c54e053",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "34c1613d-306b-47fa-b982-db156c54e053": {
      "main": [
        [
          {
            "node": "478fdb62-dde4-40e3-9e0b-2676330f91c0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5f5c9951-f936-495d-985f-7ae2108d2787": {
      "main": [
        [
          {
            "node": "fe474a5c-9358-4f1b-ad07-b2ae6169c280",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "7ea2b37e-e074-400d-9e3b-28ac0bc9c816",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Foire aux questions

Comment utiliser ce workflow ?

Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.

Dans quelles scénarios ce workflow est-il adapté ?

Avancé - Trading crypto, IA Multimodale

Est-ce payant ?

Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.

Informations sur le workflow
Niveau de difficulté
Avancé
Nombre de nœuds26
Catégorie2
Types de nœuds10
Description de la difficulté

Adapté aux utilisateurs avancés, avec des workflows complexes contenant 16+ nœuds

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34