The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"id": "a9bbe9d0-51aa-40f8-8931-f405c695c732",
"name": "Window Buffer Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
1140,
140
],
"parameters": {
"sessionKey": "=335458847",
"sessionIdType": "customKey"
},
"typeVersion": 1.3
},
{
"id": "2d6315d6-959d-4e16-97ed-30839d826ce2",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1080,
-100
],
"parameters": {
"text": "=Ticker = {{ $json[\"Ticker symbol:\"] }}",
"options": {
"systemMessage": "=# Overview\nYou are an AI agent specialized in stock analysis. You provide technical analysis and sentiment for stock investments by combining chart data and news sentiment.\n\n# Instructions\n1. When a user requests an analysis of a stock with its symbol:\n - Send the stock symbol to both tools **technical_analysis** and **trends_analysis**\n - Analyze the combined data and prepare a JSON report with your insights\n - Provide a clear recommendation (positive, neutral, or negative)\n2. Your output must be in the format of a structured JSON object that will be used to fill an HTML template.\n3. Translate the article titles in topArticles to Hebrew\n4. Translate the sentimentHebrew results to only one of these values:\n\"\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05d6\u05e7/\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05dc\u05e9/\u05e0\u05d9\u05d9\u05d8\u05e8\u05dc\u05d9/\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05dc\u05e9/\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05d6\u05e7\". Somewhat=\u05d7\u05dc\u05e9.\n5. Write the Date value in each article: \"topArticles\" only in this format: \"DD/MM/YYYY\".\n6. Update the technicalAnalysis value as a detailed technical analysis of three paragraphs, which explains even to those who don't understand economics what you did and how you reached your conclusions. Touch on all the indicators examined (Volume, EMA, RSI, Fibonacci retracement, MACD, Bollinger bands, Resistance and support levels)\n7. Ensure that the text in the technicalAnalysis value is written in proper Hebrew, like a professional analyst. Use the think tool\n8. In the Recommendation value - recommend to buy or sell only if you think with high probability that there will be a rise or fall. Use the think tool to verify your Recommendation based on recommendationText. Advise something only if you really believe it. Your default is the \"\u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05d7\u05db\u05d5\u05ea\" value.\n\n## Tools\n- **technical_analysis**: Generates technical analysis based on stock charts\n- **trends_analysis**: Analyzes news sentiment for the requested stock\n\n## Response Format\nYou must respond with a JSON object containing exactly the following keys to fill the HTML template:\n\n```json\n{\n \"stockSymbol\": \"\u05e1\u05d9\u05de\u05d5\u05dc\",\n \"analysisDate\": \"DD/MM/YYYY\",\n \"recommendationClass\": \"positive/neutral/negative\",\n \"recommendationTitle\": \"\u05db\u05d5\u05ea\u05e8\u05ea \u05d4\u05de\u05dc\u05e6\u05d4 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea\",\n \"recommendationText\": \"\u05d4\u05e1\u05d1\u05e8 \u05de\u05e4\u05d5\u05e8\u05d8 \u05e9\u05dc \u05d4\u05d4\u05de\u05dc\u05e6\u05d4 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea\",\n \"bullishCount\": 0,\n \"neutralCount\": 0, \n \"bearishCount\": 0,\n \"bullishHeight\": 0,\n \"neutralHeight\": 0,\n \"bearishHeight\": 0,\n \"overallSentiment\": \"\u05d7\u05d9\u05d5\u05d1\u05d9/\u05e0\u05d9\u05d9\u05d8\u05e8\u05dc\u05d9/\u05e9\u05dc\u05d9\u05dc\u05d9\",\n \"Recommendation\": \"\u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05e7\u05e0\u05d5\u05ea/ \u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05d7\u05db\u05d5\u05ea/ \u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05de\u05db\u05d5\u05e8\",\n \"sentimentScore\": 0.00,\n \"chartImageUrl\": \"URL_PLACEHOLDER\",\n \"technicalAnalysis\": \"\u05e0\u05d9\u05ea\u05d5\u05d7 \u05d8\u05db\u05e0\u05d9 \u05de\u05e4\u05d5\u05e8\u05d8 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea \u05e2\u05dd \u05ea\u05d2\u05d9 <p>\",\n \"topArticles\": [\n {\n \"title\": \"\u05db\u05d5\u05ea\u05e8\u05ea \u05d4\u05de\u05d0\u05de\u05e8 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea\",\n \"url\": \"\u05db\u05ea\u05d5\u05d1\u05ea URL \u05e9\u05dc \u05d4\u05de\u05d0\u05de\u05e8\",\n \"source\": \"\u05e9\u05dd \u05d4\u05de\u05e7\u05d5\u05e8 \u05d1\u05d0\u05e0\u05d2\u05dc\u05d9\u05ea\",\n \"date\": \"DD/MM/YYYY\",\n \"sentimentClass\": \"bullish/neutral/bearish\",\n \"sentimentHebrew\": \"\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05d6\u05e7/\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05dc\u05e9/\u05e0\u05d9\u05d9\u05d8\u05e8\u05dc\u05d9/\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05dc\u05e9/\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05d6\u05e7\"\n }\n ],\n \"hotTopics\": [\n {\n \"topic\": \"\u05e9\u05dd \u05d4\u05e0\u05d5\u05e9\u05d0 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea\",\n \"article_count\": 0,\n \"average_relevance\": \"0.00\"\n }\n ]\n}"
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.7
},
{
"id": "14112026-19eb-493f-971b-28455a8d4412",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
680,
-220
],
"parameters": {
"color": 4,
"width": 1820,
"height": 580,
"content": "# AI Agent\nAI agent powered by GPT-4o that analyses stocks by combining technical analysis and news sentiment, generating detailed reports in Hebrew with data-driven investment recommendations"
},
"typeVersion": 1
},
{
"id": "8b2e573e-7acc-4b0b-a708-4ce33873a893",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
680,
380
],
"parameters": {
"width": 2820,
"height": 920,
"content": "# Technical Analysis Tool\nA tool that performs in-depth technical analysis of stock charts by combining visual pattern recognition with quantitative indicators. It fetches data from Chart-img API for generating visual charts, Twelve Data API for historical prices and technical indicators (Bollinger Bands, MACD), and uses OpenAI's GPT-4o for visual chart pattern recognition.\nThe system synthesizes this multi-source data into a comprehensive technical assessment with actionable trading insights based on support/resistance levels, Fibonacci retracements, and candlestick patterns."
},
"typeVersion": 1
},
{
"id": "b0d49fa6-5c57-4ab5-a752-93d7d278b8fa",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
2520,
-220
],
"parameters": {
"width": 980,
"height": 580,
"content": "# Trends Analysis Tool\nA tool that analyses news sentiment for requested stocks by fetching recent financial news articles, calculating sentiment metrics, identifying influential stories, and extracting trending topics. It processes data from Alpha Vantage's news API, determines overall market sentiment, and delivers structured analysis on stock sentiment, relevance, and market outlook."
},
"typeVersion": 1
},
{
"id": "13a242cf-0a01-4aea-a58e-9b734aed912c",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
1900,
140
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"stockSymbol\": \"\u05e1\u05d9\u05de\u05d5\u05dc\",\n \"analysisDate\": \"DD/MM/YYYY\",\n \"recommendationClass\": \"positive/neutral/negative\",\n \"recommendationTitle\": \"\u05db\u05d5\u05ea\u05e8\u05ea \u05d4\u05de\u05dc\u05e6\u05d4 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea\",\n \"recommendationText\": \"\u05d4\u05e1\u05d1\u05e8 \u05de\u05e4\u05d5\u05e8\u05d8 \u05e9\u05dc \u05d4\u05d4\u05de\u05dc\u05e6\u05d4 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea\",\n \"bullishCount\": 0,\n \"neutralCount\": 0, \n \"bearishCount\": 0,\n \"bullishHeight\": 0,\n \"neutralHeight\": 0,\n \"bearishHeight\": 0,\n \"overallSentiment\": \"\u05d7\u05d9\u05d5\u05d1\u05d9/\u05e0\u05d9\u05d9\u05d8\u05e8\u05dc\u05d9/\u05e9\u05dc\u05d9\u05dc\u05d9\",\n \"Recommendation\": \"\u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05e7\u05e0\u05d5\u05ea/ \u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05d7\u05db\u05d5\u05ea/ \u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05de\u05db\u05d5\u05e8\",\n \"sentimentScore\": 0.00,\n \"chartImageUrl\": \"URL_PLACEHOLDER\",\n \"technicalAnalysis\": \"\u05e0\u05d9\u05ea\u05d5\u05d7 \u05d8\u05db\u05e0\u05d9 \u05de\u05e4\u05d5\u05e8\u05d8 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea \u05e2\u05dd \u05ea\u05d2\u05d9 <p>\",\n \"topArticles\": [\n {\n \"title\": \"\u05db\u05d5\u05ea\u05e8\u05ea \u05d4\u05de\u05d0\u05de\u05e8\",\n \"url\": \"\u05db\u05ea\u05d5\u05d1\u05ea URL \u05e9\u05dc \u05d4\u05de\u05d0\u05de\u05e8\",\n \"source\": \"\u05e9\u05dd \u05d4\u05de\u05e7\u05d5\u05e8\",\n \"date\": \"DD/MM/YYYY\",\n \"sentimentClass\": \"bullish/neutral/bearish\",\n \"sentimentHebrew\": \"\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05d6\u05e7/\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05dc\u05e9/\u05e0\u05d9\u05d9\u05d8\u05e8\u05dc\u05d9/\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05dc\u05e9/\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05d6\u05e7\"\n }\n ],\n \"hotTopics\": [\n {\n \"topic\": \"\u05e9\u05dd \u05d4\u05e0\u05d5\u05e9\u05d0 \u05d1\u05e2\u05d1\u05e8\u05d9\u05ea\",\n \"article_count\": 0,\n \"average_relevance\": \"0.00\"\n }\n ]\n}"
},
"typeVersion": 1.2
},
{
"id": "bb5dd63a-a3e6-408e-a5c9-13e9f72f2b26",
"name": "GPT 4o",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
960,
140
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "94d820d2-eb20-4184-8e21-1ed5936c9166",
"name": "Generate HTML",
"type": "n8n-nodes-base.html",
"position": [
1860,
-100
],
"parameters": {
"html": "<!DOCTYPE html>\n<html dir=\"rtl\" lang=\"he\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>\u05e0\u05d9\u05ea\u05d5\u05d7 \u05de\u05e0\u05d9\u05d9\u05ea {{ $('AI Agent').item.json.output.stockSymbol }}</title>\n</head>\n<body style=\"margin: 0; padding: 0; font-family: 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif; background-color: #f5f7fa; color: #333; line-height: 1.6; -webkit-font-smoothing: antialiased; font-size: 16px; text-align: right; direction: rtl;\">\n <!-- \u05e2\u05d5\u05d8\u05e3 \u05e8\u05d0\u05e9\u05d9 -->\n <div style=\"max-width: 650px; margin: 0 auto; background-color: #ffffff; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.08); margin-top: 30px; margin-bottom: 30px; text-align: right; direction: rtl;\">\n \n <!-- \u05db\u05d5\u05ea\u05e8\u05ea \u05e2\u05dc\u05d9\u05d5\u05e0\u05d4 -->\n <div style=\"background: linear-gradient(135deg, #0057ff 0%, #00b2ff 100%); padding: 30px 40px; text-align: center; position: relative; overflow: hidden; margin-bottom: 20px;\">\n <div style=\"position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxwYXR0ZXJuIGlkPSJwYXR0ZXJuIiB4PSIwIiB5PSIwIiB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHBhdHRlcm5UcmFuc2Zvcm09InJvdGF0ZSgzMCkiPjxwYXRoIGQ9Ik0wIDEwIEw0MCAxMCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLW9wYWNpdHk9IjAuMSIvPjwvcGF0dGVybj48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNwYXR0ZXJuKSIvPjwvc3ZnPg=='); opacity: 0.2;\"></div>\n <h1 style=\"color: #ffffff; font-weight: 700; font-size: 28px; margin: 0 0 5px 0; letter-spacing: -0.5px; position: relative;\">\u05e0\u05d9\u05ea\u05d5\u05d7 \u05de\u05e0\u05d9\u05d9\u05ea {{ $('AI Agent').item.json.output.stockSymbol }}</h1>\n <div style=\"color: rgba(255,255,255,0.85); font-size: 15px; position: relative;\">\u05ea\u05d0\u05e8\u05d9\u05da: {{ $('AI Agent').item.json.output.analysisDate }}</div>\n </div>\n \n <!-- \u05ea\u05d5\u05db\u05df \u05d4\u05de\u05d9\u05d9\u05dc -->\n <div style=\"padding: 40px; text-align: right; direction: rtl;\">\n \n <!-- \u05ea\u05d9\u05d1\u05ea \u05d4\u05de\u05dc\u05e6\u05d4 -->\n <div style=\"background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.04); padding: 25px; margin-bottom: 40px; position: relative; overflow: hidden; text-align: right;\">\n <div style=\"position: absolute; right: 0; top: 0; bottom: 0; width: 6px; background-color: #f7b955;\"></div>\n <div style=\"position: absolute; right: 0; top: 0; width: 100%; height: 100%; background: linear-gradient(90deg, rgba(247, 185, 85, 0.07) 0%, rgba(247, 185, 85, 0) 50%);\"></div>\n <div style=\"text-align: center; position: relative;\">\n <div style=\"display: inline-block; width: 40px; height: 40px; border-radius: 50%; margin-bottom: 10px; background-color: rgba(247, 185, 85, 0.15); text-align: center;\">\n <span style=\"font-size: 20px; line-height: 40px;\">\u2696\ufe0f</span>\n </div>\n <h2 style=\"margin: 0 0 10px 0; color: #f7b955; font-size: 22px; font-weight: 700; text-align: center;\">{{ $('AI Agent').item.json.output.recommendationTitle }}</h2>\n <p style=\"margin: 0; font-size: 16px; line-height: 1.6; color: #4a5568; text-align: right;\">{{ $json.message.content.recommendationText }}</p>\n <div style=\"margin-top: 25px;\">\n <a style=\"display: inline-block; background-color: #29cc7a; color: white; font-weight: 600; font-size: 16px; padding: 12px 30px; border-radius: 8px; text-decoration: none; box-shadow: 0 4px 6px rgba(41, 204, 122, 0.25); transition: all 0.2s ease;\">{{ $('AI Agent').item.json.output.Recommendation }}</a>\n </div>\n </div>\n </div>\n\n <!-- \u05e0\u05d9\u05ea\u05d5\u05d7 \u05d8\u05db\u05e0\u05d9 -->\n <div style=\"margin-bottom: 40px; text-align: right;\">\n <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">\u05e0\u05d9\u05ea\u05d5\u05d7 \u05d8\u05db\u05e0\u05d9</h2>\n \n <div style=\"background: #ffffff; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.06); overflow: hidden; margin-bottom: 25px;\">\n <img src=\"{{ $('AI Agent').item.json.output.chartImageUrl }}\" alt=\"\u05d2\u05e8\u05e3 \u05d8\u05db\u05e0\u05d9 {{ $('AI Agent').item.json.output.stockSymbol }}\" style=\"width: 100%; display: block; max-height: 450px; object-fit: contain;\">\n </div>\n \n <div style=\"background-color: #f8fafc; border-radius: 12px; padding: 25px; font-size: 15px; line-height: 1.6; color: #4a5568; text-align: right;\">\n {{ $json.message.content.technicalAnalysis }}\n </div>\n </div>\n \n <!-- \u05e0\u05d9\u05ea\u05d5\u05d7 \u05e1\u05e0\u05d8\u05d9\u05de\u05e0\u05d8 -->\n <div style=\"margin-bottom: 40px; text-align: right;\">\n <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">\u05e0\u05d9\u05ea\u05d5\u05d7 \u05e1\u05e0\u05d8\u05d9\u05de\u05e0\u05d8 \u05e9\u05d5\u05e7</h2>\n \n <!-- \u05d2\u05e8\u05e3 \u05e1\u05e0\u05d8\u05d9\u05de\u05e0\u05d8 - \u05e2\u05dd \u05d8\u05d1\u05dc\u05d4 \u05d1\u05de\u05e7\u05d5\u05dd flex -->\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin: 45px 0 30px 0;\">\n <tr valign=\"bottom\" align=\"center\">\n <td width=\"33%\" style=\"text-align: center; padding: 0 10px;\">\n <div style=\"font-weight: 600; margin-bottom: 10px; color: #29cc7a;\">{{ $('AI Agent').item.json.output.bullishCount }}</div>\n <div style=\"background-color: #29cc7a; border-radius: 8px 8px 0 0; width: 100%; height: {{ $('AI Agent').item.json.output.bullishHeight }}px; margin: 0 auto; opacity: 0.85;\"></div>\n <div style=\"font-size: 14px; color: #4a5568; margin-top: 10px;\">\u05d7\u05d9\u05d5\u05d1\u05d9</div>\n </td>\n <td width=\"33%\" style=\"text-align: center; padding: 0 10px;\">\n <div style=\"font-weight: 600; margin-bottom: 10px; color: #f7b955;\">{{ $('AI Agent').item.json.output.neutralCount }}</div>\n <div style=\"background-color: #f7b955; border-radius: 8px 8px 0 0; width: 100%; height: {{ $('AI Agent').item.json.output.neutralHeight }}px; margin: 0 auto; opacity: 0.85;\"></div>\n <div style=\"font-size: 14px; color: #4a5568; margin-top: 10px;\">\u05e0\u05d9\u05d9\u05d8\u05e8\u05dc\u05d9</div>\n </td>\n <td width=\"33%\" style=\"text-align: center; padding: 0 10px;\">\n <div style=\"font-weight: 600; margin-bottom: 10px; color: #f55e5e;\">{{ $('AI Agent').item.json.output.bearishCount }}</div>\n <div style=\"background-color: #f55e5e; border-radius: 8px 8px 0 0; width: 100%; height: {{ $('AI Agent').item.json.output.bearishHeight }}px; margin: 0 auto; opacity: 0.85;\"></div>\n <div style=\"font-size: 14px; color: #4a5568; margin-top: 10px;\">\u05e9\u05dc\u05d9\u05dc\u05d9</div>\n </td>\n </tr>\n </table>\n \n <div style=\"background-color: #f8fafc; border-radius: 10px; padding: 15px; text-align: center; font-size: 15px;\">\n \u05d4\u05e1\u05e0\u05d8\u05d9\u05de\u05e0\u05d8 \u05d4\u05db\u05dc\u05dc\u05d9 \u05dc\u05de\u05e0\u05d9\u05d9\u05ea <strong>{{ $('AI Agent').item.json.output.stockSymbol }}</strong> \u05d4\u05d5\u05d0 \n <span style=\"font-weight: 600; color: #f7b955;\">{{ $('AI Agent').item.json.output.overallSentiment }}</span> \n \u05e2\u05dd \u05e6\u05d9\u05d5\u05df \u05e9\u05dc <strong>{{ $('AI Agent').item.json.output.sentimentScore }}</strong>\n </div>\n </div>\n \n <!-- \u05de\u05d0\u05de\u05e8\u05d9\u05dd \u05de\u05e9\u05e4\u05d9\u05e2\u05d9\u05dd -->\n <div style=\"margin-bottom: 40px; text-align: right;\">\n <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">\u05de\u05d0\u05de\u05e8\u05d9\u05dd \u05de\u05e9\u05e4\u05d9\u05e2\u05d9\u05dd</h2>\n \n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse;\">\n <!-- \u05de\u05d0\u05de\u05e8 1 -->\n <tr>\n <td style=\"padding-bottom: 16px;\">\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n <tr>\n <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n <td style=\"padding: 18px 22px;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n <a href=\"{{ $('AI Agent').item.json.output.topArticles[0].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[0].title }}</a>\n </h3>\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n <tr>\n <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[0].source }} | {{ $('AI Agent').item.json.output.topArticles[0].date }}</td>\n <td style=\"text-align: left;\">\n <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n {{ $('AI Agent').item.json.output.topArticles[0].sentimentHebrew }}\n </div>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- \u05de\u05d0\u05de\u05e8 2 -->\n <tr>\n <td style=\"padding-bottom: 16px;\">\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n <tr>\n <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n <td style=\"padding: 18px 22px;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n <a href=\"{{ $('AI Agent').item.json.output.topArticles[1].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[1].title }}</a>\n </h3>\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n <tr>\n <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[1].source }} | {{ $('AI Agent').item.json.output.topArticles[1].date }}</td>\n <td style=\"text-align: left;\">\n <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n {{ $('AI Agent').item.json.output.topArticles[1].sentimentHebrew }}\n </div>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- \u05de\u05d0\u05de\u05e8 3 -->\n <tr>\n <td style=\"padding-bottom: 16px;\">\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n <tr>\n <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n <td style=\"padding: 18px 22px;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n <a href=\"{{ $('AI Agent').item.json.output.topArticles[2].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[2].title }}</a>\n </h3>\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n <tr>\n <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[2].source }} | {{ $('AI Agent').item.json.output.topArticles[2].date }}</td>\n <td style=\"text-align: left;\">\n <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n {{ $('AI Agent').item.json.output.topArticles[2].sentimentHebrew }}\n </div>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- \u05de\u05d0\u05de\u05e8 4 -->\n <tr>\n <td style=\"padding-bottom: 16px;\">\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n <tr>\n <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n <td style=\"padding: 18px 22px;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n <a href=\"{{ $('AI Agent').item.json.output.topArticles[3].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[3].title }}</a>\n </h3>\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n <tr>\n <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[3].source }} | {{ $('AI Agent').item.json.output.topArticles[3].date }}</td>\n <td style=\"text-align: left;\">\n <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n {{ $('AI Agent').item.json.output.topArticles[3].sentimentHebrew }}\n </div>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n \n <!-- \u05de\u05d0\u05de\u05e8 5 -->\n <tr>\n <td style=\"padding-bottom: 16px;\">\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; background-color: #f8fafc; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); overflow: hidden;\">\n <tr>\n <td width=\"4\" style=\"background-color: #f7b955;\"></td>\n <td style=\"padding: 18px 22px;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 16px; font-weight: 600; line-height: 1.4; text-align: right;\">\n <a href=\"{{ $('AI Agent').item.json.output.topArticles[4].url }}\" target=\"_blank\" style=\"color: #2b6cb0; text-decoration: none;\">{{ $('AI Agent').item.json.output.topArticles[4].title }}</a>\n </h3>\n <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-top: 10px;\">\n <tr>\n <td style=\"font-size: 13px; color: #718096; text-align: right;\">{{ $('AI Agent').item.json.output.topArticles[4].source }} | {{ $('AI Agent').item.json.output.topArticles[4].date }}</td>\n <td style=\"text-align: left;\">\n <div style=\"display: inline-block; padding: 3px 10px; border-radius: 30px; font-weight: 500; font-size: 12px; background-color: rgba(247, 185, 85, 0.1); color: #f7b955;\">\n {{ $('AI Agent').item.json.output.topArticles[4].sentimentHebrew }}\n </div>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </div>\n \n <!-- \u05e0\u05d5\u05e9\u05d0\u05d9\u05dd \u05d7\u05de\u05d9\u05dd - \u05d2\u05e8\u05e1\u05d4 \u05de\u05e9\u05d5\u05e4\u05e8\u05ea \u05dc\u05de\u05d5\u05d1\u05d9\u05d9\u05dc -->\n <div style=\"margin-bottom: 30px; text-align: right;\">\n <h2 style=\"font-size: 20px; color: #1a202c; margin: 0 0 20px 0; padding-bottom: 12px; border-bottom: 1px solid #edf2f7; font-weight: 700; text-align: right;\">\u05e0\u05d5\u05e9\u05d0\u05d9\u05dd \u05d7\u05de\u05d9\u05dd</h2>\n \n <div style=\"background-color: #f8fafc; border-radius: 12px; padding: 20px 25px; text-align: right;\">\n <p style=\"margin: 0 0 15px 0; font-size: 15px; color: #4a5568; text-align: right;\">\u05d4\u05e0\u05d5\u05e9\u05d0\u05d9\u05dd \u05d4\u05de\u05e8\u05db\u05d6\u05d9\u05d9\u05dd \u05e9\u05de\u05d5\u05e4\u05d9\u05e2\u05d9\u05dd \u05d1\u05d7\u05d3\u05e9\u05d5\u05ea \u05e2\u05dc {{ $('AI Agent').item.json.output.stockSymbol }}:</p>\n \n <!-- \u05e0\u05d5\u05e9\u05d0 1 -->\n <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n {{ $('AI Agent').item.json.output.hotTopics[0].topic }}\n </div>\n <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n <strong>{{ $('AI Agent').item.json.output.hotTopics[0].article_count }}</strong> \u05de\u05d0\u05de\u05e8\u05d9\u05dd\n </div>\n </div>\n </div>\n <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[0].average_relevance }} * 100%);\"></div>\n </div>\n <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">\u05e8\u05dc\u05d5\u05d5\u05e0\u05d8\u05d9\u05d5\u05ea: {{ $('AI Agent').item.json.output.hotTopics[0].average_relevance }}</div>\n </div>\n \n <!-- \u05e0\u05d5\u05e9\u05d0 2 -->\n <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n {{ $('AI Agent').item.json.output.hotTopics[1].topic }}\n </div>\n <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n <strong>{{ $('AI Agent').item.json.output.hotTopics[1].article_count }}</strong> \u05de\u05d0\u05de\u05e8\u05d9\u05dd\n </div>\n </div>\n </div>\n <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[1].average_relevance }} * 100%);\"></div>\n </div>\n <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">\u05e8\u05dc\u05d5\u05d5\u05e0\u05d8\u05d9\u05d5\u05ea: {{ $('AI Agent').item.json.output.hotTopics[1].average_relevance }}</div>\n </div>\n \n <!-- \u05e0\u05d5\u05e9\u05d0 3 -->\n <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n {{ $('AI Agent').item.json.output.hotTopics[2].topic }}\n </div>\n <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n <strong>{{ $('AI Agent').item.json.output.hotTopics[2].article_count }}</strong> \u05de\u05d0\u05de\u05e8\u05d9\u05dd\n </div>\n </div>\n </div>\n <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[2].average_relevance }} * 100%);\"></div>\n </div>\n <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">\u05e8\u05dc\u05d5\u05d5\u05e0\u05d8\u05d9\u05d5\u05ea: {{ $('AI Agent').item.json.output.hotTopics[2].average_relevance }}</div>\n </div>\n \n <!-- \u05e0\u05d5\u05e9\u05d0 4 -->\n <div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #edf2f7;\">\n <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n {{ $('AI Agent').item.json.output.hotTopics[3].topic }}\n </div>\n <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n <strong>{{ $('AI Agent').item.json.output.hotTopics[3].article_count }}</strong> \u05de\u05d0\u05de\u05e8\u05d9\u05dd\n </div>\n </div>\n </div>\n <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[3].average_relevance }} * 100%);\"></div>\n </div>\n <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">\u05e8\u05dc\u05d5\u05d5\u05e0\u05d8\u05d9\u05d5\u05ea: {{ $('AI Agent').item.json.output.hotTopics[3].average_relevance }}</div>\n </div>\n \n <!-- \u05e0\u05d5\u05e9\u05d0 5 -->\n <div style=\"margin-bottom: 0;\">\n <div style=\"display: table; width: 100%; margin-bottom: 8px;\">\n <div style=\"display: table-cell; vertical-align: middle; text-align: right; font-weight: 600; font-size: 15px;\">\n {{ $('AI Agent').item.json.output.hotTopics[4].topic }}\n </div>\n <div style=\"display: table-cell; vertical-align: middle; text-align: left; white-space: nowrap;\">\n <div style=\"display: inline-block; background-color: #edf2f7; border-radius: 30px; padding: 4px 12px; font-size: 13px; color: #4a5568; text-align: center;\">\n <strong>{{ $('AI Agent').item.json.output.hotTopics[4].article_count }}</strong> \u05de\u05d0\u05de\u05e8\u05d9\u05dd\n </div>\n </div>\n </div>\n <div style=\"background-color: #e2e8f0; height: 4px; width: 100%; border-radius: 2px; overflow: hidden;\">\n <div style=\"background-color: #4299e1; height: 100%; width: calc({{ $('AI Agent').item.json.output.hotTopics[4].average_relevance }} * 100%);\"></div>\n </div>\n <div style=\"text-align: left; font-size: 12px; color: #718096; margin-top: 4px;\">\u05e8\u05dc\u05d5\u05d5\u05e0\u05d8\u05d9\u05d5\u05ea: {{ $('AI Agent').item.json.output.hotTopics[4].average_relevance }}</div>\n </div>\n </div>\n </div>\n\t \n <!-- \u05e4\u05d5\u05d8\u05e8 -->\n <div style=\"background-color: #f8fafc; padding: 25px 40px; text-align: center; border-top: 1px solid #edf2f7;\">\n <div style=\"font-size: 13px; color: #718096; line-height: 1.6;\">\n <p style=\"margin: 0 0 8px 0;\">\u05d3\u05d5\u05d7 \u05d6\u05d4 \u05e0\u05d5\u05e6\u05e8 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9 \u05d5\u05d0\u05d9\u05e0\u05d5 \u05de\u05d4\u05d5\u05d5\u05d4 \u05d4\u05de\u05dc\u05e6\u05ea \u05d4\u05e9\u05e7\u05e2\u05d4.</p>\n <p style=\"margin: 0;\">\u05d9\u05e9 \u05dc\u05d4\u05ea\u05d9\u05d9\u05e2\u05e5 \u05e2\u05dd \u05d9\u05d5\u05e2\u05e5 \u05d4\u05e9\u05e7\u05e2\u05d5\u05ea \u05de\u05d5\u05e8\u05e9\u05d4 \u05dc\u05e4\u05e0\u05d9 \u05e7\u05d1\u05dc\u05ea \u05d4\u05d7\u05dc\u05d8\u05d5\u05ea \u05d4\u05e9\u05e7\u05e2\u05d4.</p>\n </div>\n <div style=\"margin-top: 20px;\">\n \u05e0\u05d1\u05e0\u05d4 \u05d1-\u2764\ufe0f \u05e2\"\u05d9 <a href=\"https://www.linkedin.com/in/elay-g\" style=\"display: inline-block; text-decoration: none;\">\u05e2\u05d9\u05dc\u05d9 \u05d2\u05d6</a>\n </div>\n </div>\n \n </div>\n\n</body>\n</html>"
},
"typeVersion": 1.2
},
{
"id": "84a2fe62-e936-49ca-83d6-a02371e02166",
"name": "Send Stock Analysis",
"type": "n8n-nodes-base.emailSend",
"position": [
2280,
-100
],
"parameters": {
"html": "={{ $json.html }}",
"options": {},
"subject": "=\u05d4\u05e1\u05e7\u05d9\u05e8\u05d4 \u05d4\u05d9\u05d5\u05de\u05d9\u05ea \u05e9\u05dc \u05de\u05e0\u05d9\u05d9\u05ea {{ $('AI Agent').item.json.output.stockSymbol }}: {{ $('AI Agent').item.json.output.analysisDate }}",
"toEmail": "={{ $('On form submission').item.json[\"Email:\"] }}",
"fromEmail": "Elay's AI Assistant <elayguez@gmail.com>"
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"executeOnce": false,
"typeVersion": 2.1
},
{
"id": "36943e20-b0fc-40b0-b695-e0bdbd9182d1",
"name": "Adjust HTML Colors",
"type": "n8n-nodes-base.code",
"position": [
2080,
-100
],
"parameters": {
"jsCode": "// New function to remove topics with only one article - ultra-simple approach\nfunction removeSingleArticleTopics(html) {\n // First, see if there are any topics with exactly 1 article\n if (!html.includes('<strong>1</strong> \u05de\u05d0\u05de\u05e8\u05d9\u05dd')) {\n console.log('No topics with 1 article found');\n return html;\n }\n\n // Find each line that contains the \"\u05e0\u05d5\u05e9\u05d0\" comment\n // and check if it has exactly 1 article mentioned\n const lines = html.split('\\n');\n const linesToRemove = [];\n\n // For each line containing \"1 \u05de\u05d0\u05de\u05e8\u05d9\u05dd\", find the topic it belongs to\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].includes('<strong>1</strong> \u05de\u05d0\u05de\u05e8\u05d9\u05dd')) {\n console.log(`Found line ${i} with 1 article mention`);\n \n // Go back to find the start of this topic\n let startLine = -1;\n for (let j = i; j >= 0; j--) {\n if (lines[j].includes('<!-- \u05e0\u05d5\u05e9\u05d0') || \n lines[j].includes('<div style=\"margin-bottom: 15px; padding-bottom: 15px; border-bottom:')) {\n startLine = j;\n break;\n }\n }\n \n if (startLine === -1) {\n console.log(`Couldn't find start of topic for line ${i}`);\n continue;\n }\n \n // Go forward to find the end of this topic\n let endLine = -1;\n let divCount = 0;\n for (let j = startLine; j < lines.length; j++) {\n // Count opening divs\n const openMatches = lines[j].match(/<div/g);\n if (openMatches) {\n divCount += openMatches.length;\n }\n \n // Count closing divs\n const closeMatches = lines[j].match(/<\\/div>/g);\n if (closeMatches) {\n divCount -= closeMatches.length;\n }\n \n // When divCount returns to 0, we've found the end\n if (divCount === 0 && j > startLine) {\n endLine = j;\n break;\n }\n }\n \n if (endLine === -1) {\n console.log(`Couldn't find end of topic for line ${i}`);\n continue;\n }\n \n // Now we have the start and end lines of the topic\n console.log(`Found topic from line ${startLine} to ${endLine}`);\n \n // Mark these lines for removal\n for (let j = startLine; j <= endLine; j++) {\n linesToRemove.push(j);\n }\n }\n }\n \n // Remove the marked lines\n const newLines = [];\n for (let i = 0; i < lines.length; i++) {\n if (!linesToRemove.includes(i)) {\n newLines.push(lines[i]);\n }\n }\n \n console.log(`Removed ${linesToRemove.length} lines in total`);\n return newLines.join('\\n');\n}// Code for updating colors according to sentiment - for n8n\n\n// Define colors by sentiment type\nconst colors = {\n positive: {\n main: '#29cc7a', // Green\n background: 'rgba(41, 204, 122, 0.15)',\n gradient: 'rgba(41, 204, 122, 0.07)',\n accent: 'rgba(41, 204, 122, 0.1)'\n },\n neutral: {\n main: '#f7b955', // Orange\n background: 'rgba(247, 185, 85, 0.15)',\n gradient: 'rgba(247, 185, 85, 0.07)',\n accent: 'rgba(247, 185, 85, 0.1)'\n },\n negative: {\n main: '#f55e5e', // Red\n background: 'rgba(245, 94, 94, 0.15)',\n gradient: 'rgba(245, 94, 94, 0.07)',\n accent: 'rgba(245, 94, 94, 0.1)'\n }\n};\n\n// Function to identify sentiment type from text\nfunction getSentimentType(text) {\n if (!text) return 'neutral';\n \n const lowerText = text.toLowerCase();\n \n // Negative keywords - check first because there are expressions with both \"positive\" and \"negative\" together\n if (lowerText.includes('\u05e9\u05dc\u05d9\u05dc\u05d9') || lowerText.includes('negative') || \n lowerText.includes('bearish') || lowerText.includes('\u05d9\u05e8\u05d9\u05d3\u05d4') || \n lowerText.includes('\u05d3\u05d5\u05d1\u05d9') || lowerText.includes('\u05de\u05db\u05d9\u05e8\u05d4') || \n lowerText.includes('\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05d6\u05e7') || lowerText.includes('\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05dc\u05e9') ||\n lowerText.includes('\u05e9\u05dc\u05d9\u05dc\u05d9\u05ea')) {\n return 'negative';\n }\n \n // Positive keywords\n if (lowerText.includes('\u05d7\u05d9\u05d5\u05d1\u05d9') || lowerText.includes('positive') || \n lowerText.includes('bullish') || lowerText.includes('\u05e2\u05dc\u05d9\u05d9\u05d4') || \n lowerText.includes('\u05e9\u05d5\u05e8\u05d9') || lowerText.includes('\u05e7\u05e0\u05d9\u05d9\u05d4') || \n lowerText.includes('\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05d6\u05e7') || lowerText.includes('\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05dc\u05e9') ||\n lowerText.includes('\u05d7\u05d9\u05d5\u05d1\u05d9\u05ea')) {\n return 'positive';\n }\n \n // Additional check for expressions containing only \"strong\" or \"weak\"\n if (lowerText.includes('\u05d7\u05d6\u05e7')) {\n // If no negative word, assume it's positive\n return 'positive';\n }\n \n // Default - neutral\n return 'neutral';\n}\n\n// Function to check if a specific text belongs to a sentiment - used for bug fixing\nfunction debugSentiment(text) {\n console.log(`Sentiment check: \"${text}\" => ${getSentimentType(text)}`);\n}\n\n// New function to remove undefined articles from HTML\nfunction removeUndefinedArticles(html) {\n // Find all article blocks\n const articleBlocksRegex = /<tr>\\s*<td style=\"padding-bottom: 16px;\">\\s*<table[^>]*>[\\s\\S]*?<\\/table>\\s*<\\/td>\\s*<\\/tr>/g;\n const articleBlocks = Array.from(html.matchAll(articleBlocksRegex));\n \n // No articles found\n if (!articleBlocks || articleBlocks.length === 0) {\n console.log(\"No article blocks found\");\n return html;\n }\n \n // Function to check if an article is fully undefined\n function isFullyUndefinedArticle(articleHtml) {\n // An article is considered fully undefined if:\n // 1. It has href=\"undefined\"\n // 2. It has link text that is \"undefined\"\n // 3. It has \"undefined | undefined\" (source and date)\n return articleHtml.includes('href=\"undefined\"') && \n articleHtml.includes('>undefined</a>') &&\n articleHtml.includes('undefined | undefined');\n }\n \n // Identify blocks to remove\n const blocksToRemove = [];\n for (const match of articleBlocks) {\n const block = match[0];\n if (isFullyUndefinedArticle(block)) {\n console.log(\"Found undefined article, will remove\");\n blocksToRemove.push(match);\n } else {\n console.log(\"Found valid article, keeping it\");\n }\n }\n \n // If no blocks to remove, return original HTML\n if (blocksToRemove.length === 0) {\n console.log(\"No undefined articles found to remove\");\n return html;\n }\n \n console.log(`Found ${blocksToRemove.length} undefined articles to remove`);\n \n // Create a new string by removing the matches from end to start (to avoid index shifting)\n let cleanedHtml = html;\n for (let i = blocksToRemove.length - 1; i >= 0; i--) {\n const match = blocksToRemove[i];\n cleanedHtml = cleanedHtml.slice(0, match.index) + cleanedHtml.slice(match.index + match[0].length);\n }\n \n return cleanedHtml;\n}\n\n// Get the HTML from the specified parameter\nconst html = $input.first().json.html;\nlet updatedHtml = html;\n\n// Bug checks - check several keywords\ndebugSentiment(\"\u05d7\u05d9\u05d5\u05d1\u05d9\");\ndebugSentiment(\"\u05e9\u05dc\u05d9\u05dc\u05d9\");\ndebugSentiment(\"\u05e0\u05d9\u05d9\u05d8\u05e8\u05dc\u05d9\");\ndebugSentiment(\"\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05d6\u05e7\");\ndebugSentiment(\"\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05d6\u05e7\");\ndebugSentiment(\"\u05d7\u05d9\u05d5\u05d1\u05d9-\u05d7\u05dc\u05e9\");\ndebugSentiment(\"\u05e9\u05dc\u05d9\u05dc\u05d9-\u05d7\u05dc\u05e9\");\n\n// 1. Update colors in the recommendation title\nconst titleMatch = html.match(/<h2 style=\"[^\"]*color: #[a-f0-9]+;[^\"]*\">([^<]+)<\\/h2>/i);\nif (titleMatch) {\n const titleText = titleMatch[1].trim();\n const titleSentiment = getSentimentType(titleText);\n \n // Update title color\n updatedHtml = updatedHtml.replace(\n /(<h2 style=\"[^\"]*color: )#[a-f0-9]+(;[^\"]*\">)/i,\n `$1${colors[titleSentiment].main}$2`\n );\n \n // Update side bar color\n updatedHtml = updatedHtml.replace(\n /(<div style=\"position: absolute; right: 0; top: 0; bottom: 0; width: 6px; background-color: )#[a-f0-9]+(;\"><\\/div>)/i,\n `$1${colors[titleSentiment].main}$2`\n );\n \n // Update gradient color\n updatedHtml = updatedHtml.replace(\n /(<div style=\"position: absolute; right: 0; top: 0; width: 100%; height: 100%; background: linear-gradient\\(90deg, )rgba\\([^)]+\\)( 0%, )rgba\\([^)]+\\)( 50%\\);\"><\\/div>)/i,\n `$1${colors[titleSentiment].gradient}$2${colors[titleSentiment].gradient.replace('0.07', '0')}$3`\n );\n \n // Update icon background color\n updatedHtml = updatedHtml.replace(\n /(<div style=\"display: inline-block; width: 40px; height: 40px; border-radius: 50%; margin-bottom: 10px; background-color: )rgba\\([^)]+\\)(; text-align: center;\">)/i,\n `$1${colors[titleSentiment].background}$2`\n );\n}\n\n// 2. Update overall sentiment color\nconst sentimentMatch = updatedHtml.match(/<span style=\"[^\"]*font-weight: 600; color: #[a-f0-9]+;[^\"]*\">([^<]+)<\\/span>/i);\nif (sentimentMatch) {\n const sentimentText = sentimentMatch[1].trim();\n const sentimentType = getSentimentType(sentimentText);\n \n updatedHtml = updatedHtml.replace(\n /(<span style=\"[^\"]*font-weight: 600; color: )#[a-f0-9]+(;[^\"]*\">)/i,\n `$1${colors[sentimentType].main}$2`\n );\n}\n\n// 3. Update article colors\nconst articleBlocks = updatedHtml.match(/<tr>\\s*<td style=\"padding-bottom: 16px;\">\\s*<table[^>]*>[\\s\\S]*?<\\/table>\\s*<\\/td>\\s*<\\/tr>/g);\nif (articleBlocks) {\n for (const block of articleBlocks) {\n // Check if this is a fully undefined article before skipping\n const isUndefined = block.includes('href=\"undefined\"') && \n block.includes('>undefined</a>') && \n block.includes('undefined | undefined');\n \n // Skip if this is a completely undefined article\n if (isUndefined) {\n console.log(\"Skipping color update for undefined article\");\n continue;\n }\n \n // Find sentiment within the block\n const articleSentimentMatch = block.match(/<div style=\"[^\"]*padding: 3px 10px;[^\"]*\">([^<]+)<\\/div>/i);\n if (articleSentimentMatch) {\n const articleSentimentText = articleSentimentMatch[1].trim();\n const articleSentimentType = getSentimentType(articleSentimentText);\n \n // Debug check - log the identified sentiment\n debugSentiment(articleSentimentText);\n \n // Create updated block\n let updatedBlock = block;\n \n // Update side line color\n updatedBlock = updatedBlock.replace(\n /(<td width=\"4\" style=\"background-color: )#[a-f0-9]+(;\"><\\/td>)/i,\n `$1${colors[articleSentimentType].main}$2`\n );\n \n // Update sentiment tag colors (background and text color)\n updatedBlock = updatedBlock.replace(\n /(<div style=\"[^\"]*background-color: )rgba\\([^)]+\\)(; color: )#[a-f0-9]+(;[^\"]*\">)/i,\n `$1${colors[articleSentimentType].accent}$2${colors[articleSentimentType].main}$3`\n );\n \n // Replace the block with its updated version\n updatedHtml = updatedHtml.replace(block, updatedBlock);\n }\n }\n}\n\n// 4. Update recommendation button color\nconst buttonMatch = updatedHtml.match(/<a style=\"[^\"]*background-color: #[a-f0-9]+;[^\"]*\">([^<]+)<\\/a>/i);\nif (buttonMatch) {\n const buttonText = buttonMatch[1].trim();\n let buttonSentiment = 'neutral'; // Default\n \n // Determine sentiment based on button text\n if (buttonText.includes(\"\u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05e7\u05e0\u05d5\u05ea\")) {\n buttonSentiment = 'positive';\n } else if (buttonText.includes(\"\u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05de\u05db\u05d5\u05e8\")) {\n buttonSentiment = 'negative';\n } else if (buttonText.includes(\"\u05de\u05de\u05dc\u05d9\u05e5 \u05dc\u05d7\u05db\u05d5\u05ea\")) {\n buttonSentiment = 'neutral';\n }\n \n // Update button background color\n updatedHtml = updatedHtml.replace(\n /(<a style=\"[^\"]*background-color: )#[a-f0-9]+(;[^\"]*\">)/i,\n `$1${colors[buttonSentiment].main}$2`\n );\n \n // Update box-shadow color\n const boxShadowRgba = `rgba(${parseInt(colors[buttonSentiment].main.substring(1, 3), 16)}, ${parseInt(colors[buttonSentiment].main.substring(3, 5), 16)}, ${parseInt(colors[buttonSentiment].main.substring(5, 7), 16)}, 0.25)`;\n updatedHtml = updatedHtml.replace(\n /(box-shadow: 0 4px 6px )rgba\\([^)]+\\)(;[^\"]*\">)/i,\n `$1${boxShadowRgba}$2`\n );\n}\n\n// 5. Remove undefined articles\nupdatedHtml = removeUndefinedArticles(updatedHtml);\n\n// 6. Remove topics with only one article\nupdatedHtml = removeSingleArticleTopics(updatedHtml);\n\n// Return updated HTML\nreturn { html: updatedHtml };"
},
"typeVersion": 2
},
{
"id": "d9174ea1-e42b-4533-98ab-9dc8f94055db",
"name": "Think",
"type": "@n8n/n8n-nodes-langchain.toolThink",
"position": [
1680,
140
],
"parameters": {},
"typeVersion": 1
},
{
"id": "ca2820e9-553d-477b-9084-74b2fab92cc9",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
940,
260
],
"parameters": {
"color": 3,
"width": 160,
"height": 80,
"content": "### Replace OpenAI Credentials"
},
"typeVersion": 1
},
{
"id": "2e0013ca-2dda-425c-b6d8-bdd3b3cd262d",
"name": "Generate Variables For API",
"type": "n8n-nodes-base.code",
"position": [
2760,
-20
],
"parameters": {
"jsCode": "// Function to generate yesterday's date in the required format\nfunction getYesterdayDateFormat() {\n // Create a current date object\n const today = new Date();\n \n // Set the date to the previous day (yesterday)\n today.setDate(today.getDate() - 1);\n \n // Reset hours, minutes, seconds and milliseconds to 00:00:00.000\n today.setHours(0, 0, 0, 0);\n \n // Extract components\n const year = today.getFullYear();\n const month = String(today.getMonth() + 1).padStart(2, '0'); // Months in JS start from 0\n const day = String(today.getDate()).padStart(2, '0');\n const hours = String(today.getHours()).padStart(2, '0');\n const minutes = String(today.getMinutes()).padStart(2, '0');\n \n // Build the string in the required format\n return `${year}${month}${day}T${hours}${minutes}`;\n}\n// Calculate the date\nconst yesterdayDate = getYesterdayDateFormat();\n// Return the result in the format required by n8n - array of objects\nreturn [\n {\n json: {\n wanted_date: yesterdayDate\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "a3d8d689-7b9a-4d45-9a9b-ffb9597606a1",
"name": "Set Variables",
"type": "n8n-nodes-base.set",
"position": [
2920,
-20
],
"parameters": {
"values": {
"number": [
{
"name": "wantedDate",
"value": "={{ $json.wanted_date }}"
}
],
"string": [
{
"name": "stockSymbol",
"value": "={{ $('Workflow Input Trigger').item.json.ticker }}"
},
{
"name": "apikey"
}
]
},
"options": {}
},
"typeVersion": 2
},
{
"id": "f4eeb758-ba3e-4fb2-882f-7422bdcdc30b",
"name": "Get News Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
3100,
-20
],
"parameters": {
"url": "=https://www.alphavantage.co/query?function=NEWS_SENTIMENT&tickers={{ $json.stockSymbol }}&sort=RELEVANCE&time_from={{ $json.wantedDate }}&apikey={{ $json.apikey }}",
"options": {}
},
"typeVersion": 4.1
},
{
"id": "87a2eb36-36e2-427d-8db2-2de3a280e404",
"name": "Analyse API Input",
"type": "n8n-nodes-base.code",
"position": [
3280,
-20
],
"parameters": {
"jsCode": "/**\n * Custom code for n8n Code node to analyze news data from Alpha Vantage\n * \n * - The code receives API data from the previous node\n * - Analyzes sentiment, leading articles, and hot topics\n * - Returns a structured JSON object for further processing\n */\n\nconst stockSymbol = $('Set Variables').first().json.stockSymbol\nconst allNews = $input.first().json.feed\n ;\nconst today = new Date().toISOString().split('T')[0]; // Current date in YYYY-MM-DD format\n\n// Filter articles relevant to the stock\nconst relevantArticles = allNews.filter(
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuthopenAiApismtp
About this workflow
Code Schedule. Uses memoryBufferWindow, agent, stickyNote, outputParserStructured. Event-driven trigger; 45 nodes.
Source: https://github.com/Zie619/n8n-workflows — original creator credit. Request a take-down →