{
  "name": "FF High Impact \u2192 Analysis \u2192 Telegram (MVP)",
  "nodes": [
    {
      "id": "cron-live",
      "name": "Cron Live (every 15m)",
      "type": "n8n-nodes-base.cron",
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyX",
              "unit": "minutes",
              "value": 15
            }
          ]
        }
      },
      "typeVersion": 2,
      "position": [
        200,
        200
      ]
    },
    {
      "id": "http-today",
      "name": "HTTP \u2192 FF Calendar (Today)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        420,
        200
      ],
      "parameters": {
        "url": "https://www.forexfactory.com/calendar?day=today",
        "options": {
          "redirect": {
            "followRedirects": true
          },
          "headers": {
            "User-Agent": "n8n-bot"
          }
        },
        "responseFormat": "string"
      }
    },
    {
      "id": "html-extract",
      "name": "HTML Extract (High Impact)",
      "type": "n8n-nodes-base.htmlExtract",
      "typeVersion": 1,
      "position": [
        640,
        200
      ],
      "parameters": {
        "extractionValues": {
          "values": [
            {
              "key": "row",
              "cssSelector": "tr.calendar__row:has(span.icon--ff-impact-red)",
              "returnArray": true
            }
          ]
        },
        "options": {
          "rawHtml": true
        }
      }
    },
    {
      "id": "html-map",
      "name": "Map Fields",
      "type": "n8n-nodes-base.htmlExtract",
      "typeVersion": 1,
      "position": [
        860,
        200
      ],
      "parameters": {
        "extractionValues": {
          "values": [
            {
              "key": "time",
              "cssSelector": "td.calendar__time",
              "returnArray": false
            },
            {
              "key": "currency",
              "cssSelector": "td.calendar__currency",
              "returnArray": false
            },
            {
              "key": "title",
              "cssSelector": "td.calendar__event a.calendar__event-title",
              "returnArray": false
            },
            {
              "key": "link",
              "cssSelector": "td.calendar__event a.calendar__event-title",
              "returnArray": false,
              "attribute": "href"
            },
            {
              "key": "forecast",
              "cssSelector": "td.calendar__forecast",
              "returnArray": false
            },
            {
              "key": "actual",
              "cssSelector": "td.calendar__actual",
              "returnArray": false
            },
            {
              "key": "previous",
              "cssSelector": "td.calendar__previous",
              "returnArray": false
            }
          ]
        }
      }
    },
    {
      "id": "fn-clean",
      "name": "Function \u2192 Clean & TZ",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        1080,
        200
      ],
      "parameters": {
        "functionCode": "const tz='Asia/Baku';\nconst toNum=(s)=>{if(!s) return null; const x=String(s).replace(/[^0-9+\\-\\.]/g,''); return x?Number(x):null};\nreturn items.map(it=>{\n  const d=new Date(); // \u0627\u0645\u0631\u0648\u0632\n  // \u0632\u0645\u0627\u0646 \u0631\u0648\u06cc \u0633\u0627\u06cc\u062a \u0645\u0645\u06a9\u0646\u0647 'All Day' \u06cc\u0627 '\u2014' \u0628\u0627\u0634\u062f\n  const time=String(it.json.time||'').trim();\n  let iso = null;\n  try{\n    if(/\\d/.test(time)){\n      // \u0645\u062b\u0627\u0644: 8:30am \u2192 \u0628\u0647 \u062a\u0627\u0631\u06cc\u062e \u0627\u0645\u0631\u0648\u0632 \u062f\u0631 Asia/Baku \u062a\u0628\u062f\u06cc\u0644 \u06a9\u0646\n      const m=time.match(/(\\d{1,2}):(\\d{2})\\s*(am|pm)?/i);\n      if(m){\n        let h=Number(m[1]); const min=Number(m[2]); const ampm=m[3];\n        if(ampm && /pm/i.test(ampm) && h<12) h+=12; if(ampm && /am/i.test(ampm) && h===12) h=0;\n        const dt=new Date(Date.UTC(d.getUTCFullYear(),d.getUTCMonth(),d.getUTCDate(),h-4,min)); // \u062a\u0642\u0631\u06cc\u0628\u06cc\u061b \u0628\u0639\u062f\u0627\u064b \u0628\u0627 TZ \u0648\u0627\u0642\u0639\u06cc \u06a9\u0627\u0631\u0628\u0631 \u062a\u0646\u0638\u06cc\u0645 \u06a9\u0646\n        iso=dt.toISOString();\n      }\n    }\n  }catch(e){}\n  const forecast = toNum(it.json.forecast);\n  const actual = toNum(it.json.actual);\n  const previous = toNum(it.json.previous);\n  const surprise = (actual!=null && forecast!=null) ? (actual-forecast) : null;\n  return { json: { ...it.json, time_local: iso, forecast, actual, previous, surprise } };\n});"
      }
    },
    {
      "id": "if-has-actual",
      "name": "IF Actual Published?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1300,
        200
      ],
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json[\"actual\"]}}",
              "operation": "largerOrEqual",
              "value2": 0
            }
          ]
        }
      }
    },
    {
      "id": "llm-analyze",
      "name": "GPT-5 Thinking \u2192 Macro Impact",
      "type": "n8n-nodes-base.openAiChat",
      "typeVersion": 4,
      "position": [
        1520,
        160
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "model": "gpt-5-thinking",
        "systemMessage": "\u062a\u0648 \u06cc\u06a9 \u062a\u062d\u0644\u06cc\u0644\u200c\u06af\u0631 \u06a9\u0644\u0627\u0646 \u0628\u0631\u0627\u06cc USD/DXY, XAU/USD, XAG/USD, NAS100, DJI \u0647\u0633\u062a\u06cc. \u062f\u0627\u062f\u0647 \u0648\u0631\u0648\u062f\u06cc: \u0646\u0627\u0645 \u0631\u0648\u06cc\u062f\u0627\u062f\u060c \u0627\u0631\u0632\u060c Forecast, Actual, Previous, Surprise \u0646\u0631\u0645\u0627\u0644 \u0634\u062f\u0647 \u0648 \u062a\u0648\u0636\u06cc\u062d \u0646\u0648\u0639 \u0631\u0648\u06cc\u062f\u0627\u062f (\u062a\u0648\u0631\u0645\u06cc/\u0627\u0634\u062a\u063a\u0627\u0644/\u0631\u0634\u062f/\u0646\u0631\u062e). \u0628\u0631 \u0627\u0633\u0627\u0633 \u0642\u0648\u0627\u0639\u062f \u0627\u0642\u062a\u0635\u0627\u062f \u06a9\u0644\u0627\u0646\u060c \u062c\u0647\u062a \u0627\u062b\u0631 1 \u062a\u0627 6 \u0633\u0627\u0639\u062a \u0622\u06cc\u0646\u062f\u0647 \u0631\u0627 \u0628\u0627 \u06cc\u06a9\u06cc \u0627\u0632 \u0628\u0631\u0686\u0633\u0628\u200c\u0647\u0627\u06cc Strong \u2191 / Mild \u2191 / Neutral / Mild \u2193 / Strong \u2193 \u0628\u0631\u0627\u06cc \u0647\u0631 \u062f\u0627\u0631\u0627\u06cc\u06cc \u0628\u062f\u0647 \u0648 \u06cc\u06a9 Confidence 0\u2013100% \u0647\u0645 \u0628\u062f\u0647. \u0628\u06af\u0648 \u06a9\u0647 \u0627\u06cc\u0646 \u062a\u062d\u0644\u06cc\u0644 \u062a\u0648\u0635\u06cc\u0647 \u0633\u0631\u0645\u0627\u06cc\u0647\u200c\u06af\u0630\u0627\u0631\u06cc \u0646\u06cc\u0633\u062a.",
        "messages": "Event: {{$json.title}} | CCY: {{$json.currency}} | Forecast: {{$json.forecast}} | Actual: {{$json.actual}} | Previous: {{$json.previous}} | Surprise: {{$json.surprise}}\n\u0646\u0648\u0639 \u0631\u0648\u06cc\u062f\u0627\u062f \u0631\u0627 \u0627\u0632 \u0639\u0646\u0648\u0627\u0646 \u062d\u062f\u0633 \u0628\u0632\u0646 (CPI/NFP/Rate/PPI/GDP/PMI...).",
        "temperature": 0.2
      }
    },
    {
      "id": "format-tele",
      "name": "Function \u2192 Format Telegram",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        1740,
        160
      ],
      "parameters": {
        "functionCode": "return items.map(it=>{\nconst j=it.json; const a=it.json;\nconst lines=[\n`\ud83d\udce3 High Impact`,\n`\ud83d\udd52 ${j.time || '\u2014'} (Asia/Baku)  |  ${j.currency}`,\n`\ud83d\udccc ${j.title}`,\n`Forecast: ${j.forecast ?? '\u2014'} | Actual: ${j.actual ?? '\u2014'} | Prev: ${j.previous ?? '\u2014'}`,\n`\ud83d\udd17 FF: ${j.link}`,\n`\\n\ud83e\udde0 GPT: ${j.data || j.answer || j.text || ''}`\n];\nreturn { json: { text: lines.join('\\n') } };\n});"
      }
    },
    {
      "id": "telegram-send",
      "name": "Telegram \u2192 Send Message",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        1960,
        160
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "sendMessage",
        "chatId": "REPLACE_CHAT_ID",
        "text": "={{$json[\"text\"]}}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      }
    },
    {
      "id": "telegram-trigger",
      "name": "Telegram Trigger (/today|/week|/stats)",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1,
      "position": [
        200,
        520
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "updates": [
          "message"
        ]
      }
    },
    {
      "id": "router-cmd",
      "name": "IF Command",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        420,
        520
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"message\"][\"text\"]}}",
              "operation": "contains",
              "value2": "/today"
            }
          ]
        }
      }
    },
    {
      "id": "http-week",
      "name": "HTTP \u2192 FF Calendar (This Week)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        420,
        680
      ],
      "parameters": {
        "url": "https://www.forexfactory.com/calendar?week=this",
        "responseFormat": "string"
      }
    }
  ],
  "connections": {
    "cron-live": {
      "main": [
        [
          {
            "node": "http-today",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "http-today": {
      "main": [
        [
          {
            "node": "html-extract",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "html-extract": {
      "main": [
        [
          {
            "node": "html-map",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "html-map": {
      "main": [
        [
          {
            "node": "fn-clean",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fn-clean": {
      "main": [
        [
          {
            "node": "if-has-actual",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "if-has-actual": {
      "main": [
        [
          {
            "node": "llm-analyze",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "format-tele",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "llm-analyze": {
      "main": [
        [
          {
            "node": "format-tele",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "format-tele": {
      "main": [
        [
          {
            "node": "telegram-send",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "telegram-trigger": {
      "main": [
        [
          {
            "node": "router-cmd",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "router-cmd": {
      "main": [
        [
          {
            "node": "http-today",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}