{
  "name": "AI News-to-Polymarket Market Matcher",
  "nodes": [
    {
      "parameters": {
        "content": "## AI News-to-Market Matcher\n\nMatches breaking news from RSS feeds to live Polymarket prediction markets, then uses AI to assess impact and send trading signals.\n\n**Setup:**\n1. Set your RSS feed URL in the RSS Feed Trigger node (default: Google News top stories)\n2. Configure an OpenAI credential for the AI analysis node\n3. Set up Telegram or Slack credentials for alerts\n4. Adjust the confidence threshold in the IF node (default: 70)\n\n**How it works:**\n- RSS trigger fires on new articles\n- Extracts keywords from the headline and searches Polymarket\n- Gets live pricing for the best matching market\n- AI analyzes whether the news affects the market and predicts direction\n- High-confidence signals are sent to Telegram"
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000001",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [240, 80]
    },
    {
      "parameters": {
        "url": "https://news.google.com/rss",
        "options": {}
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000002",
      "name": "RSS News Feed",
      "type": "n8n-nodes-base.rssFeedReadTrigger",
      "typeVersion": 1,
      "position": [240, 360]
    },
    {
      "parameters": {
        "jsCode": "// Extract search keywords from the RSS headline\nconst title = $input.first().json.title || '';\n// Remove common stop words and short words\nconst stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'will', 'be', 'to', 'of', 'in', 'for', 'on', 'at', 'by', 'and', 'or', 'but', 'not', 'it', 'its', 'has', 'have', 'had', 'that', 'this', 'with', 'from', 'as', 'says', 'said']);\nconst keywords = title\n  .replace(/[^a-zA-Z0-9\\s]/g, '')\n  .split(/\\s+/)\n  .filter(w => w.length > 2 && !stopWords.has(w.toLowerCase()))\n  .slice(0, 5)\n  .join(' ');\n\nreturn [{\n  json: {\n    title,\n    link: $input.first().json.link || '',\n    pubDate: $input.first().json.pubDate || '',\n    searchQuery: keywords\n  }\n}];"
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000003",
      "name": "Extract Keywords",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [490, 360]
    },
    {
      "parameters": {
        "resource": "market",
        "operation": "search",
        "query": "={{ $json.searchQuery }}",
        "filters": {
          "active": true,
          "limit": 5
        }
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000004",
      "name": "Search Polymarket",
      "type": "n8n-nodes-polymarket-tools.polymarket",
      "typeVersion": 1,
      "position": [740, 360]
    },
    {
      "parameters": {
        "jsCode": "// Get the top matching market and extract the first token ID for pricing\nconst markets = $input.all();\nif (markets.length === 0) {\n  return [{ json: { _noMatch: true } }];\n}\n\nconst top = markets[0].json;\n\n// Parse tokens -- Gamma returns these as JSON-encoded strings\nlet tokens = top.tokens || [];\nif (typeof tokens === 'string') {\n  try { tokens = JSON.parse(tokens); } catch(e) { tokens = []; }\n}\n\nlet clobTokenIds = top.clobTokenIds || [];\nif (typeof clobTokenIds === 'string') {\n  try { clobTokenIds = JSON.parse(clobTokenIds); } catch(e) { clobTokenIds = []; }\n}\n\nlet outcomes = top.outcomes || [];\nif (typeof outcomes === 'string') {\n  try { outcomes = JSON.parse(outcomes); } catch(e) { outcomes = []; }\n}\n\nlet outcomePrices = top.outcomePrices || [];\nif (typeof outcomePrices === 'string') {\n  try { outcomePrices = JSON.parse(outcomePrices); } catch(e) { outcomePrices = []; }\n}\n\nconst firstTokenId = (tokens[0] && tokens[0].tokenId) || clobTokenIds[0] || '';\n\nreturn [{\n  json: {\n    question: top.question || top.title || 'Unknown',\n    conditionId: top.conditionId || '',\n    slug: top.slug || '',\n    volume: top.volume || 0,\n    tokenId: firstTokenId,\n    outcomes,\n    outcomePrices,\n    _noMatch: false\n  }\n}];"
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000005",
      "name": "Pick Top Market",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [990, 360]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "match-check-001",
              "leftValue": "={{ $json._noMatch }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "notTrue"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000006",
      "name": "Market Found?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.1,
      "position": [1240, 360]
    },
    {
      "parameters": {
        "resource": "price",
        "operation": "get",
        "tokenId": "={{ $json.tokenId }}",
        "includeData": ["midpoint", "spread"]
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000007",
      "name": "Get Price",
      "type": "n8n-nodes-polymarket-tools.polymarket",
      "typeVersion": 1,
      "position": [1490, 300]
    },
    {
      "parameters": {
        "jsCode": "// Combine news + market data + prices into an AI analysis prompt\nconst price = $input.first().json;\n\n// Pull forward data from earlier nodes via $('nodeName')\nconst newsData = $('Extract Keywords').first().json;\nconst marketData = $('Pick Top Market').first().json;\n\nconst prompt = `You are a prediction market analyst. Analyze whether this news affects the given Polymarket market.\n\nNEWS HEADLINE: ${newsData.title}\nNEWS URL: ${newsData.link}\nPUBLISHED: ${newsData.pubDate}\n\nMATCHED MARKET: ${marketData.question}\nCURRENT PRICE: $${Number(price.price || price.midpoint || 0).toFixed(3)}\nMIDPOINT: $${Number(price.midpoint || 0).toFixed(3)}\nSPREAD: ${JSON.stringify(price.spread || 'N/A')}\nVOLUME: $${Number(marketData.volume || 0).toLocaleString()}\n\nRespond with JSON only:\n{\n  \"relevant\": true/false,\n  \"confidence\": 0-100,\n  \"direction\": \"YES_UP\" | \"YES_DOWN\" | \"NEUTRAL\",\n  \"reasoning\": \"1-2 sentence explanation\"\n}`;\n\nreturn [{\n  json: {\n    prompt,\n    newsTitle: newsData.title,\n    newsLink: newsData.link,\n    marketQuestion: marketData.question,\n    currentPrice: price.price || price.midpoint || 0,\n    slug: marketData.slug\n  }\n}];"
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000008",
      "name": "Build AI Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1740, 300]
    },
    {
      "parameters": {
        "model": "gpt-4o-mini",
        "messages": {
          "values": [
            {
              "content": "={{ $json.prompt }}"
            }
          ]
        },
        "options": {
          "temperature": 0.3,
          "maxTokens": 300
        }
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000009",
      "name": "AI Analysis",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [1990, 300],
      "credentials": {
        "openAiApi": {
          "id": "",
          "name": "OpenAI API"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse AI response and determine if confidence exceeds threshold\nconst aiRaw = $input.first().json;\nconst promptData = $('Build AI Prompt').first().json;\nlet text = aiRaw.text || aiRaw.message?.content || JSON.stringify(aiRaw);\n\n// Try to extract JSON from the response\nlet analysis = { relevant: false, confidence: 0, direction: 'NEUTRAL', reasoning: 'Could not parse AI response' };\ntry {\n  const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    analysis = JSON.parse(jsonMatch[0]);\n  }\n} catch(e) {\n  // Keep defaults\n}\n\nreturn [{\n  json: {\n    confidence: analysis.confidence || 0,\n    direction: analysis.direction || 'NEUTRAL',\n    reasoning: analysis.reasoning || '',\n    relevant: analysis.relevant || false,\n    newsTitle: promptData.newsTitle,\n    newsLink: promptData.newsLink,\n    marketQuestion: promptData.marketQuestion,\n    currentPrice: promptData.currentPrice,\n    slug: promptData.slug\n  }\n}];"
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000010",
      "name": "Parse AI Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [2240, 300]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "confidence-check-001",
              "leftValue": "={{ $json.confidence }}",
              "rightValue": 70,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000011",
      "name": "Confidence > 70?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.1,
      "position": [2490, 300]
    },
    {
      "parameters": {
        "chatId": "",
        "text": "=*AI Market Signal*\n\nNews: {{ $json.newsTitle }}\nMarket: {{ $json.marketQuestion }}\nPrice: ${{ $json.currentPrice }}\nDirection: {{ $json.direction }}\nConfidence: {{ $json.confidence }}%\n\n_{{ $json.reasoning }}_\n\nhttps://polymarket.com/event/{{ $json.slug }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "b2c3d4e5-2222-4b00-c000-000000000012",
      "name": "Send Signal to Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [2740, 240],
      "credentials": {
        "telegramApi": {
          "id": "",
          "name": "Telegram Bot"
        }
      }
    }
  ],
  "connections": {
    "RSS News Feed": {
      "main": [
        [
          {
            "node": "Extract Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Keywords": {
      "main": [
        [
          {
            "node": "Search Polymarket",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Polymarket": {
      "main": [
        [
          {
            "node": "Pick Top Market",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick Top Market": {
      "main": [
        [
          {
            "node": "Market Found?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Market Found?": {
      "main": [
        [
          {
            "node": "Get Price",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Get Price": {
      "main": [
        [
          {
            "node": "Build AI Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build AI Prompt": {
      "main": [
        [
          {
            "node": "AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Analysis": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "Confidence > 70?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confidence > 70?": {
      "main": [
        [
          {
            "node": "Send Signal to Telegram",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    { "name": "polymarket" },
    { "name": "prediction-markets" },
    { "name": "ai" },
    { "name": "news" },
    { "name": "telegram" }
  ],
  "meta": {
    "instanceId": ""
  }
}
