{
  "id": "XHT9RtBGp20npYUj",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Forex Risk Intelligence Pipeline",
  "tags": [],
  "nodes": [
    {
      "id": "922f3200-b29c-4404-8614-904bbd905e0d",
      "name": "Forex News RSS Trigger",
      "type": "n8n-nodes-base.rssFeedReadTrigger",
      "position": [
        0,
        -176
      ],
      "parameters": {
        "feedUrl": "https://www.forexlive.com/feed/",
        "pollTimes": {
          "item": [
            {
              "mode": "custom",
              "cronExpression": "*/5 * * * *"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7900199b-46a7-42a0-a88f-724ecf3a04ab",
      "name": "Normalize News Data",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        -176
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "63c7028a-a40e-4cc7-b4fe-1c150d013e7d",
              "name": "title",
              "type": "string",
              "value": "={{ $json.title }}"
            },
            {
              "id": "88dc0159-4164-46ae-83ed-d1c663c3f254",
              "name": "description",
              "type": "string",
              "value": "={{ $json.contentSnippet }}"
            },
            {
              "id": "fa0434bc-dd8c-4314-990a-1d996c657bcf",
              "name": "link",
              "type": "string",
              "value": "={{ $json.link }}"
            },
            {
              "id": "872d1276-2e9f-4947-9b14-8c6a0646bbfc",
              "name": "date",
              "type": "string",
              "value": "={{ $json.isoDate }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "589988db-0ea0-416e-9e32-ecce1c5ae1d5",
      "name": "Relevance Analyzer",
      "type": "n8n-nodes-base.code",
      "position": [
        448,
        -176
      ],
      "parameters": {
        "jsCode": "// Safe combine (handles undefined + trims)\nconst text = (\n  ($json.title || \"\") + \" \" + ($json.description || \"\")\n).toLowerCase().trim();\n\n// Debug (VERY IMPORTANT)\nconsole.log(\"TEXT:\", text);\n\nlet keywords = [\n  \"inflation\",\n  \"interest\",\n  \"rate\",\n  \"usd\",\n  \"inr\",\n  \"fed\",\n  \"fomc\",\n  \"rbi\",\n  \"central bank\",\n  \"monetary\",\n  \"policy\",\n  \"gdp\",\n  \"economy\",\n  \"trade\",\n  \"exports\",\n  \"imports\",\n  \"deficit\",\n  \"surplus\",\n  \"oil\",\n  \"energy\"\n];\n\nlet matched = [];\n\nfor (let word of keywords) {\n  if (text.includes(word)) {\n    matched.push(word);\n  }\n}\n\nlet isRelevant = matched.length > 0;\n\nreturn [\n  {\n    json: {\n      ...$json,\n      is_relevant: isRelevant,\n      matched_keywords: matched,\n      filter_reason: isRelevant\n        ? `Relevant due to: ${matched.join(\", \")}`\n        : \"Not relevant: No forex-related keywords found\"\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "b8b92a6f-6b18-47f7-9f91-e30d185f3fbc",
      "name": "Forex Relevance Check",
      "type": "n8n-nodes-base.if",
      "position": [
        672,
        -176
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "506c7a29-4b82-4c20-b76b-f928eb5601ec",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json[\"is_relevant\"] === true }}",
              "rightValue": ""
            },
            {
              "id": "b95ffaf2-0b6d-4a35-8996-3b3a28adf51a",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "e5804713-0afa-43a6-be57-b1bc1af19e0e",
      "name": "AI Forex Risk Analyzer",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        896,
        -256
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "GPT-4.1-MINI"
        },
        "options": {},
        "responses": {
          "values": [
            {
              "content": "=You are a senior forex risk analyst.\n\nAnalyze the news below and return STRICT JSON only.\n\n{\n  \"summary\": \"...\",\n  \"currency_pairs\": [\"USD/INR\"],\n  \"impact\": \"...\",\n  \"risk_level\": \"...\",\n  \"confidence_score\": integer between 0 and 100\",\n  \"reason\": \"...\",\n  \"recommended_action\": \"...\"\n}\n\nNews:\nTitle: {{$json[\"title\"]}}\nDescription: {{ $json.description }}"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "c3f73aa8-4cd5-4b43-864a-23d16a869862",
      "name": "Merge News + AI Output",
      "type": "n8n-nodes-base.merge",
      "position": [
        1248,
        -176
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "37cc0333-3dc5-4f5b-8a6f-41bcff39cb24",
      "name": "Parse AI Response",
      "type": "n8n-nodes-base.code",
      "position": [
        1472,
        -176
      ],
      "parameters": {
        "jsCode": "// Step 1: Safely extract AI text from ANY structure\nlet raw = \"\";\n\n// Try multiple possible paths (robust handling)\nif ($json.output?.[0]?.content?.[0]?.text) {\n  raw = $json.output[0].content[0].text;\n} else if ($json.text) {\n  raw = $json.text;\n} else if ($json.message?.content) {\n  raw = $json.message.content;\n}\n\n// Fail if nothing found\nif (!raw) {\n  throw new Error(\"No AI output found in response\");\n}\n\n// Step 2: Clean markdown\nraw = raw.replace(/```json/g, \"\").replace(/```/g, \"\").trim();\n\n// Step 3: Parse JSON\nlet parsed;\ntry {\n  parsed = JSON.parse(raw);\n} catch (err) {\n  console.log(\"RAW OUTPUT:\", raw);\n  throw new Error(\"Invalid JSON from AI\");\n}\n\n// Step 4: Merge with ORIGINAL data (CRITICAL)\nreturn [\n  {\n    json: {\n      ...$json,   // keeps title, description, etc.\n      ...parsed   // adds AI output\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "98d35a4c-6c42-46d9-aa74-02fa36a4a4bf",
      "name": "Risk Scoring Engine",
      "type": "n8n-nodes-base.code",
      "position": [
        1696,
        -176
      ],
      "parameters": {
        "jsCode": "// ===== Reuse existing data =====\nconst data = $json;\n\n// ===== Risk Scoring =====\nlet score = data.confidence_score || 50;\n\nif (data.risk_level?.toLowerCase() === \"high\") score += 20;\nelse if (data.risk_level?.toLowerCase() === \"medium\") score += 10;\nelse score -= 10;\n\nif (data.impact?.toLowerCase().includes(\"bearish\")) score += 5;\n\nif (score > 100) score = 100;\nif (score < 0) score = 0;\n\n// ===== Priority =====\nlet priority = \"LOW\";\nif (score >= 80) priority = \"HIGH\";\nelse if (score >= 60) priority = \"MEDIUM\";\n\n// ===== Dedupe Key =====\nconst title = data.title || \"\";\nconst dedupe_key = title\n  .toLowerCase()\n  .replace(/[^\\w\\s]/g, \"\")\n  .trim();\n\n// ===== Return =====\nreturn [\n  {\n    json: {\n      ...data,\n      final_score: score,\n      priority,\n      dedupe_key\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "61eefd94-f642-4141-aad6-da1b45be67b2",
      "name": "Check Duplicate News",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1920,
        -176
      ],
      "parameters": {
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "keyName": "key",
              "keyValue": "={{ $json.dedupe_key }}"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "Jufr3QqRJ9OCTS9V",
          "cachedResultUrl": "/projects/yWWlFm5a4icF4iCq/datatables/Jufr3QqRJ9OCTS9V",
          "cachedResultName": "forex_memory"
        }
      },
      "typeVersion": 1.1,
      "alwaysOutputData": true
    },
    {
      "id": "d7a7dd03-e883-49f8-beca-7f59dd82b075",
      "name": "Duplicate Decision",
      "type": "n8n-nodes-base.if",
      "position": [
        2144,
        -176
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1c1866e0-1540-439f-8cae-2429944f0847",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json }}",
              "rightValue": ""
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "32b85653-73c3-4589-8c95-8728b2808404",
      "name": "Store Processed News",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        2368,
        -272
      ],
      "parameters": {
        "columns": {
          "value": {
            "key": "={{ $('Risk Scoring Engine').item.json.dedupe_key }}",
            "value": "={{ $('Risk Scoring Engine').item.json.summary }}"
          },
          "schema": [
            {
              "id": "key",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "key",
              "defaultMatch": false
            },
            {
              "id": "value",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "value",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "key"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "Jufr3QqRJ9OCTS9V",
          "cachedResultUrl": "/projects/yWWlFm5a4icF4iCq/datatables/Jufr3QqRJ9OCTS9V",
          "cachedResultName": "forex_memory"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "a0c91008-b409-4261-a370-205acf7325ce",
      "name": "High Priority Filter",
      "type": "n8n-nodes-base.if",
      "position": [
        2368,
        -80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ecd541f4-f515-4b19-8220-0bb9152a1e3f",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Risk Scoring Engine').item.json.priority }}",
              "rightValue": "HIGH"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "23efc02a-c2e1-4727-9cf4-3e89aa7331f8",
      "name": "Send Forex Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        2592,
        -80
      ],
      "parameters": {
        "text": "={{ \n\" *Forex Risk Alert*\\n\\n\" +\n\"*Title:* \" + $('Risk Scoring Engine').item.json.title + \"\\n\" +\n\"*Impact:* \" + $('Risk Scoring Engine').item.json.impact + \"\\n\" +\n\"*Risk Level:* \" + $('Risk Scoring Engine').item.json.risk_level + \"\\n\" +\n\"*Confidence:* \" + $('Risk Scoring Engine').item.json.confidence_score + \"%\\n\\n\" +\n\"*Summary:*\\n\" + $('Risk Scoring Engine').item.json.summary + \"\\n\\n\" +\n\"*Action:*\\n\" + $('Risk Scoring Engine').item.json.recommended_action\n}}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0ARFB4R6RK",
          "cachedResultName": "forex-alert"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "279f3cce-04bd-462e-b0e4-e389ebabb406",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -704,
        -848
      ],
      "parameters": {
        "width": 560,
        "height": 576,
        "content": "## AI Forex Risk Intelligence Workflow\nThis workflow automates the conversion of real-time forex and macroeconomic news into actionable risk alerts using AI.\n\n### How it works:\nThe system continuously fetches news from RSS feeds and normalizes the data. It then filters for forex-relevant events using keyword and logic-based checks. Relevant news is analyzed by an AI model to generate structured insights such as summary, currency impact, risk level, and recommended actions.\nA deduplication mechanism uses a normalized title (dedupe_key) to ensure the same news is not processed multiple times. New events are stored in a data table, and only high-priority signals trigger Slack alerts, reducing noise and focusing on important market movements.\n\n### Setup steps:\n\nConfigure RSS feed source for forex/economic news\nAdd OpenAI API key for AI analysis\nSet up Slack webhook for alerts\nEnable Data Table for storing processed news\n\nThis workflow transforms unstructured news into real-time decision intelligence for forex monitoring."
      },
      "typeVersion": 1
    },
    {
      "id": "9742d7a0-7ca8-4326-bfb6-36c60a50fa39",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 336,
        "content": "## Data Ingestion\nFetches real-time forex news and normalizes raw RSS data for processing."
      },
      "typeVersion": 1
    },
    {
      "id": "d472937b-9ea1-4539-bdce-b4fb8a5da574",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 1200,
        "height": 336,
        "content": "## AI Analysis\nFilters relevant news and uses AI to generate structured forex risk insights."
      },
      "typeVersion": 1
    },
    {
      "id": "4e55c9ef-2db5-41fe-acca-300c3e9fea7d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1616,
        -352
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 336,
        "content": "## Decision Engine\nScores risk, generates deduplication key, and ensures only unique events are processed."
      },
      "typeVersion": 1
    },
    {
      "id": "059b246a-a157-4286-9851-950605b5b4cb",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2320,
        -384
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 496,
        "content": "## Alerts & Storage\nStores processed news and sends alerts only for high-priority events."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "3ae746c6-d5fa-4025-88bc-ce80edadf150",
  "connections": {
    "Send Forex Alert": {
      "main": [
        []
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "Risk Scoring Engine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Duplicate Decision": {
      "main": [
        [],
        [
          {
            "node": "Store Processed News",
            "type": "main",
            "index": 0
          },
          {
            "node": "High Priority Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Relevance Analyzer": {
      "main": [
        [
          {
            "node": "Forex Relevance Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize News Data": {
      "main": [
        [
          {
            "node": "Relevance Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Risk Scoring Engine": {
      "main": [
        [
          {
            "node": "Check Duplicate News",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Duplicate News": {
      "main": [
        [
          {
            "node": "Duplicate Decision",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "High Priority Filter": {
      "main": [
        [
          {
            "node": "Send Forex Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Processed News": {
      "main": [
        []
      ]
    },
    "Forex Relevance Check": {
      "main": [
        [
          {
            "node": "AI Forex Risk Analyzer",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge News + AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Forex Risk Analyzer": {
      "main": [
        [
          {
            "node": "Merge News + AI Output",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Forex News RSS Trigger": {
      "main": [
        [
          {
            "node": "Normalize News Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge News + AI Output": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}