{
  "id": "GtOVwfzfA4dk0exU",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Real-Time Oil Price Crisis Detection and Smart Alert System via Qwq-32b",
  "tags": [],
  "nodes": [
    {
      "id": "e63bbdb7-d063-4f97-8eb8-1050bdb82157",
      "name": "Schedule Every 5 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2176,
        480
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "0605042b-b18d-491d-9418-89af3a229c1d",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -1920,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "oilPriceApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Oil Price API endpoint URL__>"
            },
            {
              "id": "id-2",
              "name": "opecReportsUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__OPEC Reports API endpoint__>"
            },
            {
              "id": "id-3",
              "name": "shippingDataUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Shipping Data API endpoint__>"
            },
            {
              "id": "id-4",
              "name": "newsApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__News API endpoint__>"
            },
            {
              "id": "id-5",
              "name": "dashboardApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Dashboard API endpoint__>"
            },
            {
              "id": "id-6",
              "name": "alertEmailRecipients",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Email addresses for alerts (comma-separated)__>"
            },
            {
              "id": "id-7",
              "name": "slackChannel",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Slack channel ID for alerts__>"
            },
            {
              "id": "id-8",
              "name": "infoThreshold",
              "type": "number",
              "value": 30
            },
            {
              "id": "id-9",
              "name": "warningThreshold",
              "type": "number",
              "value": 60
            },
            {
              "id": "id-10",
              "name": "criticalThreshold",
              "type": "number",
              "value": 85
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "3d1f09cb-e959-493e-9ed1-a792a543783e",
      "name": "Fetch Oil Price Data (API)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        304
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.oilPriceApiUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "1b0559d6-7853-436c-91ff-5401ca6fbdbf",
      "name": "Fetch OPEC Reports",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        464
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.opecReportsUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "db390c16-d4b1-4607-9188-350e700f4595",
      "name": "Fetch Shipping Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        592
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.shippingDataUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "5765af2b-7800-4655-9110-74a4c5018403",
      "name": "Fetch News Feed",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1696,
        736
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.newsApiUrl }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "c629c377-1587-4df6-94b5-c64bf974b256",
      "name": "Merge All Data Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1408,
        432
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "d8ebe013-070d-4ea8-9410-5a05aff03dbc",
      "name": "Data Cleaning & Preprocessing",
      "type": "n8n-nodes-base.code",
      "position": [
        -1200,
        432
      ],
      "parameters": {
        "jsCode": "// Data Cleaning & Preprocessing for Oil Price Crisis Detection\n// Handles data from multiple sources: API, OPEC, Shipping, News\n\nconst cleanedData = [];\n\n// Process all input items\nfor (const item of $input.all()) {\n  try {\n    const source = item.json.source || 'unknown';\n    const timestamp = item.json.timestamp || new Date().toISOString();\n    \n    // Standardize timestamp to ISO format\n    let standardizedTimestamp;\n    try {\n      standardizedTimestamp = new Date(timestamp).toISOString();\n    } catch (e) {\n      standardizedTimestamp = new Date().toISOString();\n    }\n    \n    // Initialize cleaned data object\n    const cleanedItem = {\n      timestamp: standardizedTimestamp,\n      source: source,\n      rawData: item.json\n    };\n    \n    // Extract oil price data\n    if (item.json.price !== undefined && item.json.price !== null) {\n      cleanedItem.oilPrice = parseFloat(item.json.price) || 0;\n    } else if (item.json.brent_price !== undefined) {\n      cleanedItem.oilPrice = parseFloat(item.json.brent_price) || 0;\n    } else if (item.json.wti_price !== undefined) {\n      cleanedItem.oilPrice = parseFloat(item.json.wti_price) || 0;\n    }\n    \n    // Extract volume data\n    if (item.json.volume !== undefined && item.json.volume !== null) {\n      cleanedItem.volume = parseFloat(item.json.volume) || 0;\n    } else if (item.json.trading_volume !== undefined) {\n      cleanedItem.volume = parseFloat(item.json.trading_volume) || 0;\n    }\n    \n    // Extract shipping/supply data\n    if (item.json.shipping_volume !== undefined) {\n      cleanedItem.shippingVolume = parseFloat(item.json.shipping_volume) || 0;\n    }\n    if (item.json.supply_disruption !== undefined) {\n      cleanedItem.supplyDisruption = item.json.supply_disruption;\n    }\n    \n    // Extract and analyze news sentiment\n    if (item.json.title || item.json.headline || item.json.text) {\n      const newsText = (item.json.title || item.json.headline || item.json.text || '').toLowerCase();\n      \n      // Simple sentiment analysis based on keywords\n      const negativeKeywords = ['crisis', 'war', 'conflict', 'shortage', 'disruption', 'sanction', 'embargo', 'attack', 'threat'];\n      const positiveKeywords = ['stable', 'agreement', 'peace', 'increase', 'surplus', 'cooperation'];\n      \n      let sentimentScore = 0;\n      negativeKeywords.forEach(keyword => {\n        if (newsText.includes(keyword)) sentimentScore -= 1;\n      });\n      positiveKeywords.forEach(keyword => {\n        if (newsText.includes(keyword)) sentimentScore += 1;\n      });\n      \n      cleanedItem.newsSentiment = sentimentScore;\n      cleanedItem.newsText = item.json.title || item.json.headline || item.json.text;\n    }\n    \n    // Extract geopolitical indicators\n    if (item.json.region !== undefined) {\n      cleanedItem.region = item.json.region;\n    }\n    if (item.json.country !== undefined) {\n      cleanedItem.country = item.json.country;\n    }\n    \n    // Handle missing values - set defaults\n    if (cleanedItem.oilPrice === undefined) {\n      cleanedItem.oilPrice = null;\n    }\n    if (cleanedItem.volume === undefined) {\n      cleanedItem.volume = null;\n    }\n    if (cleanedItem.newsSentiment === undefined) {\n      cleanedItem.newsSentiment = 0;\n    }\n    \n    // Add data quality flag\n    cleanedItem.dataQuality = {\n      hasPriceData: cleanedItem.oilPrice !== null,\n      hasVolumeData: cleanedItem.volume !== null,\n      hasNewsData: cleanedItem.newsText !== undefined,\n      completeness: 0\n    };\n    \n    // Calculate completeness score\n    let completenessCount = 0;\n    if (cleanedItem.oilPrice !== null) completenessCount++;\n    if (cleanedItem.volume !== null) completenessCount++;\n    if (cleanedItem.newsText !== undefined) completenessCount++;\n    cleanedItem.dataQuality.completeness = completenessCount / 3;\n    \n    cleanedData.push(cleanedItem);\n    \n  } catch (error) {\n    console.log('Error processing item:', error.message);\n    // Continue processing other items\n  }\n}\n\n// Return structured data array\nreturn cleanedData.map(item => ({ json: item }));"
      },
      "typeVersion": 2
    },
    {
      "id": "d381a9a2-f656-493c-893f-75a761935888",
      "name": "Statistical Anomaly Detection",
      "type": "n8n-nodes-base.code",
      "position": [
        -896,
        272
      ],
      "parameters": {
        "jsCode": "// Statistical Anomaly Detection for Oil Price Crisis Detection\n// Implements Z-score, moving average, volatility, and spike detection\n\nconst items = $input.all();\n\n// Helper function to calculate mean\nfunction calculateMean(values) {\n  return values.reduce((sum, val) => sum + val, 0) / values.length;\n}\n\n// Helper function to calculate standard deviation\nfunction calculateStdDev(values, mean) {\n  const squaredDiffs = values.map(val => Math.pow(val - mean, 2));\n  const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;\n  return Math.sqrt(variance);\n}\n\n// Helper function to calculate Z-score\nfunction calculateZScore(value, mean, stdDev) {\n  if (stdDev === 0) return 0;\n  return (value - mean) / stdDev;\n}\n\n// Helper function to calculate moving average\nfunction calculateMovingAverage(values, window = 5) {\n  if (values.length < window) return calculateMean(values);\n  const recentValues = values.slice(-window);\n  return calculateMean(recentValues);\n}\n\n// Extract price data from items\nconst priceData = items.map(item => ({\n  price: parseFloat(item.json.price || item.json.oilPrice || 0),\n  volume: parseFloat(item.json.volume || item.json.tradingVolume || 0),\n  timestamp: item.json.timestamp || new Date().toISOString()\n}));\n\n// Calculate statistics\nconst prices = priceData.map(d => d.price);\nconst volumes = priceData.map(d => d.volume);\n\nconst priceMean = calculateMean(prices);\nconst priceStdDev = calculateStdDev(prices, priceMean);\nconst volumeMean = calculateMean(volumes);\nconst volumeStdDev = calculateStdDev(volumes, volumeMean);\n\n// Calculate moving average (5-period)\nconst movingAverage = calculateMovingAverage(prices, 5);\n\n// Get current (latest) values\nconst currentPrice = prices[prices.length - 1];\nconst previousPrice = prices.length > 1 ? prices[prices.length - 2] : currentPrice;\nconst currentVolume = volumes[volumes.length - 1];\n\n// Calculate price change percentage\nconst priceChangePercent = previousPrice !== 0 \n  ? ((currentPrice - previousPrice) / previousPrice) * 100 \n  : 0;\n\n// Calculate Z-scores\nconst priceZScore = calculateZScore(currentPrice, priceMean, priceStdDev);\nconst volumeZScore = calculateZScore(currentVolume, volumeMean, volumeStdDev);\n\n// Calculate price volatility (coefficient of variation)\nconst priceVolatility = priceMean !== 0 ? (priceStdDev / priceMean) * 100 : 0;\n\n// Detect anomalies\nconst suddenPriceChange = Math.abs(priceChangePercent) > 5;\nconst volumeSpike = Math.abs(volumeZScore) > 2;\nconst priceAnomaly = Math.abs(priceZScore) > 2;\nconst highVolatility = priceVolatility > 10;\n\n// Calculate composite anomaly score (0-100)\nlet anomalyScore = 0;\n\n// Price Z-score contribution (0-30 points)\nanomalyScore += Math.min(Math.abs(priceZScore) * 10, 30);\n\n// Sudden price change contribution (0-25 points)\nif (suddenPriceChange) {\n  anomalyScore += Math.min(Math.abs(priceChangePercent) * 2, 25);\n}\n\n// Volume spike contribution (0-25 points)\nif (volumeSpike) {\n  anomalyScore += Math.min(Math.abs(volumeZScore) * 8, 25);\n}\n\n// Volatility contribution (0-20 points)\nanomalyScore += Math.min(priceVolatility * 2, 20);\n\n// Cap at 100\nanomalyScore = Math.min(anomalyScore, 100);\n\n// Determine overall anomaly flag\nconst isAnomaly = anomalyScore > 50 || suddenPriceChange || (priceAnomaly && volumeSpike);\n\n// Return enriched data with anomaly detection results\nreturn [{\n  json: {\n    // Original data\n    ...items[items.length - 1].json,\n    \n    // Statistical metrics\n    statistics: {\n      priceMean: priceMean.toFixed(2),\n      priceStdDev: priceStdDev.toFixed(2),\n      movingAverage: movingAverage.toFixed(2),\n      priceVolatility: priceVolatility.toFixed(2),\n      volumeMean: volumeMean.toFixed(2),\n      volumeStdDev: volumeStdDev.toFixed(2)\n    },\n    \n    // Current values\n    currentMetrics: {\n      price: currentPrice,\n      previousPrice: previousPrice,\n      priceChangePercent: priceChangePercent.toFixed(2),\n      volume: currentVolume,\n      priceZScore: priceZScore.toFixed(2),\n      volumeZScore: volumeZScore.toFixed(2)\n    },\n    \n    // Anomaly detection results\n    anomalyDetection: {\n      anomalyScore: Math.round(anomalyScore),\n      isAnomaly: isAnomaly,\n      flags: {\n        suddenPriceChange: suddenPriceChange,\n        volumeSpike: volumeSpike,\n        priceAnomaly: priceAnomaly,\n        highVolatility: highVolatility\n      },\n      details: {\n        priceChangeDetected: suddenPriceChange ? `${priceChangePercent.toFixed(2)}% change detected` : 'Normal',\n        volumeStatus: volumeSpike ? 'Volume spike detected' : 'Normal',\n        priceStatus: priceAnomaly ? 'Price anomaly detected' : 'Normal',\n        volatilityStatus: highVolatility ? 'High volatility' : 'Normal'\n      }\n    },\n    \n    // Timestamp\n    analysisTimestamp: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "44984f6f-6894-485f-8dfa-95f6b8710028",
      "name": "AI Trend & Geopolitical Analyzer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -928,
        656
      ],
      "parameters": {
        "text": "=Analyze the following oil market data for geopolitical risks, supply disruptions, and crisis indicators. Consider: 1) News sentiment and geopolitical events, 2) OPEC production changes, 3) Shipping disruptions, 4) Historical patterns. Provide a risk assessment score (0-100) and key risk factors. Data: {{ JSON.stringify($json) }}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "5a9fbd66-5014-4b87-a82e-33eb701282a6",
      "name": "Calculate Crisis Risk Score",
      "type": "n8n-nodes-base.code",
      "position": [
        -592,
        400
      ],
      "parameters": {
        "jsCode": "// Calculate Comprehensive Crisis Risk Score\n// Combines multiple data sources with weighted scoring\n\nconst items = $input.all();\nconst results = [];\n\nfor (const item of items) {\n  const data = item.json;\n  \n  // Extract scores from previous nodes\n  const statisticalAnomalyScore = data.anomalyScore || 0;\n  const aiGeopoliticalScore = data.geopoliticalRiskScore || 0;\n  const priceVolatility = data.priceVolatility || 0;\n  const volumeChange = data.volumeChange || 0;\n  \n  // Normalize all scores to 0-100 scale if needed\n  const normalizedStatistical = Math.min(Math.max(statisticalAnomalyScore, 0), 100);\n  const normalizedGeopolitical = Math.min(Math.max(aiGeopoliticalScore, 0), 100);\n  const normalizedVolatility = Math.min(Math.max(priceVolatility, 0), 100);\n  const normalizedVolume = Math.min(Math.max(volumeChange, 0), 100);\n  \n  // Calculate weighted crisis risk score\n  const crisisRiskScore = (\n    (normalizedStatistical * 0.40) +\n    (normalizedGeopolitical * 0.40) +\n    (normalizedVolatility * 0.10) +\n    (normalizedVolume * 0.10)\n  );\n  \n  // Round to 2 decimal places\n  const finalScore = Math.round(crisisRiskScore * 100) / 100;\n  \n  // Determine alert level based on score\n  let alertLevel;\n  if (finalScore >= 70) {\n    alertLevel = 'critical';\n  } else if (finalScore >= 40) {\n    alertLevel = 'warning';\n  } else {\n    alertLevel = 'info';\n  }\n  \n  // Create detailed breakdown\n  const breakdown = {\n    statisticalAnomaly: {\n      score: normalizedStatistical,\n      weight: '40%',\n      contribution: Math.round(normalizedStatistical * 0.40 * 100) / 100\n    },\n    geopoliticalRisk: {\n      score: normalizedGeopolitical,\n      weight: '40%',\n      contribution: Math.round(normalizedGeopolitical * 0.40 * 100) / 100\n    },\n    priceVolatility: {\n      score: normalizedVolatility,\n      weight: '10%',\n      contribution: Math.round(normalizedVolatility * 0.10 * 100) / 100\n    },\n    volumeChanges: {\n      score: normalizedVolume,\n      weight: '10%',\n      contribution: Math.round(normalizedVolume * 0.10 * 100) / 100\n    }\n  };\n  \n  // Return comprehensive result\n  results.push({\n    json: {\n      ...data,\n      crisisRiskScore: finalScore,\n      alertLevel: alertLevel,\n      scoreBreakdown: breakdown,\n      timestamp: new Date().toISOString(),\n      scoringMethod: 'weighted_composite'\n    }\n  });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "f2612ed5-0943-4b8e-b480-47a9d85ebb66",
      "name": "Route by Alert Level",
      "type": "n8n-nodes-base.switch",
      "position": [
        -352,
        496
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Info",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "id-1",
                    "operator": {
                      "type": "number",
                      "operation": "lt"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.warningThreshold }}"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Warning",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "id-2",
                    "operator": {
                      "type": "number",
                      "operation": "gte"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.warningThreshold }}"
                  },
                  {
                    "id": "id-3",
                    "operator": {
                      "type": "number",
                      "operation": "lt"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.criticalThreshold }}"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Critical",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "id-4",
                    "operator": {
                      "type": "number",
                      "operation": "gte"
                    },
                    "leftValue": "={{ $json.riskScore }}",
                    "rightValue": "={{ $('Workflow Configuration').first().json.criticalThreshold }}"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.3
    },
    {
      "id": "e8a59011-bdd3-4ba8-bfd4-3e1d586112e2",
      "name": "Format Info Alert",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        336
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "alertLevel",
              "type": "string",
              "value": "INFO"
            },
            {
              "id": "id-2",
              "name": "alertTitle",
              "type": "string",
              "value": "Oil Market Update"
            },
            {
              "id": "id-3",
              "name": "alertMessage",
              "type": "string",
              "value": "=Risk Score: {{ $json.riskScore }} - {{ $json.summary }}"
            },
            {
              "id": "id-4",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "id-5",
              "name": "color",
              "type": "string",
              "value": "#17a2b8"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8833b06e-b711-49da-a213-d6d59547d785",
      "name": "Format Warning Alert",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "alertLevel",
              "type": "string",
              "value": "WARNING"
            },
            {
              "id": "id-2",
              "name": "alertTitle",
              "type": "string",
              "value": "\u26a0\ufe0f Oil Market Warning Alert"
            },
            {
              "id": "id-3",
              "name": "alertMessage",
              "type": "string",
              "value": "=Risk Score: {{ $json.riskScore }} - {{ $json.summary }}. Factors: {{ $json.riskFactors }}"
            },
            {
              "id": "id-4",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "id-5",
              "name": "color",
              "type": "string",
              "value": "#ffc107"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b912a259-b218-4ea7-9fc9-2cfb800ff5f6",
      "name": "Format Critical Alert",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        640
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "alertLevel",
              "type": "string",
              "value": "CRITICAL"
            },
            {
              "id": "id-2",
              "name": "alertTitle",
              "type": "string",
              "value": "\ud83d\udea8 CRITICAL Oil Market Crisis Alert"
            },
            {
              "id": "id-3",
              "name": "alertMessage",
              "type": "string",
              "value": "=URGENT - Risk Score: {{ $json.riskScore }} - {{ $json.summary }}. Immediate action required. Factors: {{ $json.riskFactors }}"
            },
            {
              "id": "id-4",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "id-5",
              "name": "color",
              "type": "string",
              "value": "#dc3545"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "59216b80-04b3-401a-806c-1a8b994c9734",
      "name": "Send Email Alert",
      "type": "n8n-nodes-base.gmail",
      "position": [
        176,
        400
      ],
      "parameters": {
        "sendTo": "={{ $('Workflow Configuration').first().json.alertEmailRecipients }}",
        "message": "=<h2>{{ $json.alertTitle }}</h2><p>{{ $json.alertMessage }}</p><p><strong>Timestamp:</strong> {{ $json.timestamp }}</p><p><strong>Details:</strong> {{ JSON.stringify($json, null, 2) }}</p>",
        "options": {},
        "subject": "={{ $json.alertTitle }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "6bba8b1d-6bdc-4a53-9910-49e795c5a23b",
      "name": "Send Slack Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        192,
        576
      ],
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration').first().json.slackChannel }}"
        },
        "attachments": [
          {
            "text": "={{ $json.alertTitle }}\n{{ $json.alertMessage }}\nTimestamp: {{ $json.timestamp }}",
            "color": "={{ $json.color }}",
            "fallback": "Alert Notification"
          }
        ],
        "messageType": "attachment",
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "360d44b6-be4b-4d9e-99a7-29b0cad84364",
      "name": "Store Alert History",
      "type": "n8n-nodes-base.postgres",
      "position": [
        240,
        64
      ],
      "parameters": {
        "table": {
          "__rl": true,
          "mode": "name",
          "value": "alert_history"
        },
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "columns": {
          "value": {},
          "mappingMode": "autoMapInputData"
        },
        "options": {}
      },
      "typeVersion": 2.6
    },
    {
      "id": "1d8ffdde-0611-478b-bac4-8d861234528a",
      "name": "Send to Dashboard API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        64,
        64
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.dashboardApiUrl }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "0fad6e92-4d61-4b28-9951-714e23dffd6b",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2224,
        -160
      ],
      "parameters": {
        "width": 336,
        "height": 336,
        "content": "## How It Works\nScheduled runs collect data from oil markets, global shipping movements, news sources, and official reports. The system performs statistical checks to detect anomalies and volatility shifts. An AI-driven geopolitical model evaluates emerging risks and assigns a crisis score. Based on severity thresholds, results are routed to the appropriate alert channels for rapid response."
      },
      "typeVersion": 1
    },
    {
      "id": "9019b097-ef4c-4c90-a96c-1d02bd941659",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1872,
        -160
      ],
      "parameters": {
        "width": 464,
        "height": 272,
        "content": "## Setup Steps\n1. **Data Sources:** Connect the oil price API, OPEC report feeds, shipping databases, and news sources.\n2. **AI Model:** Configure the OpenRouter ChatGPT model for geopolitical and risk analysis.\n3. **Alerts:** Define severity rules and route alerts to Email, Slack, or Dashboard APIs.\n4. **Storage:** Configure a database for historical records, audit logging, and trend tracking."
      },
      "typeVersion": 1
    },
    {
      "id": "1125bbc0-11b7-464b-b475-252b593719ec",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1376,
        -144
      ],
      "parameters": {
        "color": 6,
        "width": 672,
        "height": 208,
        "content": "\n## Prerequisites\nOil market API credentials; news feed access; OPEC data source; OpenRouter API key; Slack/email/dashboard integrations\n\n## Use Cases\nSupply chain risk monitoring; energy market crisis detection; geopolitical threat assessment; trader decision support; operational risk management"
      },
      "typeVersion": 1
    },
    {
      "id": "d724f033-8c42-4d3e-b95b-1104e6a68985",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -928,
        800
      ],
      "parameters": {
        "model": "qwen/qwq-32b",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5af012b5-81f3-4624-825d-e32befc29e70",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        -144
      ],
      "parameters": {
        "color": 3,
        "width": 624,
        "height": 192,
        "content": "## Customization\nAdjust risk thresholds; add market data sources; modify alert routing rules \n\n## Benefits\nReduces crisis detection lag 90%; consolidates fragmented data; enables proactive response "
      },
      "typeVersion": 1
    },
    {
      "id": "968857a5-92e6-4b8b-9b23-d02acbf84685",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2224,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 496,
        "content": "## Schedule Data Collection\nTriggers workflow every 5 minutes to fetch oil markets, OPEC reports, shipping data, news feeds\nWhy: 5-minute cadence catches early warnings before markets react"
      },
      "typeVersion": 1
    },
    {
      "id": "096bb892-a30a-42f1-ab45-5c131e5256a9",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1488,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 416,
        "content": "## Aggregate Shipping Data\nCollects vessel tracking, route changes, port delays, freight costs\nWhy: Shipping delays signal supply disruptions or geopolitical tensions before announcements"
      },
      "typeVersion": 1
    },
    {
      "id": "9c572d55-c40a-411e-ae34-09c6828baf87",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1968,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 688,
        "content": "## Fetch Oil Price Data\nRetrieves spot prices, futures, contract rates from multiple exchanges\nWhy: Oil prices move first in crises; multiple sources catch geographic and contract divergence"
      },
      "typeVersion": 1
    },
    {
      "id": "cc635d3b-3751-443c-9e89-4690584e4a77",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1216,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 464,
        "content": "##  Clean Multi-Source Data\nStandardizes oil prices, shipping metrics, dates, timestamps into unified schema\nWhy: Fragmented data prevents analysis"
      },
      "typeVersion": 1
    },
    {
      "id": "55123cd3-ccb6-49ad-abf1-0a92c6526eba",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 336,
        "content": "## Statistical Anomaly Detection\nIdentifies unusual shifts: price volatility spikes, shipping jumps, OPEC deviations, news volume surges\nWhy: Quantitative anomalies flag problems objectively \n"
      },
      "typeVersion": 1
    },
    {
      "id": "b19afa6f-008c-4e56-8fb8-75901269fec3",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 512,
        "content": "## AI Crisis Risk Model\nUses Claude to analyze geopolitical tensions, sanctions risk\nWhy: AI connects disparate signals into coherent risk narrative; combines quantitative anomalies with context"
      },
      "typeVersion": 1
    },
    {
      "id": "90af94d4-ca13-4d6a-84fe-fadf4b03c6f4",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        144
      ],
      "parameters": {
        "color": 7,
        "height": 448,
        "content": "\n## Score Emerging Threats\nAssigns crisis risk rating: threat probability, impact, time horizon\nWhy: Scoring prioritizes analyst time on highest-threat scenarios"
      },
      "typeVersion": 1
    },
    {
      "id": "db334106-5b75-452c-b5de-1d084ef08267",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -384,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 688,
        "content": "## Route Critical Alerts to Traders\nSends high-risk signals (8-10 severity) immediately via Slack and dashboard\nWhy: Traders need sub-minute warnings to adjust positions; speed determines profit/loss"
      },
      "typeVersion": 1
    },
    {
      "id": "0643b2b5-13f4-4437-badc-61878b0aec03",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 320,
        "content": "## Alerts to Dashboard & Storage\nPosts lower-severity signals (1-4) to dashboard for shift review\nWhy: Lower-risk items inform strategy without urgent action; creates searchable record"
      },
      "typeVersion": 1
    },
    {
      "id": "52f35260-0719-48dc-aa3d-877a90f1eb35",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 528,
        "content": "\n## Alert to Email & Slack\nLogs alerts with timestamps, reasoning, sources, routing decisions\nWhy: Regulators require monitoring evidence; audit trail proves due diligence"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b07a4dc6-2c1b-4584-ab5f-414cc35d0484",
  "connections": {
    "Format Info Alert": {
      "main": [
        [
          {
            "node": "Send to Dashboard API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch OPEC Reports": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Format Warning Alert": {
      "main": [
        [
          {
            "node": "Send Email Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send to Dashboard API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Alert Level": {
      "main": [
        [
          {
            "node": "Format Info Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Warning Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Critical Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Critical Alert": {
      "main": [
        [
          {
            "node": "Send Email Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send to Dashboard API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Trend & Geopolitical Analyzer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Send to Dashboard API": {
      "main": [
        [
          {
            "node": "Store Alert History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Data Sources": {
      "main": [
        [
          {
            "node": "Data Cleaning & Preprocessing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "Fetch Oil Price Data (API)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch OPEC Reports",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Shipping Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch News Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Every 5 Minutes": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Oil Price Data (API)": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Crisis Risk Score": {
      "main": [
        [
          {
            "node": "Route by Alert Level",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Cleaning & Preprocessing": {
      "main": [
        [
          {
            "node": "Statistical Anomaly Detection",
            "type": "main",
            "index": 0
          },
          {
            "node": "AI Trend & Geopolitical Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Statistical Anomaly Detection": {
      "main": [
        [
          {
            "node": "Calculate Crisis Risk Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Trend & Geopolitical Analyzer": {
      "main": [
        [
          {
            "node": "Calculate Crisis Risk Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}