{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "07884732-59ef-43c5-b95c-1afacf9406b5",
      "name": "Window Buffer Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        304,
        -480
      ],
      "parameters": {
        "sessionKey": "=335458847",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "8f51d022-061d-4d15-9a3f-6146352027a7",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        496,
        -704
      ],
      "parameters": {
        "text": "=Ticker = {{ $json.body.ticker }}",
        "options": {
          "systemMessage": "=You are an expert AI financial analyst. Your primary role is to act as an orchestrator: you will call specialized tools to gather raw data, and then you will synthesize that data into a complete, well-written, and structured report.\n\n# Instructions\nYour task is to follow this exact procedure step-by-step:\n\n1.  **Identify the Input:** The user will provide a stock symbol (e.g., \"AAPL\").\n\n2.  **Gather Data Using Tools (Mandatory Calls):**\n    You MUST use the provided tools to gather all necessary information. You cannot generate or invent any data yourself. Your ability to complete this task is entirely dependent on the outputs of these tools.\n    *   **First, call the `technical_analysis` tool:**\n        *   Invoke it using the syntax: `technical_analysis(stock_symbol=\"[IDENTIFIED_STOCK_SYMBOL]\")`\n    *   **Next, call the `trends_analysis` tool:**\n        *   Invoke it using the syntax: `trends_analysis(stock_symbol=\"[IDENTIFIED_STOCK_SYMBOL]\")`\n    *   **Finally, call the `fundamental_analysis` tool:**\n        *   Invoke it using the syntax: `fundamental_analysis(stock_symbol=\"[IDENTIFIED_STOCK_SYMBOL]\")`\n\n3.  **Synthesize, Write, and Formulate a Recommendation:**\n    Once you have received the outputs from ALL THREE tools, you must perform the following synthesis and writing tasks:\n    *   **Write the `technicalAnalysis` Text:** Analyze the raw data from the `technical_analysis` tool. Based on this data, write a professional, human-readable paragraph that synthesizes the most important technical indicators (like the trend, RSI, Bollinger Bands), states the resulting bias (e.g., bullish, bearish, neutral), and gives a final actionable takeaway. **This final output must be a single string wrapped in `<p>` tags.**\n    *   **Write the `recommendationText`:** Analyze the combined technical, news sentiment, and fundamental data. Formulate a clear investment recommendation (Buy, Hold, or Sell). Then, write a clear, professional paragraph explaining the \"why\" behind your recommendation, referencing key data points from all three analysis types.\n    *   Your default recommendation should be \"Hold\" unless there is a strong, data-driven signal for \"Buy\" or \"Sell\". Ensure all generated text is professional, clear, and written in English.\n\n4.  **Construct the Final Report:**\n    Finally, compile all the gathered data and your synthesized text into the JSON object specified in the Response Format. Ensure strict adherence to the schema.\n\n## Tools\n- **technical_analysis(stock_symbol: string)**: Retrieves comprehensive technical analysis data and a chart image URL for a given stock ticker.\n- **trends_analysis(stock_symbol: string)**: Fetches news sentiment, relevant articles, and identifies trending topics for a given stock ticker.\n- **fundamental_analysis(stock_symbol: string)**: Fetches fundamental financial data, including valuation, performance, and a summary of financial health.\n\n## Response Format\nYou must respond with a JSON object containing exactly the following keys. Pay close attention to the `technicalAnalysis` and `recommendationText` fields, which you must write yourself based on the tool data.\n\n{\n  \"stockSymbol\": \"STOCK_TICKER\",\n  \"analysisDate\": \"DD/MM/YYYY\",\n  \"recommendationClass\": \"positive/neutral/negative\",\n  \"recommendationTitle\": \"Concise Recommendation Title in English\",\n  \"recommendationText\": \"Detailed explanation of the recommendation in English that you have written.\",\n  \"bullishCount\": 0,\n  \"neutralCount\": 0,\n  \"bearishCount\": 0,\n  \"bullishHeight\": 0,\n  \"neutralHeight\": 0,\n  \"bearishHeight\": 0,\n  \"overallSentiment\": \"Bullish/Neutral/Bearish\",\n  \"Recommendation\": \"Buy/Hold/Sell\",\n  \"sentimentScore\": 0.00,\n  \"chartImageUrl\": \"URL_PLACEHOLDER\",\n  \"technicalAnalysis\": \"<p>Detailed technical analysis that you have written, using p tags for paragraphs.</p>\",\n  \"fundamentalAnalysis\": {\n    \"company_overview\": \"The company description extracted from the tool.\",\n    \"key_metrics\": {\n      \"market_cap\": \"The market cap extracted from the tool.\",\n      \"pe_ratio\": \"The P/E ratio extracted from the tool.\",\n      \"eps\": \"The EPS extracted from the tool.\"\n    },\n    \"financial_summary\": \"The financial summary paragraph from the tool.\"\n  },\n  \"topArticles\": [\n    {\n      \"title\": \"Article title in English\",\n      \"url\": \"URL of the article\",\n      \"source\": \"Source name in English\",\n      \"date\": \"DD/MM/YYYY\",\n      \"sentimentClass\": \"bullish/neutral/bearish\"\n    }\n  ],\n  \"hotTopics\": [\n    {\n      \"topic\": \"Topic name in English\",\n      \"article_count\": 0,\n      \"average_relevance\": \"0.00\"\n    }\n  ]\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "e496b7fa-3f78-47ad-8795-cc99f35c1053",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        944,
        -480
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"stockSymbol\": \"STOCK_TICKER\",\n  \"analysisDate\": \"DD/MM/YYYY\",\n  \"recommendationClass\": \"positive/neutral/negative\",\n  \"recommendationTitle\": \"Concise Recommendation Title in English\",\n  \"recommendationText\": \"Detailed explanation of the recommendation in English.\",\n  \"bullishCount\": 0,\n  \"neutralCount\": 0,\n  \"bearishCount\": 0,\n  \"bullishHeight\": 0,\n  \"neutralHeight\": 0,\n  \"bearishHeight\": 0,\n  \"overallSentiment\": \"Bullish/Neutral/Bearish\",\n  \"Recommendation\": \"Buy/Hold/Sell\",\n  \"sentimentScore\": 0.00,\n  \"chartImageUrl\": \"URL_PLACEHOLDER\",\n  \"technicalAnalysis\": \"<p>Detailed technical analysis in English, using p tags for paragraphs.</p>\",\n  \"topArticles\": [\n    {\n      \"title\": \"Article title in English\",\n      \"url\": \"URL of the article\",\n      \"source\": \"Source name in English\",\n      \"date\": \"DD/MM/YYYY\",\n      \"sentimentClass\": \"bullish/neutral/bearish\"\n    }\n  ],\n  \"hotTopics\": [\n    {\n      \"topic\": \"Topic name in English\",\n      \"article_count\": 0,\n      \"average_relevance\": \"0.00\"\n    }\n  ]\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "5cef1553-8424-4c65-9893-d8f5f3260fa7",
      "name": "Generate HTML",
      "type": "n8n-nodes-base.html",
      "position": [
        1152,
        -608
      ],
      "parameters": {
        "html": "<!DOCTYPE html>\n<html dir=\"ltr\" lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Stock Analysis of {{ $('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: left; direction: ltr;\">\n    <!-- Main Wrapper -->\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: left; direction: ltr;\">\n        \n        <!-- Header -->\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,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC93MzA9cGF0dGVybj48L2RlZnM+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;\">Stock Analysis of {{ $('AI Agent').item.json.output.stockSymbol }}</h1>\n            <div style=\"color: rgba(255,255,255,0.85); font-size: 15px; position: relative;\">Date: {{ $('AI Agent').item.json.output.analysisDate }}</div>\n        </div>\n        \n        <!-- Email Content -->\n        <div style=\"padding: 40px; text-align: left; direction: ltr;\">\n            \n            <!-- Recommendation Box -->\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: left;\">\n                <div style=\"position: absolute; left: 0; top: 0; bottom: 0; width: 6px; background-color: #f7b955;\"></div>\n                <div style=\"position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: linear-gradient(90deg, rgba(247, 185, 85, 0) 50%, rgba(247, 185, 85, 0.07) 100%);\"></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: left;\">{{ $('AI Agent').item.json.output.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            <!-- Technical Analysis -->\n            <div style=\"margin-bottom: 40px; text-align: left;\">\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: left;\">Technical Analysis</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=\"data:image/png;base64,{{ $('AI Agent').item.json.output.chartImageUrl }}\" alt=\"Technical Chart {{ $('AI Agent').item.json.output.stockSymbol }}\" style=\"width: 100%; display: block; max-height: 450px; object-fit: contain;\">\n                </div>\n\n                <!-- Technical Analysis Description -->\n                <!-- Technical Analysis Description -->\n              <div style=\"background-color: #f8fafc; border-radius: 12px; padding: 25px; font-size: 15px; line-height: 1.6; color: #4a5568; text-align: left;\">\n                  {{ $('AI Agent').item.json.output.technicalAnalysis }}\n              </div>\n            </div>\n            \n            <!-- Fundamental Analysis -->\n            <div style=\"margin-bottom: 40px; text-align: left;\">\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: left;\">Fundamental Analysis</h2>\n                \n                <div style=\"background-color: #f8fafc; border-radius: 12px; padding: 25px; font-size: 15px; line-height: 1.6; color: #4a5568; text-align: left;\">\n                    \n                    <h3 style=\"font-size: 16px; color: #1a202c; margin: 0 0 10px 0; font-weight: 600;\">Company Overview</h3>\n                    <p style=\"margin: 0 0 20px 0;\">{{ $('AI Agent').item.json.output.fundamentalAnalysis.company_overview }}</p>\n                    \n                    <h3 style=\"font-size: 16px; color: #1a202c; margin: 0 0 15px 0; font-weight: 600;\">Key Metrics</h3>\n                    <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse; margin-bottom: 20px;\">\n                        <tr>\n                            <td style=\"padding: 8px; border: 1px solid #e2e8f0; background-color: #ffffff; border-radius: 8px 0 0 8px;\"><strong>Market Cap</strong></td>\n                            <td style=\"padding: 8px; border: 1px solid #e2e8f0; text-align: right;\">{{ $('AI Agent').item.json.output.fundamentalAnalysis.key_metrics.market_cap }}</td>\n                        </tr>\n                        <tr>\n                            <td style=\"padding: 8px; border: 1px solid #e2e8f0; background-color: #ffffff;\"><strong>P/E Ratio</strong></td>\n                            <td style=\"padding: 8px; border: 1px solid #e2e8f0; text-align: right;\">{{ $('AI Agent').item.json.output.fundamentalAnalysis.key_metrics.pe_ratio }}</td>\n                        </tr>\n                        <tr>\n                            <td style=\"padding: 8px; border: 1px solid #e2e8f0; background-color: #ffffff; border-radius: 0 0 0 8px;\"><strong>EPS</strong></td>\n                            <td style=\"padding: 8px; border: 1px solid #e2e8f0; text-align: right;\">{{ $('AI Agent').item.json.output.fundamentalAnalysis.key_metrics.eps }}</td>\n                        </tr>\n                    </table>\n                    \n                    <h3 style=\"font-size: 16px; color: #1a202c; margin: 0 0 10px 0; font-weight: 600;\">Financial Summary</h3>\n                    <p style=\"margin: 0;\">{{ $('AI Agent').item.json.output.fundamentalAnalysis.financial_summary }}</p>\n                    \n                </div>\n            </div>\n          \n            <!-- Market Sentiment Analysis -->\n            <div style=\"margin-bottom: 40px; text-align: left;\">\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: left;\">Market Sentiment Analysis</h2>\n                \n                <!-- Sentiment Chart - using table instead of 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;\">Bullish</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;\">Neutral</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;\">Bearish</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                    The overall sentiment for <strong>{{ $('AI Agent').item.json.output.stockSymbol }}</strong> is \n                    <span style=\"font-weight: 600; color: #f7b955;\">{{ $('AI Agent').item.json.output.overallSentiment }}</span> \n                    with a score of <strong>{{ $('AI Agent').item.json.output.sentimentScore }}</strong>\n                </div>\n            </div>\n            \n            <!-- Influential Articles -->\n            <div style=\"margin-bottom: 40px; text-align: left;\">\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: left;\">Influential Articles</h2>\n                \n                <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" style=\"border-collapse: collapse;\">\n                    <!-- Article 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: left;\">\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: left;\">{{ $('AI Agent').item.json.output.topArticles[0].source }} | {{ $('AI Agent').item.json.output.topArticles[0].date }}</td>\n                                                <td style=\"text-align: right;\">\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].sentimentClass }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- Article 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: left;\">\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: left;\">{{ $('AI Agent').item.json.output.topArticles[1].source }} | {{ $('AI Agent').item.json.output.topArticles[1].date }}</td>\n                                                <td style=\"text-align: right;\">\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].sentimentClass }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- Article 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: left;\">\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: left;\">{{ $('AI Agent').item.json.output.topArticles[2].source }} | {{ $('AI Agent').item.json.output.topArticles[2].date }}</td>\n                                                <td style=\"text-align: right;\">\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].sentimentClass }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- Article 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: left;\">\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: left;\">{{ $('AI Agent').item.json.output.topArticles[3].source }} | {{ $('AI Agent').item.json.output.topArticles[3].date }}</td>\n                                                <td style=\"text-align: right;\">\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].sentimentClass }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                    \n                    <!-- Article 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: left;\">\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: left;\">{{ $('AI Agent').item.json.output.topArticles[4].source }} | {{ $('AI Agent').item.json.output.topArticles[4].date }}</td>\n                                                <td style=\"text-align: right;\">\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].sentimentClass }}\n                                                    </div>\n                                                </td>\n                                            </tr>\n                                        </table>\n                                    </td>\n                                </tr>\n                            </table>\n                        </td>\n                    </tr>\n                </table>\n            </div>\n              \n    <!-- Hot Topics - improved mobile version -->\n    <div style=\"margin-bottom: 30px; text-align: left;\">\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: left;\">Hot Topics</h2>\n        \n        <div style=\"background-color: #f8fafc; border-radius: 12px; padding: 20px 25px; text-align: left;\">\n            <p style=\"margin: 0 0 15px 0; font-size: 15px; color: #4a5568; text-align: left;\">The main topics appearing in the news for {{ $('AI Agent').item.json.output.stockSymbol }}:</p>\n            \n            <!-- Topic 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: left; 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: right; 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> articles\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: right; font-size: 12px; color: #718096; margin-top: 4px;\">Relevance: {{ $('AI Agent').item.json.output.hotTopics[0].average_relevance }}</div>\n            </div>\n            \n            <!-- Topic 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: left; 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: right; 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> articles\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: right; font-size: 12px; color: #718096; margin-top: 4px;\">Relevance: {{ $('AI Agent').item.json.output.hotTopics[1].average_relevance }}</div>\n            </div>\n            \n            <!-- Topic 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: left; 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: right; 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> articles\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: right; font-size: 12px; color: #718096; margin-top: 4px;\">Relevance: {{ $('AI Agent').item.json.output.hotTopics[2].average_relevance }}</div>\n            </div>\n            \n            <!-- Topic 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: left; 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: right; 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> articles\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: right; font-size: 12px; color: #718096; margin-top: 4px;\">Relevance: {{ $('AI Agent').item.json.output.hotTopics[3].average_relevance }}</div>\n            </div>\n            \n            <!-- Topic 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: left; 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: right; 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> articles\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: right; font-size: 12px; color: #718096; margin-top: 4px;\">Relevance: {{ $('AI Agent').item.json.output.hotTopics[4].average_relevance }}</div>\n            </div>\n        </div>\n    </div>\n\t        \n        <!-- Footer -->\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;\">This report is automatically generated and does not constitute investment recommendation.</p>\n                <p style=\"margin: 0;\">A licensed investment advisor should be consulted before making investment decisions.</p>\n            </div>\n        </div>\n        \n    </div>\n\n</body>\n</html>"
      },
      "typeVersion": 1.2
    },
    {
      "id": "c166866c-eeba-4318-b416-81b7b3c19b9a",
      "name": "Adjust HTML Colors",
      "type": "n8n-nodes-base.code",
      "position": [
        1376,
        -608
      ],
      "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> articles')) {\n    console.log('No topics with 1 article found');\n    return html;\n  }\n\n  // Find each line that contains the \"Topic\" 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 articles\", find the topic it belongs to\n  for (let i = 0; i < lines.length; i++) {\n    if (lines[i].includes('<strong>1</strong> articles')) {\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('<!-- Topic') ||\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}\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 (based on Alpha Vantage labels or AI recommendation)\nfunction getSentimentType(text) {\n  if (!text) return 'neutral';\n\n  const lowerText = text.toLowerCase();\n\n  // Primary checks for overall categories\n  if (lowerText.includes('very bearish') || lowerText.includes('somewhat-bearish') || lowerText.includes('bearish') || lowerText.includes('negative') || lowerText.includes('sell')) {\n    return 'negative';\n  }\n\n  if (lowerText.includes('very bullish') || lowerText.includes('somewhat-bullish') || lowerText.includes('bullish') || lowerText.includes('positive') || lowerText.includes('buy')) {\n    return 'positive';\n  }\n\n  // Default to neutral for \"neutral\" or \"hold\"\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// Debug checks - check several keywords that might appear\n// These are for testing the `getSentimentType` function during development.\n// They can be removed in a production environment.\ndebugSentiment(\"Bullish\");\ndebugSentiment(\"Bearish\");\ndebugSentiment(\"Neutral\");\ndebugSentiment(\"Very Bullish\");\ndebugSentiment(\"Very Bearish\");\ndebugSentiment(\"Somewhat-Bullish\");\ndebugSentiment(\"Somewhat-Bearish\");\ndebugSentiment(\"Buy\");\ndebugSentiment(\"Sell\");\ndebugSentiment(\"Hold\");\n\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 (from AI's \"Recommendation\" field)\n  if (buttonText.includes(\"Buy\")) {\n    buttonSentiment = 'positive';\n  } else if (buttonText.includes(\"Sell\")) {\n    buttonSentiment = 'negative';\n  } else if (buttonText.includes(\"Hold\")) {\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": "8291dabe-ff78-4d6c-9ea4-8fe24647bab6",
      "name": "Think",
      "type": "@n8n/n8n-nodes-langchain.toolThink",
      "position": [
        432,
        -480
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "dc026f91-6498-4fb9-a0af-b02cc4354c33",
      "name": "Technical Analysis Tool",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        688,
        -480
      ],
      "parameters": {
        "name": "technical_analysis",
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "seaEukGjb7anNr8G",
          "cachedResultName": "technical_analysis"
        },
        "description": "Call this tool to get an analysis of a requested stock. It'll be obligatory to pass ticker.",
        "workflowInputs": {
          "value": {
            "ticker": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('ticker', ``, 'string') }}"
          },
          "schema": [
            {
              "id": "ticker",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ticker",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "chart_style",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "chart_style",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2
    },
    {
      "id": "eb7037fd-f890-4ab9-8682-624ff591b9ec",
      "name": "Trends Analysis Tool",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        560,
        -480
      ],
      "parameters": {
        "name": "trends_analysis",
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "40uBcB6ZllsOv87x",
          "cachedResultUrl": "/workflow/40uBcB6ZllsOv87x",
          "cachedResultName": "trends_analysis"
        },
        "description": "Call this tool to get an analysis of a requested stock. It'll be obligatory to pass ticker.",
        "workflowInputs": {
          "value": {
            "ticker": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('ticker', ``, 'string') }}"
          },
          "schema": [
            {
              "id": "ticker",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ticker",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "chart_style",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "chart_style",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2
    },
    {
      "id": "b0ffca3b-bdfb-45a2-93f8-18740c787777",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        176,
        -480
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b29d5670-2017-4c25-9a13-375cd1c33e15",
      "name": "Buy?",
      "type": "n8n-nodes-base.if",
      "position": [
        1152,
        -800
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "97521dab-6d19-4c65-90b7-46368e29cb9f",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.output.recommendationTitle }}",
              "rightValue": "Buy"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5bde248c-2376-461e-9c5d-af74edd50197",
      "name": "Buy in Alpaca",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1376,
        -800
      ],
      "parameters": {
        "url": "https://paper-api.alpaca.markets/v2/orders",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"symbol\": \"{{ $('AI Agent').item.json.output.stockSymbol }}\",\n  \"notional\": 7000,\n  \"side\": \"buy\",\n  \"type\": \"market\",\n  \"time_in_force\": \"day\"\n}\n",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "APCA-API-KEY-ID"
            },
            {
              "name": "APCA-API-SECRET-KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d8b51acb-5c1a-4c4b-be4a-6226893161e9",
      "name": "Send Stock Analysis",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        1600,
        -608
      ],
      "parameters": {
        "html": "={{ $json.html }}",
        "options": {},
        "subject": "=Review of {{ $('AI Agent').item.json.output.stockSymbol }}: {{ $('AI Agent').item.json.output.analysisDate }}",
        "toEmail": "=PASTE_YOUR_EMAIL_HERE",
        "fromEmail": "=PASTE_YOUR_EMAIL_HERE"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "typeVersion": 2.1
    },
    {
      "id": "d4b7b88a-d743-4a27-ab15-d076108542ba",
      "name": "When Executed by Another Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -48,
        -704
      ],
      "parameters": {
        "inputSource": "passthrough"
      },
      "typeVersion": 1.1
    },
    {
      "id": "91fb04a4-c32b-4fd9-8525-dc79d1a1fa7b",
      "name": "Fundamental Analysis Tool",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        816,
        -480
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "a2cFXeEYGZZpej4f",
          "cachedResultName": "fundamental_analysis"
        },
        "description": "Call this tool to get fundamental financial analysis for a stock, including its financial health, valuation metrics, and a summary. It is obligatory to pass the stock ticker.",
        "workflowInputs": {
          "value": {
            "ticker": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('ticker', ``, 'string') }}"
          },
          "schema": [
            {
              "id": "ticker",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ticker",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ticker"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5a7eb499-14f7-4397-8ea7-648570084573",
      "name": "Get Chart URL",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -176,
        -144
      ],
      "parameters": {
        "url": "https://api.chart-img.com/v2/tradingview/advanced-chart",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "jsonBody": "={\n  \"style\": \"candle\",\n  \"theme\": \"light\",\n  \"interval\": \"1W\",\n  \"symbol\": \"{{ $json.chart_prefix }}{{ $('When Executed by Another Workflow').item.json.ticker }}\",\n  \"override\": {\n    \"showStudyLastValue\": false\n  },\n  \"studies\": [\n    {\n      \"name\": \"Volume\",\n      \"forceOverlay\": true\n    },\n    {\n      \"name\": \"Moving Average Exponential\",\n      \"inputs\": {\n        \"length\": 200\n      }\n    },\n    {\n      \"name\": \"Relative Strength Index\"\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "9ac9d83d-54c0-48cd-82ee-60b81b984c17",
      "name": "Get Price History",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -400,
        112
      ],
      "parameters": {
        "url": "=https://api.twelvedata.com/time_series",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{ $('When Executed by Another Workflow').item.json.ticker }}"
            },
            {
              "name": "interval",
              "value": "1day"
            },
            {
              "name": "outputsize",
              "value": "180"
            },
            {
              "name": "apikey",
              "value": "={{ $('Set Stock Symbol and API Key').item.json.TwelveData_API_Key }}"
            },
            {
              "name": "exchange",
              "value": "={{ $json.exchange }}"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "c17fd85b-3ae8-44ab-900e-c9bd9c7e83dc",
      "name": "Get Bollinger Bands",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -176,
        304
      ],
      "parameters": {
        "url": "=https://api.twelvedata.com/bbands",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{ $('When Executed by Another Workflow').item.json.ticker }}"
            },
            {
              "name": "interval",
              "value": "1day"
            },
            {
              "name": "outputsize",
              "value": "1"
            },
            {
              "name": "apikey",
              "value": "={{ $('Set Stock Symbol and API Key').item.json.TwelveData_API_Key }}"
            },
            {
              "name": "exchange",
              "value": "={{ $json.exchange }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "87fba7ce-acb7-4709-9e47-485e303f0e27",
      "name": "Get MACD",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -176,
        496
      ],
      "parameters": {
        "url": "=https://api.twelvedata.com/macd",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{ $('When Executed by Another Workflow').item.json.ticker }}"
            },
            {
              "name": "interval",
              "value": "1day"
            },
            {
              "name": "outputsize",
              "value": "1"
            },
            {
              "name": "apikey",
              "value": "={{ $('Set Stock Symbol and API Key').item.json.TwelveData_API_Key }}"
            },
            {
              "name": "exchange",
              "value": "={{ $json.exchange }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "f91c80e5-000b-45eb-a019-92d24046ffef",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        48,
        208
      ],
      "parameters": {
        "numberInputs": 3
      },
      "typeVersion": 3.1
    },
    {
      "id": "7399c477-db5e-4b05-96f8-931ba45a2d74",
      "name": "Calculate Support Resistance",
      "type": "n8n-nodes-base.code",
      "position": [
        -176,
        112
      ],
      "parameters": {
        "jsCode": "// Get historical price data\nconst data = $input.item.json;\n\n// Check if data exists\nif (!data.values || data.values.length === 0) {\n  return { json: { error: \"No price data available\", ticker: data.meta?.symbol } };\n}\n\n// Convert prices to numbers\nconst prices = data.values.map(v => parseFloat(v.close)).reverse();\n\n// Function to calculate Fibonacci levels\nfunction calculateFibonacciLevels() {\n  // Find min and max prices\n  const max = Math.max(...prices);\n  const min = Math.min(...prices);\n  const diff = max - min;\n  \n  return {\n    level_0: min.toFixed(2),\n    level_0_236: (min + diff * 0.236).toFixed(2),\n    level_0_382: (min + diff * 0.382).toFixed(2),\n    level_0_5: (min + diff * 0.5).toFixed(2),\n    level_0_618: (min + diff * 0.618).toFixed(2),\n    level_0_786: (min + diff * 0.786).toFixed(2),\n    level_1: max.toFixed(2)\n  };\n}\n// Function to identify support and resistance levels\nfunction findSupportResistanceLevels() {\n  // We need at least 30 data points\n  if (prices.length < 30) {\n    return { support: [], resistance: [] };\n  }\n  \n  const supportLevels = [];\n  const resistanceLevels = [];\n  \n  // Check each point (except edges) if it's a local minimum or maximum\n  const lookback = 5; // how many points to check in each direction\n  \n  for (let i = lookback; i < prices.length - lookback; i++) {\n    // Check for local minimum (support)\n    let isMinimum = true;\n    for (let j = i - lookback; j < i; j++) {\n      if (prices[j] <= prices[i]) {\n        isMinimum = false;\n        break;\n      }\n    }\n    \n    for (let j = i + 1; j <= i + lookback; j++) {\n      if (prices[j] <= prices[i]) {\n        isMinimum = false;\n        break;\n      }\n    }\n    \n    if (isMinimum) {\n      supportLevels.push(prices[i]);\n    }\n    \n    // Check for local maximum (resistance)\n    let isMaximum = true;\n    for (let j = i - lookback; j < i; j++) {\n      if (prices[j] >= prices[i]) {\n        isMaximum = false;\n        break;\n      }\n    }\n    \n    for (let j = i + 1; j <= i + lookback; j++) {\n      if (prices[j] >= prices[i]) {\n        isMaximum = false;\n        break;\n      }\n    }\n    \n    if (isMaximum) {\n      resistanceLevels.push(prices[i]);\n    }\n  }\n  \n  // Sort and remove duplicates\n  const uniqueSupports = [...new Set(supportLevels)];\n  const uniqueResistances = [...new Set(resistanceLevels)];\n  \n  // Return only significant levels (up to 5 of each)\n  return {\n    support: uniqueSupports.sort((a, b) => b - a).slice(0, 5).map(p => p.toFixed(2)),\n    resistance: uniqueResistances.sort((a, b) => a - b).slice(0, 5).map(p => p.toFixed(2))\n  };\n}\n\n// Calculate levels\nconst fibonacciLevels = calculateFibonacciLevels();\nconst supportResistanceLevels = findSupportResistanceLevels();\n\n// Return information with additional stock data\nreturn {\n  json: {\n    ticker: data.meta.symbol,\n    currentPrice: parseFloat(data.values[0].close).toFixed(2),\n    fibonacci: fibonacciLevels,\n    supportResistance: supportResistanceLevels,\n    dataPoints: prices.length\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "d7b48dbd-3c98-44fb-8090-7a5dd591b9f2",
      "name": "Organizing Data",
      "type": "n8n-nodes-base.code",
      "position": [
        272,
        224
      ],
      "parameters": {
        "jsCode": "// Getting data from different sources\n// Checking existence of objects before trying to access them\nconst items = $input.all();\nconst fibData = $input.first().json;\n// Trying to locate bband and MACD data if they exist\nlet bbandsData = null;\nlet macdData = null;\n// Trying to check if there is data in additional items\nif (items.length > 1 && items[1] && items[1].json) {\n  bbandsData = items[1].json;\n}\nif (items.length > 2 && items[2] && items[2].json) {\n  macdData = items[2].json;\n}\n// Creating data structure for the response - ensure all fields exist\nconst result = {\n  ticker: fibData.ticker || \"Unknown\",\n  currentPrice: fibData.currentPrice || \"0\",\n  timestamp: new Date().toISOString(),\n  technicalAnalysis: {\n    fibonacci: fibData.fibonacci || {},\n    supportResistance: fibData.supportResistance || { support: [], resistance: [] },\n    bollingerBands: {},\n    macd: {}\n  }\n};\n// Adding Bollinger Bands data - only if they exist\nif (bbandsData && bbandsData.values && bbandsData.values.length > 0) {\n  const bbands = bbandsData.values[0];\n  result.technicalAnalysis.bollingerBands = {\n    upperBand: parseFloat(bbands.upper_band).toFixed(2),\n    middleBand: parseFloat(bbands.middle_band).toFixed(2),\n    lowerBand: parseFloat(bbands.lower_band).toFixed(2)\n  };\n} else if (bbandsData && bbandsData.status === \"ok\") {\n  // If returning another data format\n  result.technicalAnalysis.bollingerBands = {\n    upperBand: bbandsData.upperBand || bbandsData.upper_band || \"0\",\n    middleBand: bbandsData.middleBand || bbandsData.middle_band || \"0\",\n    lowerBand: bbandsData.lowerBand || bbandsData.lower_band || \"0\"\n  };\n}\n// Adding MACD data - only if they exist\nif (macdData && macdData.values && macdData.values.length > 0) {\n  const macd = macdData.values[0];\n  result.technicalAnalysis.macd = {\n    macd: parseFloat(macd.macd).toFixed(2),\n    signal: parseFloat(macd.signal).toFixed(2),\n    histogram: parseFloat(macd.hist).toFixed(2)\n  };\n} else if (macdData && macdData.status === \"ok\") {\n  // If returning another data format\n  result.technicalAnalysis.macd = {\n    macd: macdData.macd || \"0\",\n    signal: macdData.signal || \"0\",\n    histogram: macdData.histogram || macdData.hist || \"0\"\n  };\n}\n// Creating summary and recommendation\nlet bullishFactors = [];\nlet bearishFactors = [];\n// Analyzing Bollinger Bands - only if data exists\nconst bbands = result.technicalAnalysis.bollingerBands;\nif (bbands.upperBand && bbands.lowerBand) {\n  const currentPrice = parseFloat(result.currentPrice);\n  const upperBand = parseFloat(bbands.upperBand);\n  const lowerBand = parseFloat(bbands.lowerBand);\n  if (!isNaN(currentPrice) && !isNaN(upperBand) && !isNaN(lowerBand)) {\n    if (currentPrice > upperBand) {\n      bearishFactors.push(\"Price above upper Bollinger Band - possible overbought opportunity\");\n    } else if (currentPrice < lowerBand) {\n      bullishFactors.push(\"Price below lower Bollinger Band - possible oversold opportunity\");\n    }\n  }\n}\n// Analyzing MACD - only if data exists\nconst macdInfo = result.technicalAnalysis.macd;\nif (macdInfo.macd && macdInfo.signal) {\n  const macd = parseFloat(macdInfo.macd);\n  const signal = parseFloat(macdInfo.signal);\n  if (!isNaN(macd) && !isNaN(signal)) {\n    if (macd > signal) {\n      bullishFactors.push(\"MACD above signal line - positive indication\");\n    } else {\n      bearishFactors.push(\"MACD below signal line - negative indication\");\n    }\n  }\n}\n// Analyzing support and resistance levels - only if data exists\nconst supportResistance = result.technicalAnalysis.supportResistance;\nif (supportResistance.support && supportResistance.resistance) {\n  const currentPrice = parseFloat(result.currentPrice);\n  if (!isNaN(currentPrice)) {\n    const supports = supportResistance.support.map(s => parseFloat(s)).filter(s => !isNaN(s));\n    const resistances = supportResistance.resistance.map(r => parseFloat(r)).filter(r => !isNaN(r));\n    \n    // Finding the closest support level\n    let closestSupport = null;\n    let minSupportDist = Infinity;\n    for (const support of supports) {\n      if (support < currentPrice) {\n        const dist = currentPrice - support;\n        if (dist < minSupportDist) {\n          minSupportDist = dist;\n          closestSupport = support;\n        }\n      }\n    }\n    \n    // Finding the closest resistance level\n    let closestResistance = null;\n    let minResistanceDist = Infinity;\n    for (const resistance of resistances) {\n      if (resistance > currentPrice) {\n        const dist = resistance - currentPrice;\n        if (dist < minResistanceDist) {\n          minResistanceDist = dist;\n          closestResistance = resistance;\n        }\n      }\n    }\n    \n    // Adding support/resistance analysis\n    if (closestSupport !== null) {\n      const supportPercentage = ((currentPrice - closestSupport) / currentPrice * 100).toFixed(2);\n      if (supportPercentage < 5) {\n        bullishFactors.push(`Price is close to support level (${supportPercentage}%) - possible upward reversal opportunity`);\n      }\n    }\n    \n    if (closestResistance !== null) {\n      const resistancePercentage = ((closestResistance - currentPrice) / currentPrice * 100).toFixed(2);\n      if (resistancePercentage < 5) {\n        bearishFactors.push(`Price is close to resistance level (${resistancePercentage}%) - possible downward reversal opportunity`);\n      }\n    }\n  }\n}\n// Analyzing Fibonacci - only if data exists\nconst fibonacci = result.technicalAnalysis.fibonacci;\nif (fibonacci && Object.keys(fibonacci).length > 0) {\n  const currentPrice = parseFloat(result.currentPrice);\n  if (!isNaN(currentPrice)) {\n    const fibLevels = Object.values(fibonacci).map(level => parseFloat(level)).filter(level => !isNaN(level));\n    fibLevels.sort((a, b) => a - b);\n    \n    // Checking which Fibonacci level the price is at\n    for (let i = 0; i < fibLevels.length - 1; i++) {\n      if (currentPrice >= fibLevels[i] && currentPrice <= fibLevels[i+1]) {\n        // If the price is close to a Fibonacci resistance level\n        if (Math.abs(currentPrice - fibLevels[i+1]) / currentPrice * 100 < 2) {\n          bearishFactors.push(`Price is close to Fibonacci level ${[0, 23.6, 38.2, 50, 61.8, 78.6, 100][Math.min(i+1, 6)]}% - possible resistance`);\n        }\n        // If the price is close to a Fibonacci support level\n        if (Math.abs(currentPrice - fibLevels[i]) / currentPrice * 100 < 2) {\n          bullishFactors.push(`Price is close to Fibonacci level ${[0, 23.6, 38.2, 50, 61.8, 78.6, 100][Math.min(i, 6)]}% - possible support`);\n        }\n        break;\n      }\n    }\n  }\n}\n// Adding general recommendation based on factors\nlet recommendation = \"\";\nif (bullishFactors.length > bearishFactors.length) {\n  recommendation = \"Positive\";\n} else if (bearishFactors.length > bullishFactors.length) {\n  recommendation = \"Negative\";\n} else {\n  recommendation = \"Neutral\";\n}\n// Adding summary to the result\nresult.summary = {\n  recommendation: recommendation,\n  bullishFactors: bullishFactors,\n  bearishFactors: bearishFactors\n};\nreturn { json: result };"
      },
      "typeVersion": 2
    },
    {
      "id": "2133d79e-d3d0-408e-843d-599d8ecf90fb",
      "name": "Merge-2",
      "type": "n8n-nodes-base.merge",
      "position": [
        496,
        16
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.1
    },
    {
      "id": "88f2c69c-443e-4771-8533-fa449ddddfe0",
      "name": "ChatGPT 4o",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        944,
        16
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "anthropic/claude-sonnet-4"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "# Role\nYou are a senior technical analyst who merges visual insights with quantitative indicators.\n\n# Inputs\n1. Visual JSON from Agent 1:\n   {\n     \"ai_agent_visual_analysis\": \"...\"\n   }\n2. Technical-indicator JSON in the format:\n   {\n     \"ticker\": \"...\",\n     \"currentPrice\": \"...\",\n     \"timestamp\": \"...\",\n     \"technicalAnalysis\": {\n       \"fibonacci\": { ... },\n       \"supportResistance\": { ... },\n       \"bollingerBands\": { ... },\n       \"macd\": { ... }\n     },\n     \"summary\": { ... }\n   }\n\n# Expected Sections\nWrite five titled sections exactly in this order:\n\n1. Quick Stats  \n   - Ticker, current price, timestamp  \n   - Overall recommendation from technical JSON, if present\n\n2. Candles and EMA  \n   - Use Agent 1 data: trendDirection, candlestickPatterns, emaRelation, volumeNotes\n\n3. RSI  \n   - Report rsiNumeric and rsiState from Agent 1  \n   - Mention rsiDivergence and its implication\n\n4. Indicator Synthesis  \n   - Fibonacci \u2013 cite closest level above and below price  \n   - Bollinger Bands \u2013 quote upper, middle, lower and note price position  \n   - MACD \u2013 quote macd, signal, histogram, note cross or momentum if numbers are valid  \n   - Support-Resistance \u2013 use technicalAnalysis plus priceZones from Agent 1 to highlight the nearest levels\n\n5. Actionable Takeaway  \n   - One sentence bias (bullish, bearish, neutral)  \n   - Clear next step such as watch for break above X or pullback to Y\n\n# Style Rules\n- Be concise and strictly data driven  \n- Every statement must reference either a value from the inputs or a specific visual observation from Agent 1  \n- No speculation beyond supplied data  \n- End after the Takeaway section \u2013 output nothing else"
            },
            {
              "content": "={{ $json.textPayload }}"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "06138546-ccdb-415d-b071-b77839a73cf4",
      "name": "Set Variable",
      "type": "n8n-nodes-base.set",
      "position": [
        272,
        -144
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fdf7e016-7082-4146-9038-454139023990",
              "name": "ai_agent_visual_analysis",
              "type": "string",
              "value": "={{ $('First Technical Analysis').item.json.choices[0].message.content }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f77ca1a6-80e4-4f05-8944-2d6b882af377",
      "name": "Warp as JSON for GPT",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        16
      ],
      "parameters": {
        "jsCode": "/**\n * INPUT: items[0].json (Original JSON)\n * OUTPUT: { textPayload: \"```json\\n{ ... }\\n```\" }\n */\n\nconst pretty = JSON.stringify(items[0].json, null, 2);\nconst wrapped = `\\`\\`\\`json\\n${pretty}\\n\\`\\`\\``;\n\nreturn [\n  {\n    json: {\n      textPayload: wrapped\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "ae7ea355-8675-4d1d-9abc-2af309e4f968",
      "name": "Set Final Response",
      "type": "n8n-nodes-base.set",
      "position": [
        1296,
        16
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fdf7e016-7082-4146-9038-454139023990",
              "name": "response",
              "type": "string",
              "value": "={{ $json.message.content }}"
            },
            {
              "id": "4e5afd49-67c2-40ab-bc8c-565dea3850ed",
              "name": "image",
              "type": "string",
              "value": "={{ $('Get Chart URL').item.binary.data.toBase64() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "139e96a0-a984-4d55-a74f-61ef0f9da5d0",
      "name": "Set Stock Symbol and API Key",
      "type": "n8n-nodes-base.set",
      "position": [
        -1968,
        320
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cf5f7210-5b54-4f4a-abf7-87873be82df4",
              "name": "ticker",
              "type": "string",
              "value": "={{ $json.ticker }}"
            },
            {
              "id": "9f008c4b-60e2-4d99-a119-b0170ec28358",
              "name": "TwelveData_API_Key",
              "type": "string",
              "value": "PASTE_YOUR_TWELVEDATA_API_KEY_HERE"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "94f77044-5eaa-4cc3-8f80-33bab2a4f265",
      "name": "First Technical Analysis",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        48,
        -144
      ],
      "parameters": {
        "text": "=# Role\nYou are a visual chart analyst.  \nYour only input is a weekly candlestick chart image that shows:\n- Price candles and volume bars  \n- One short-term EMA line  \n- An RSI panel with its live value  \n\n# Task\nInspect the image and produce a **structured JSON** object with the following keys:\n\n{\n  \"rsiNumeric\": number,          // exact RSI value from the chart\n  \"rsiState\": \"overbought\" | \"oversold\" | \"neutral\",\n  \"rsiDivergence\": \"bullish\" | \"bearish\" | \"none\",\n  \"trendDirection\": \"up\" | \"down\" | \"sideways\",\n  \"candlestickPatterns\": [ \"pattern1\", \"pattern2\", ... ],   // max 3\n  \"emaRelation\": \"aboveEMA\" | \"belowEMA\" | \"testingEMA\",\n  \"volumeNotes\": \"string\",        // brief comment on recent volume behavior\n  \"priceZones\": {                 // visually inferred areas\n      \"potentialSupport\": [number, ...],  // up to 2 levels taken from visible lows\n      \"potentialResistance\": [number, ...]// up to 2 levels taken from visible highs\n  }\n}\n\n# Style Rules\n- Derive every value only from what is visible in the chart\n- Do not mention any external data or speculation\n- Return the JSON object only, nothing else\n",
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "mistralai/mistral-small-3.2-24b-instruct:free"
        },
        "options": {
          "detail": "auto"
        },
        "resource": "image",
        "simplify": false,
        "inputType": "base64",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "fc2f67a1-a9d3-4d82-bf25-e3204dff2fc6",
      "name": "Test NASDAQ",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1744,
        320
      ],
      "parameters": {
        "url": "=https://api.twelvedata.com/price?symbol={{ $json.ticker }}&exchange=NASDAQ&apikey={{ $json.TwelveData_API_Key }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "c876d10e-f134-443b-b08b-5937dd6f2e63",
      "name": "Set exchange to NASDAQ",
      "type": "n8n-nodes-base.set",
      "position": [
        -848,
        112
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a8064bb3-f0f5-41d9-9b1c-a290d78df5cf",
              "name": "exchange",
              "type": "string",
              "value": "NASDAQ"
            },
            {
              "id": "7fe282b6-ffe4-485c-b8f9-71c658d37f1c",
              "name": "chart_prefix",
              "type": "string",
              "value": "=NASDAQ:"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "908fc3d9-8c39-4c8e-8e7c-1666381dff0e",
      "name": "Test NYSE",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1296,
        416
      ],
      "parameters": {
        "url": "=https://api.twelvedata.com/price?symbol={{ $('When Executed by Another Workflow').item.json.ticker }}&exchange=NYSE&apikey={{ $('Set Stock Symbol and API Key').item.json.TwelveData_API_Key }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "da0cd2ba-e608-44e3-9903-025299ee0523",
      "name": "Set exchange to NYSE",
      "type": "n8n-nodes-base.set",
      "position": [
        -848,
        304
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a8064bb3-f0f5-41d9-9b1c-a290d78df5cf",
              "name": "exchange",
              "type": "string",
              "value": "NYSE"
            },
            {
              "id": "7fe282b6-ffe4-485c-b8f9-71c658d37f1c",
              "name": "chart_prefix",
              "type": "string",
              "value": "=NYSE:"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b6ea57ba-26bc-43a1-8ef2-4a02c3ad8f72",
      "name": "Stop and Error",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        -848,
        496
      ],
      "parameters": {
        "errorMessage": "Stock doesn't exist"
      },
      "typeVersion": 1
    },
    {
      "id": "886fd8c6-f292-4314-b253-c16686fd49ad",
      "name": "Merge1",
      "type": "n8n-nodes-base.merge",
      "position": [
        -624,
        224
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "75937778-8413-4740-931f-514e1c5317c6",
      "name": "is on nasdaq?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1520,
        320
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "fe8433f4-cdb8-401a-9ad2-fbf725e21b5e",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('Test NASDAQ').item.json.price }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "59cf52f1-bb5b-44fc-ade2-01fefe22b4fa",
      "name": "is on NYSE?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1072,
        416
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e445151e-eab3-44a6-ac63-a21576cf2f78",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $('Test NYSE').item.json.price }}",
              "rightValue": "="
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7e49c39a-7ddb-467f-b736-bfbabda4a616",
      "name": "Set Variables",
      "type": "n8n-nodes-base.set",
      "position": [
        -1952,
        1088
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "ticker",
              "value": "={{ $('When Executed by Another Workflow').item.json.ticker }}"
            },
            {
              "name": "apikey",
              "value": "PASTE_YOUR_ALPHA_VANTAGE_KEY_HERE"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "c5d3334f-c9d2-4fb5-b192-212d91268c09",
      "name": "Get Company Overview",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1728,
        1088
      ],
      "parameters": {
        "url": "=https://www.alphavantage.co/query?function=OVERVIEW&symbol={{ $json.ticker }}&apikey={{ $json.apikey }}",
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "a624a584-d560-4f99-b8b5-9e69cff53021",
      "name": "Get Income Statement",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1280,
        1008
      ],
      "parameters": {
        "url": "=https://www.alphavantage.co/query?function=INCOME_STATEMENT&symbol={{ $('Set Variables').item.json.ticker }}&apikey={{ $('Set Variables').item.json.apikey }}",
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "f21b5ba6-34d8-4cff-941e-1d56f695e49d",
      "name": "Get Balance Sheet",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -832,
        1088
      ],
      "parameters": {
        "url": "=https://www.alphavantage.co/query?function=BALANCE_SHEET&symbol={{ $('Set Variables').item.json.ticker }}&apikey={{ $('Set Variables').item.json.apikey }}",
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "f094f62e-0208-402b-ad48-de09e6ef2300",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        -32,
        1088
      ],
      "parameters": {
        "jsCode": "// Get the raw AI response\nlet aiResponseString = $input.first().json.message.content;\n\n// Remove Markdown code block wrappers if present\naiResponseString = aiResponseString\n  .replace(/^```json\\s*/i, '') // remove opening ```json\n  .replace(/^```\\s*/i, '')     // remove opening ``` if no 'json'\n  .replace(/```$/i, '')        // remove closing ```\n  .trim();\n\n// Try to parse\nlet finalJson;\ntry {\n  finalJson = JSON.parse(aiResponseString);\n} catch (error) {\n  throw new Error(\"Failed to parse JSON. AI output was:\\n\" + aiResponseString);\n}\n\n// Return clean JSON\nreturn finalJson;"
      },
      "typeVersion": 2
    },
    {
      "id": "5966e79b-4f15-4bd9-b924-4c68a852edee",
      "name": "Merge2",
      "type": "n8n-nodes-base.merge",
      "position": [
        -608,
        1072
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition",
        "numberInputs": 3
      },
      "typeVersion": 3.2
    },
    {
      "id": "a4edb282-0ed2-45f6-a021-e3d2152b434a",
      "name": "ChatGPT 4o1",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -384,
        1088
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "id",
          "value": "z-ai/glm-4.5-air:free"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are a precise AI financial analyst. Your task is to analyze a JSON object containing raw financial data from Alpha Vantage for a single company and convert it into a structured summary.\n\nYou will receive data from the OVERVIEW, INCOME_STATEMENT, and BALANCE_SHEET endpoints.\n\nFollow these instructions exactly:\n1.  From the OVERVIEW data, extract the company `Description`.\n2.  From the OVERVIEW data, extract the following `key_metrics`: `MarketCapitalization`, `PERatio`, `EPS`.\n3.  From the INCOME_STATEMENT data (`annualReports`), find the `totalRevenue` and `netIncome` for the two most recent years.\n4.  From the BALANCE_SHEET data (`annualReports`), find the `totalAssets` and `totalLiabilities` for the most recent year. Calculate the Debt-to-Equity ratio as (totalLiabilities / (totalAssets - totalLiabilities)).\n5.  Write a brief, data-driven `financial_summary` paragraph (3-4 sentences). State whether revenue and income are growing or shrinking, and comment on the company's valuation (using P/E) and financial stability (using the debt ratio). Remain objective and stick to the data provided.\n\nYour final output MUST be a single, clean JSON object with the following structure. Do not include any text outside of this JSON structure.\n\n{\n  \"company_overview\": \"The extracted company description.\",\n  \"key_metrics\": {\n    \"market_cap\": \"...\",\n    \"pe_ratio\": \"...\",\n    \"eps\": \"...\"\n  },\n  \"recent_performance\": {\n    \"latest_revenue\": \"...\",\n    \"previous_revenue\": \"...\",\n    \"latest_net_income\": \"...\",\n    \"previous_net_income\": \"...\"\n  },\n  \"financial_health\": {\n    \"debt_to_equity_ratio\": \"...\"\n  },\n  \"financial_summary\": \"Your generated summary paragraph.\"\n}"
            },
            {
              "content": "=={{ JSON.stringify($input.item.json) }}"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "4b579344-2d17-4883-a59a-00e47e6bd274",
      "name": "Generate Variables For API",
      "type": "n8n-nodes-base.code",
      "position": [
        320,
        960
      ],
      "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": "f25d0d72-aa62-4640-9e8e-ab6da383cecc",
      "name": "Get News Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        688,
        1008
      ],
      "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": "821b3d6f-03a8-42d1-8705-f57a9f147027",
      "name": "Analyse API Input",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        1008
      ],
      "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 Variables1').first().json.stockSymbol\nconst allNews = $input.first().json.feed;\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(article => {\n  return article.ticker_sentiment &&\n    article.ticker_sentiment.some(ticker =>     ticker.ticker.toUpperCase().includes(stockSymbol.toUpperCase())\n    );\n});\n\n// Sentiment analysis\nlet sentimentCounts = {\n  \"Bullish\": 0,\n  \"Somewhat-Bullish\": 0,\n  \"Neutral\": 0,\n  \"Somewhat-Bearish\": 0,\n  \"Bearish\": 0\n};\n\nlet totalSentimentScore = 0;\nlet totalRelevanceScore = 0;\n\nrelevantArticles.forEach(article => {\n  const stockTicker = article.ticker_sentiment.find(ticker => ticker.ticker === stockSymbol);\n  if (stockTicker) {\n    sentimentCounts[stockTicker.ticker_sentiment_label]++;\n    totalSentimentScore += parseFloat(stockTicker.ticker_sentiment_score) * parseFloat(stockTicker.relevance_score);\n    totalRelevanceScore += parseFloat(stockTicker.relevance_score);\n  }\n});\n\nconst avgSentimentScore = totalRelevanceScore > 0 ? totalSentimentScore / totalRelevanceScore : 0;\n\n// Determining overall sentiment\nlet overallSentiment;\nif (avgSentimentScore >= 0.35) {\n  overallSentiment = \"Very Bullish\";\n} else if (avgSentimentScore >= 0.15) {\n  overallSentiment = \"Bullish\";\n} else if (avgSentimentScore > -0.15) {\n  overallSentiment = \"Neutral\";\n} else if (avgSentimentScore > -0.35) {\n  overallSentiment = \"Bearish\";\n} else {\n  overallSentiment = \"Very Bearish\";\n}\n\n// Most influential articles\nconst topArticles = relevantArticles\n  .map(article => {\n    const stockTicker = article.ticker_sentiment.find(ticker => ticker.ticker === stockSymbol);\n    return {\n      title: article.title,\n      url: article.url,\n      source: article.source,\n      date: formatDate(article.time_published),\n      sentiment_label: stockTicker ? stockTicker.ticker_sentiment_label : \"N/A\",\n      sentiment_score: stockTicker ? parseFloat(stockTicker.ticker_sentiment_score) : 0,\n      relevance_score: stockTicker ? parseFloat(stockTicker.relevance_score) : 0,\n      impact_score: stockTicker ? Math.abs(parseFloat(stockTicker.ticker_sentiment_score) * parseFloat(stockTicker.relevance_score)) : 0\n    };\n  })\n  .sort((a, b) => b.impact_score - a.impact_score)\n  .slice(0, 5);\n\n// Analysis of main topics\nconst topicsMap = {};\n\nrelevantArticles.forEach(article => {\n  if (article.topics) {\n    article.topics.forEach(topic => {\n      if (!topicsMap[topic.topic]) {\n        topicsMap[topic.topic] = {\n          count: 0,\n          relevance: 0\n        };\n      }\n      topicsMap[topic.topic].count++;\n      topicsMap[topic.topic].relevance += parseFloat(topic.relevance_score);\n    });\n  }\n});\n\nconst hotTopics = Object.entries(topicsMap)\n  .map(([topic, data]) => ({\n    topic,\n    article_count: data.count,\n    average_relevance: (data.relevance / data.count).toFixed(2)\n  }))\n  .sort((a, b) => b.article_count - a.article_count)\n  .slice(0, 5);\n\n// Creating result object\nconst analysisResult = {\n  stock_symbol: stockSymbol,\n  analysis_date: today,\n  sentiment_analysis: {\n    overall_sentiment: overallSentiment,\n    sentiment_score: parseFloat(avgSentimentScore.toFixed(4)),\n    sentiment_distribution: sentimentCounts\n  },\n  top_articles: topArticles.map(article => ({\n    title: article.title,\n    source: article.source,\n    url: article.url,\n    date: article.date,\n    sentiment: article.sentiment_label,\n    impact_score: article.impact_score.toFixed(4)\n  })),\n  hot_topics: hotTopics,\n  recent_trends: {\n    description: getTrendDescription(overallSentiment, hotTopics),\n    market_outlook: getMarketOutlook(overallSentiment)\n  }\n};\n\n// Helper functions\nfunction formatDate(dateStr) {\n  if (!dateStr) return \"N/A\";\n  \n  try {\n    // Format: 20250418T152049 -> 2025-04-18\n    const year = dateStr.substring(0, 4);\n    const month = dateStr.substring(4, 6);\n    const day = dateStr.substring(6, 8);\n    return `${year}-${month}-${day}`;\n  } catch (e) {\n    return dateStr;\n  }\n}\n\nfunction getTrendDescription(sentiment, topics) {\n  let description = \"\";\n  \n  if (sentiment === \"Very Bullish\" || sentiment === \"Bullish\") {\n    description = \"The sentiment is leaning positive, with investors primarily focused on \";\n  } else if (sentiment === \"Very Bearish\" || sentiment === \"Bearish\") {\n    description = \"The sentiment is leaning negative, with primary concerns centered around \";\n  } else {\n    description = \"The sentiment is mixed, with a focus on \";\n  }\n  \n  if (topics.length > 0) {\n    const topThreeTopics = topics.slice(0, Math.min(3, topics.length));\n    description += topThreeTopics.map(t => t.topic).join(\", \");\n  } else {\n    description += \"a variety of topics\";\n  }\n  \n  return description + \".\";\n}\n\nfunction getMarketOutlook(sentiment) {\n  if (sentiment === \"Very Bullish\") {\n    return \"Market outlook is very positive. The general sentiment indicates high investor confidence and potential for short-term gains.\";\n  } else if (sentiment === \"Bullish\") {\n    return \"Market outlook is positive. There are indications of cautious optimism among investors.\";\n  } else if (sentiment === \"Neutral\") {\n    return \"Market outlook is mixed. There are balancing forces of optimism and pessimism in the market.\";\n  } else if (sentiment === \"Bearish\") {\n    return \"Market outlook is negative. There are concerns among investors that may impact the stock in the short term.\";\n  } else {\n    return \"Market outlook is very negative. A significant sense of caution and a tendency towards selling is present.\";\n  }\n}\n\n// Return the object for further flow in n8n\nreturn {\n  json: analysisResult\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "4060c865-4e1f-4fa4-be9f-d7e1281fa647",
      "name": "Set Variables1",
      "type": "n8n-nodes-base.set",
      "position": [
        496,
        960
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "wantedDate",
              "value": "={{ $json.wanted_date }}"
            }
          ],
          "string": [
            {
              "name": "stockSymbol",
              "value": "={{ $('When Executed by Another Workflow').item.json.ticker }}"
            },
            {
              "name": "apikey",
              "value": "PASTE_YOUR_ALPHA_VANTAGE_KEY_HERE"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "0b1d0141-9abc-4060-9dcc-c5885b1e8b7b",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1968,
        -736
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 6,
              "triggerAtMinute": 20
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e03306d1-ef91-4c8d-8410-9c250391fc5f",
      "name": "Stock List",
      "type": "n8n-nodes-base.set",
      "position": [
        -1744,
        -736
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "86ee7564-be7e-41c2-94bc-8d01b914d20b",
              "name": "tickers",
              "type": "array",
              "value": "[\"LLY\", \"UNH\", \"JPM\", \"V\", \"XOM\", \"HD\", \"CAT\", \"AAPL\", \"MSFT\", \"NVDA\", \"AMZN\", \"GOOGL\", \"META\", \"AVGO\", \"COST\"]"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "eb5c1c24-70da-417d-a632-e6c67135104b",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        -400,
        -736
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 1.5
      },
      "typeVersion": 1.1
    },
    {
      "id": "09e8eb01-36ce-489c-8bcd-1591339ce395",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -1520,
        -736
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "tickers"
      },
      "typeVersion": 1
    },
    {
      "id": "8a1c9441-dfac-4be8-aa23-d70c405c0194",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -1296,
        -736
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "9691c2e1-09a4-4397-a3c3-32354f42838d",
      "name": "Set Ticker for AI Agent",
      "type": "n8n-nodes-base.set",
      "position": [
        -1072,
        -816
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "453f5281-4c13-4c04-b032-6b7efb9fa7b4",
              "name": "body.ticker",
              "type": "string",
              "value": "={{ $json.tickers }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1a1a30e3-37d3-4811-b7ba-e32bcd02c7c0",
      "name": "On form submission",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -1296,
        -464
      ],
      "parameters": {
        "options": {
          "appendAttribution": true,
          "respondWithOptions": {
            "values": {
              "formSubmittedText": "Success! Check your inbox (or spam folder) for your analysis report."
            }
          }
        },
        "formTitle": "Manual Entry: Stock Analysis",
        "formFields": {
          "values": [
            {
              "fieldLabel": "tickers",
              "placeholder": "TSLA",
              "requiredField": true
            }
          ]
        },
        "responseMode": "lastNode",
        "formDescription": "Please enter the company\u2019s NASDAQ ticker symbol (e.g. AAPL) to get a weekly email with combined technical-and-news sentiment analysis from our AI agent"
      },
      "typeVersion": 2.2
    },
    {
      "id": "79afd6c0-8700-4a07-963d-14d3d632e547",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "onError": "continueRegularOutput",
      "position": [
        -624,
        -816
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "00f3eb80-2033-4bfc-948a-a7d02b8c66a8",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $('Stock List').item.json.tickers }}",
              "rightValue": "={{ $('Set Ticker for AI Agent').item.json.body.ticker }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "0cfca228-18c8-4672-aed7-c8eef1ed79b1",
      "name": "Execute Workflow",
      "type": "n8n-nodes-base.executeWorkflow",
      "position": [
        -848,
        -816
      ],
      "parameters": {
        "options": {
          "waitForSubWorkflow": true
        },
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "=yCTFl7hB5iXoEHqo"
        },
        "workflowInputs": {
          "value": {},
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9626d175-aecb-4a56-b4be-0456bfd16ed7",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2048,
        -944
      ],
      "parameters": {
        "color": 5,
        "width": 1888,
        "height": 704,
        "content": "## Triggers \nThis workflow starts the analysis either on a weekly schedule for a predefined list of stocks or when you manually submit a single stock ticker through a form."
      },
      "typeVersion": 1
    },
    {
      "id": "ee35df8f-18db-4669-9ee3-23df7de62150",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -944
      ],
      "parameters": {
        "color": 4,
        "width": 1984,
        "height": 704,
        "content": "## Stock Analysis (Main AI Agent) \nThis is the core AI agent that orchestrates the entire process, calling the other analysis tools to gather data and then synthesizing it all into a final, structured report."
      },
      "typeVersion": 1
    },
    {
      "id": "508e7c21-f969-4df9-8b20-ab1b6e5accc7",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2032,
        800
      ],
      "parameters": {
        "color": 6,
        "width": 2224,
        "height": 592,
        "content": "## Fundamental Analysis \nThis sub-workflow fetches and summarizes key financial health metrics, including income statements, balance sheets, and company overview data from Alpha Vantage."
      },
      "typeVersion": 1
    },
    {
      "id": "dcbe0d2b-56d2-4be6-921e-d720700494f4",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2032,
        -224
      ],
      "parameters": {
        "color": 6,
        "width": 3584,
        "height": 992,
        "content": "## Technical Analysis \nThis sub-workflow gathers technical indicator data (like MACD and Bollinger Bands) from Twelve Data and generates a stock chart image using Chart-Img."
      },
      "typeVersion": 1
    },
    {
      "id": "f322286d-d9b7-4776-90ec-2c43e9e0657c",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        256,
        800
      ],
      "parameters": {
        "color": 6,
        "width": 976,
        "height": 416,
        "content": "## Trends Analysis \nThis sub-workflow retrieves recent news articles from Alpha Vantage and uses a code node to analyze them, determining the overall market sentiment for the stock.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fd50e9e7-c5b7-4810-a843-270b89f41232",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3248,
        -272
      ],
      "parameters": {
        "color": 7,
        "width": 1040,
        "height": 832,
        "content": "# Workflow Description and Setup Guide\n\n### This template deploys a multi-agent system that fully automates advanced stock analysis. It uses a central AI orchestrator to call specialized sub-workflows, synthesizing technical, fundamental, and news sentiment data into a single, actionable report delivered to your inbox. **All API Keys can be acquired for free!**\n\n**How it Works**\n\n*   **Orchestration:** An AI Agent acts as the central \"brain,\" managing the analysis pipeline. It's triggered either on a schedule for a list of stocks or manually for a single ticker.\n*   **Specialist Agents (Tools):** The orchestrator invokes three sub-workflows as tools to gather intelligence:\n    1.  **Technical Analysis:** Fetches TA data (RSI, MACD, Bollinger Bands) and uses a vision-AI to analyze the stock's chart, identifying trends and patterns.\n    2.  **Fundamental Analysis:** Retrieves financial statements (Income, Balance Sheet) to generate a summary of the company's financial health and valuation.\n    3.  **News Sentiment Analysis:** Aggregates and analyzes market news to determine overall sentiment and identify key discussion topics.\n*   **Synthesis & Delivery:** The main agent synthesizes all findings to formulate a final recommendation (Buy, Hold, Sell), generates a professional HTML report, and delivers it via email.\n\n**Setup (Est. 10-15 mins)**\n\nThis is an advanced, multi-workflow template. Please follow the steps precisely.\n\n1.  **Split Modules:** Separate the 5 color-coded modules on this canvas into 5 new, individual workflows.\n2.  **Acquire APIs:** Acquire the 7 APIs needed for this workflow. These need Credentials: Gemini,, OpenRouter, SMTP, Alpaca. These just need to be pasted in: AlphaVantage, TwelveData, Chart-Img.\n3.  **Configure & Link:** Insert your API keys and email, then re-link the `Execute Workflow` and `Tool` nodes to connect your new workflows.\n4.  **Activate:** Toggle all 5 workflows to \"Active.\"\n\n\u27a1\ufe0f **For the complete, detailed guide, visit: https://docs.google.com/document/d/1Ri_GfuIlc0QVm1aDrWJjRCg_2vWRCrw_SjRV9cBoKmw/edit?usp=sharing**\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e6f89009-8597-4962-b3b7-b5629e0f2ea2",
      "name": "Wait1",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1504,
        1008
      ],
      "parameters": {
        "amount": 2.5
      },
      "typeVersion": 1.1
    },
    {
      "id": "9cd1279e-be4a-4bb9-bf02-ed98e8838a60",
      "name": "Wait2",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1056,
        1088
      ],
      "parameters": {
        "amount": 2.5
      },
      "typeVersion": 1.1
    },
    {
      "id": "1d04650e-e7b0-4863-97a0-3dd2740be8da",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2016,
        -832
      ],
      "parameters": {
        "width": 416,
        "height": 256,
        "content": "### Scheduled Batch Trigger \nRuns a predefined list of stocks weekly. List of stocks can be changed"
      },
      "typeVersion": 1
    },
    {
      "id": "eedab314-aceb-4cea-8ca9-034c2e32569c",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1392,
        -544
      ],
      "parameters": {
        "width": 304,
        "height": 224,
        "content": "### Manual Single Trigger\nRun analysis for one stock instantly via a form."
      },
      "typeVersion": 1
    },
    {
      "id": "1b634628-5b2c-471d-a8d4-5e8b20a960c8",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        -864
      ],
      "parameters": {
        "width": 416,
        "height": 224,
        "content": "### Optional: Complete paper trade in Alpaca"
      },
      "typeVersion": 1
    },
    {
      "id": "44bd4ee3-24c4-4cef-8d12-382eed6be9f5",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1104,
        -624
      ],
      "parameters": {
        "width": 656,
        "height": 208,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n### Generate report and send to email"
      },
      "typeVersion": 1
    },
    {
      "id": "d5140977-409b-42be-b758-47653a8d4665",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1536,
        -448
      ],
      "parameters": {
        "color": 3,
        "height": 80,
        "content": "### Add SMTP Credentials and add your email"
      },
      "typeVersion": 1
    },
    {
      "id": "e0a90013-d3b2-40ef-a91d-5d16c7574fb9",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        -512
      ],
      "parameters": {
        "width": 400,
        "height": 224,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n### These 3 tools are 3 different sub-workflows, and each must be linked back to this"
      },
      "typeVersion": 1
    },
    {
      "id": "313f4c86-88b2-4a61-9f80-bdc9719bdb58",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2000,
        64
      ],
      "parameters": {
        "width": 1504,
        "height": 592,
        "content": "### Exchange & Symbol Validation \nThis part checks if the ticker exists on NASDAQ or NYSE before proceeding"
      },
      "typeVersion": 1
    },
    {
      "id": "04858fb5-dad2-47f1-b515-376fdd8d34f9",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -448,
        32
      ],
      "parameters": {
        "width": 464,
        "height": 624,
        "content": "### Fetch Technical Indicator Data \nThis part gathers raw TA metrics from the Twelve Data API."
      },
      "typeVersion": 1
    },
    {
      "id": "08e54ad9-a9dc-40fb-ac0a-e9e1c668d2bb",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -208
      ],
      "parameters": {
        "width": 688,
        "height": 224,
        "content": "### Visual Chart Analysis\nThis part pulls a chart from the Chart-IMG API and uses a vision-capable AI model to analyze it"
      },
      "typeVersion": 1
    },
    {
      "id": "47e2f3db-2eb9-4a07-ae15-8c2be5df0968",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        -64
      ],
      "parameters": {
        "width": 832,
        "height": 208,
        "content": "### Synthesize and Format Report \nMerges all TA data and uses an AI analyst to write the final summary"
      },
      "typeVersion": 1
    },
    {
      "id": "39837dfb-b0a1-4a13-8625-b69975b558ea",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1776,
        944
      ],
      "parameters": {
        "width": 1312,
        "height": 320,
        "content": "### Fetch Financial Statements\nRetrieves Overview, Income, and Balance Sheet data from Alpha Vantage, with built-in waits to respect API rate limits.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "a632acfb-b6cf-4e02-aaec-87cf867e073c",
      "name": "Sticky Note17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        1024
      ],
      "parameters": {
        "width": 528,
        "height": 224,
        "content": "### AI Analyzes data and response is finalized"
      },
      "typeVersion": 1
    },
    {
      "id": "1beda621-1803-4e13-afaa-df07ddb97fed",
      "name": "Sticky Note18",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2000,
        1232
      ],
      "parameters": {
        "color": 3,
        "width": 192,
        "height": 80,
        "content": "### Add AlphaVantage API Key"
      },
      "typeVersion": 1
    },
    {
      "id": "30b23edc-65df-4796-9846-4eb1771f7e40",
      "name": "Sticky Note19",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        1104
      ],
      "parameters": {
        "color": 3,
        "width": 192,
        "height": 80,
        "content": "### Add AlphaVantage API Key"
      },
      "typeVersion": 1
    },
    {
      "id": "de4bd4ba-7010-4bd9-b246-8b951fa42e61",
      "name": "Sticky Note20",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -336
      ],
      "parameters": {
        "color": 3,
        "width": 150,
        "height": 80,
        "content": "### Add Gemini Credential"
      },
      "typeVersion": 1
    },
    {
      "id": "db3a80d5-af48-4b01-a729-bf6df639dc54",
      "name": "Sticky Note21",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2016,
        512
      ],
      "parameters": {
        "color": 3,
        "width": 192,
        "height": 80,
        "content": "### Add TwelveData API Key"
      },
      "typeVersion": 1
    },
    {
      "id": "d2b6c5d0-3720-497e-8921-8fb1fe6941ef",
      "name": "Sticky Note22",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        16
      ],
      "parameters": {
        "color": 3,
        "width": 150,
        "height": 80,
        "content": "### Add OpenRouter Credential"
      },
      "typeVersion": 1
    },
    {
      "id": "e73c800f-5bd5-4806-997f-0a0727035776",
      "name": "Sticky Note23",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        144
      ],
      "parameters": {
        "color": 3,
        "width": 182,
        "height": 80,
        "content": "### Add OpenRouter Credential"
      },
      "typeVersion": 1
    },
    {
      "id": "1c2819e0-8d15-4824-88f4-584435ece402",
      "name": "Sticky Note24",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -352,
        1216
      ],
      "parameters": {
        "color": 3,
        "width": 150,
        "height": 80,
        "content": "### Add OpenRouter Credential"
      },
      "typeVersion": 1
    },
    {
      "id": "e9e663ed-c30d-4a35-8d01-199defde3b31",
      "name": "Sticky Note25",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        624,
        896
      ],
      "parameters": {
        "width": 208,
        "height": 208,
        "content": "### Fetch News Articles \nGets data from AlphaVantage"
      },
      "typeVersion": 1
    },
    {
      "id": "d43105bb-34e4-4be2-9375-1f7461d5abd5",
      "name": "Sticky Note26",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        848,
        896
      ],
      "parameters": {
        "width": 304,
        "height": 208,
        "content": "### Sentiment & Topic Analysis\nThis code processes the news to calculate an overall sentiment score and identify trending topics.\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buy?": {
      "main": [
        [
          {
            "node": "Buy in Alpaca",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Organizing Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Think": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "Get Income Statement",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait2": {
      "main": [
        [
          {
            "node": "Get Balance Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Get Price History",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Chart URL",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Bollinger Bands",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get MACD",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge2": {
      "main": [
        [
          {
            "node": "ChatGPT 4o1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge-2": {
      "main": [
        [
          {
            "node": "Warp as JSON for GPT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Buy?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Generate HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get MACD": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Test NYSE": {
      "main": [
        [
          {
            "node": "is on NYSE?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ChatGPT 4o": {
      "main": [
        [
          {
            "node": "Set Final Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stock List": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ChatGPT 4o1": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Test NASDAQ": {
      "main": [
        [
          {
            "node": "is on nasdaq?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "is on NYSE?": {
      "main": [
        [
          {
            "node": "Set exchange to NYSE",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Stop and Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Variable": {
      "main": [
        [
          {
            "node": "Merge-2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buy in Alpaca": {
      "main": [
        []
      ]
    },
    "Generate HTML": {
      "main": [
        [
          {
            "node": "Adjust HTML Colors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Chart URL": {
      "main": [
        [
          {
            "node": "First Technical Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get News Data": {
      "main": [
        [
          {
            "node": "Analyse API Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Variables": {
      "main": [
        [
          {
            "node": "Get Company Overview",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "is on nasdaq?": {
      "main": [
        [
          {
            "node": "Set exchange to NASDAQ",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Test NYSE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Variables1": {
      "main": [
        [
          {
            "node": "Get News Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Set Ticker for AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Organizing Data": {
      "main": [
        [
          {
            "node": "Merge-2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Execute Workflow": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Stock List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Balance Sheet": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Get Price History": {
      "main": [
        [
          {
            "node": "Calculate Support Resistance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Adjust HTML Colors": {
      "main": [
        [
          {
            "node": "Send Stock Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission": {
      "main": [
        [
          {
            "node": "Set Ticker for AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Bollinger Bands": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Company Overview": {
      "main": [
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Income Statement": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 1
          },
          {
            "node": "Wait2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set exchange to NYSE": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Trends Analysis Tool": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Warp as JSON for GPT": {
      "main": [
        [
          {
            "node": "ChatGPT 4o",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Window Buffer Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Set exchange to NASDAQ": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Ticker for AI Agent": {
      "main": [
        [
          {
            "node": "Execute Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Technical Analysis Tool": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "First Technical Analysis": {
      "main": [
        [
          {
            "node": "Set Variable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Fundamental Analysis Tool": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Generate Variables For API": {
      "main": [
        [
          {
            "node": "Set Variables1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Support Resistance": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Stock Symbol and API Key": {
      "main": [
        [
          {
            "node": "Test NASDAQ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Executed by Another Workflow": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}