{
  "id": "tJie7K4sfeSoBrAp",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "TwelveData_Pro_Analyst_n8n",
  "tags": [],
  "nodes": [
    {
      "id": "09918673-da0b-462d-9ad3-6ee914df7482",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -848,
        -288
      ],
      "parameters": {
        "color": 4,
        "width": 568,
        "height": 360,
        "content": "## TwelveData Pro Analyst\n### Elite Financial Analysis with Real-Time Data\n\nFeatures:\n- 70+ Technical Indicators\n- Fundamental Analysis\n- Multi-Stock Comparison\n- Sector Analysis\n- Portfolio Risk Assessment\n- AI-Powered Insights"
      },
      "typeVersion": 1
    },
    {
      "id": "62c5687d-a102-4628-b3a4-ed69c6ad9a20",
      "name": "Filter Message Type",
      "type": "n8n-nodes-base.code",
      "position": [
        304,
        -16
      ],
      "parameters": {
        "jsCode": "const input    = $input.first().json;\nconst msg      = input.message || {};\nconst text     = msg.text || '';\n\n// \u2500\u2500 Chat ID: handle string, number, or missing \u2500\u2500\nconst rawChatId = input.chat?.id || input.chat_id || msg.chat?.id || '';\nconst chatId    = String(rawChatId).trim(); // normalise to string\n\n// \u2500\u2500 User name: handle emoji, missing, nested differently \u2500\u2500\nconst rawName = \n  input.user?.name ||           // your structure: { user: { name: \"\ud83e\udec5\" } }\n  input.user?.first_name ||     // alternative structure\n  input.user?.username ||       // fallback to username handle\n  msg.from?.first_name ||       // standard Telegram structure\n  msg.from?.username ||         // standard Telegram username\n  '';                           // empty if nothing found\n\n// Clean the name \u2014 strip excess whitespace but keep emojis intact\nconst userName = rawName.trim() || 'there';\n\n// \u2500\u2500 Determine display name for responses \u2500\u2500\n// If name is emoji-only or very short, use generic greeting\nconst isEmojiOnly = rawName.length > 0 && !/[a-zA-Z0-9]/.test(rawName);\nconst displayName = isEmojiOnly ? rawName : (userName || 'there');\n\n// \u2500\u2500 Validate text \u2500\u2500\nif (!text || text.trim() === '') {\n  return [{\n    json: {\n      skip: true,\n      chat_id: chatId,\n      user_name: userName,\n      display_name: displayName,\n      fallback_message: `Hi ${displayName}! I can only process text messages. Please type your question and I'll be happy to help you with stock analysis. \ud83d\udcca`\n    }\n  }];\n}\n\nreturn [{\n  json: {\n    skip: false,\n    text: text.trim(),\n    chat_id: chatId,\n    user_name: userName,\n    display_name: displayName\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "b9f3d0e8-7a5c-4ff5-b203-46d6bd3a3060",
      "name": "Is Valid Message?",
      "type": "n8n-nodes-base.if",
      "position": [
        528,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "skip_check",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.skip }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4bead6c9-5fe2-4686-b0da-1036869fff50",
      "name": "Send Unsupported Message",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1056,
        -160
      ],
      "parameters": {
        "text": "={{ $json.fallback_message }}",
        "chatId": "={{ $json.chat_id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "78e12537-20d5-4d15-9378-be13efde0674",
      "name": "Telegram Trigger1",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -144,
        -16
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ad9a56cb-3bb8-4896-857f-56ffff66c2ef",
      "name": "PreProcessing1",
      "type": "n8n-nodes-base.set",
      "position": [
        80,
        -16
      ],
      "parameters": {
        "options": {
          "dotNotation": true
        },
        "assignments": {
          "assignments": [
            {
              "id": "b29dc191-d5e0-4e93-939c-fca175095f69",
              "name": "message.text",
              "type": "string",
              "value": "={{ $json?.message?.text || '' }}"
            },
            {
              "id": "0cfcdf56-1dc9-4ed3-ad6a-1b01039b6028",
              "name": "user.name",
              "type": "string",
              "value": "={{ $json?.message?.from?.first_name || 'User' }}"
            },
            {
              "id": "2c850971-102b-45e0-8fa4-eee1d307a14a",
              "name": "chat.id",
              "type": "string",
              "value": "={{ $json?.message?.chat?.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "11704bf1-4a4f-4ed4-aed1-c7305dd84906",
      "name": "AI Agent1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        992,
        144
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "options": {
          "systemMessage": "Role\nYou are Ade, an elite financial analyst with over 50 years of unparalleled experience gained from working at the New York Stock Exchange (NYSE) and the London Stock Exchange (LSE). Your expertise spans:\n\n- Advanced technical analysis with multi-timeframe perspective\n- Deep fundamental valuation using Twelve Data's comprehensive API\n- Institutional-grade risk assessment\n- Market sentiment interpretation\n- Portfolio optimization strategies\n- Real-time market intelligence\n\nYou have access to Twelve Data's powerful financial API through specialized tools that provide:\n- Real-time quotes and historical price data\n- 70+ technical indicators (RSI, MACD, Bollinger Bands, ADX, Stochastic, and more)\n- Company fundamentals (P/E, EPS, revenue, market cap)\n- Income statements and balance sheets\n- Market statistics (52-week ranges, beta, volume analysis)\n- Multi-stock comparison capabilities\n- Sector performance analytics\n- Advanced risk metrics\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPROFESSIONAL ENGAGEMENT PROTOCOL\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nMANDATORY First Interaction:\n1. Introduce yourself as Ade\n2. Ask for the user's name politely\n3. Explain your capabilities briefly\n4. Offer assistance\n\nOpening Script:\n\"Good day! I'm Ade, a senior financial analyst with over 50 years of experience at the NYSE and LSE. I specialise in comprehensive stock analysis using real-time data and advanced technical indicators.\n\nTo provide you with personalised service, may I have your name? I believe in building strong professional relationships with each client I assist.\n\nI can help you with:\n\ud83d\udcca Deep-dive stock analysis with 70+ technical indicators\n\ud83d\udcc8 Stock-to-stock comparisons\n\ud83c\udfe2 Sector performance analysis\n\u26a0\ufe0f Portfolio risk assessment\n\ud83d\udca1 Investment recommendations (not financial advice)\n\nWhat would you like to analyse today?\"\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nTOOLS AVAILABLE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n1. AnalyzeStock\n   - Use when: User asks about ANY individual stock\n   - Input: Stock ticker symbol only (e.g., \"AAPL\", \"TSLA\")\n   - Returns: Comprehensive analysis with technical indicators, fundamentals, risk assessment\n\n2. CompareStocks\n   - Use when: User wants to compare multiple stocks\n   - Input: Comma-separated tickers (e.g., \"AAPL,MSFT,GOOGL\")\n   - Returns: Side-by-side comparison\n\n3. SectorAnalysis\n   - Use when: User asks about sector performance or industry trends\n   - Input: One of these exact sector names: Technology, Healthcare, Finance, Energy, Consumer, Real Estate, Utilities, Industrials\n   - Returns: Sector trends, top/bottom performers across 5 stocks per sector\n\n4. CalculateRisk\n   - Use when: User asks about risk, volatility, VaR, drawdown, or portfolio exposure\n   - Input: Ticker symbol and optional position size (default $10,000)\n   - Returns: Comprehensive risk metrics including VaR, volatility, beta, max drawdown, stop-loss\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nANALYSIS FRAMEWORK (When Interpreting Tool Results)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nWhen you receive data from your tools, analyse using this framework:\n\n1. TECHNICAL ANALYSIS SUMMARY (Conversational)\n   - RSI interpretation: \"The RSI is at [value], which indicates [overbought/oversold/neutral]...\"\n   - MACD status: \"The MACD shows [bullish/bearish] momentum with...\"\n   - Price action: \"Looking at the recent price movement...\"\n   - Volume: \"Trading volume has been [increasing/decreasing]...\"\n   - Trend strength: \"The ADX indicates [strong/weak] trend...\"\n\n2. FUNDAMENTAL ASSESSMENT (Simple Language)\n   - Valuation: \"At a P/E ratio of [X], the stock is [expensive/cheap/fairly valued]...\"\n   - Growth: \"The company's revenue and earnings show...\"\n   - Financial health: \"With [market cap/revenue/profit], the company...\"\n\n3. RISK EVALUATION (Clear & Actionable)\n   - Risk Score: [1-10 scale]\n   - Key risks: [Top 3 specific risks]\n   - Volatility: [Comparison to market]\n   - Stop-loss: [Specific price level]\n\n4. FINAL VERDICT (MANDATORY - Address by Name)\n   \"[User's Name], based on my comprehensive analysis and 50 years of market experience, my verdict is: **[BUY/SELL/HOLD]**\n\n   Price Target: $[X] ([timeframe])\n   Stop-Loss: $[X]\n   Confidence: [High/Medium/Low]\n\n   Key Reasoning:\n   - [Reason 1 with data]\n   - [Reason 2 with data]\n   - [Reason 3 with data]\n\n   This represents my professional assessment and is not financial advice.\"\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nERROR HANDLING\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nIf a tool returns an error or \"data unavailable\":\n- Acknowledge the limitation honestly\n- Share whatever partial data was returned\n- Suggest the user try again or check the ticker symbol\n- Never fabricate data or make up numbers\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nCOMMUNICATION STYLE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n\u2713 Use user's name strategically (not every sentence)\n\u2713 Be conversational and accessible\n\u2713 Explain technical terms simply when first used\n\u2713 Show confidence backed by data\n\u2713 Use emojis sparingly for emphasis (\ud83d\udcca \ud83d\udcc8 \u26a0\ufe0f \ud83d\udca1)\n\u2713 Break down complex concepts for 15-year-old comprehension\n\u2713 Reference your experience contextually\n\u2713 Be authoritative yet humble about market uncertainties\n\n\u2717 Don't use overly complex jargon without explanation\n\u2717 Don't hedge excessively - be decisive\n\u2717 Don't provide analysis without using your tools first\n\u2717 Don't forget to give FINAL VERDICT with clear recommendation\n\u2717 Never make up or estimate data \u2014 only use what the tools return\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nSTANDARD OPERATING PROCEDURE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nStep 1: Greet and obtain user's name (first interaction only)\n\nStep 2: Identify request type:\n   - Single stock \u2192 Use AnalyzeStock tool\n   - Multiple stocks \u2192 Use CompareStocks tool\n   - Sector/industry \u2192 Use SectorAnalysis tool\n   - Risk/volatility/VaR/drawdown \u2192 Use CalculateRisk tool\n\nStep 3: Call appropriate tool with correct parameters\n\nStep 4: Interpret tool results using analysis framework\n\nStep 5: Provide clear, data-driven insights in conversational language\n\nStep 6: Deliver FINAL VERDICT with:\n   - Clear BUY/SELL/HOLD recommendation\n   - Price targets\n   - Stop-loss levels\n   - Confidence assessment\n   - Key reasoning points\n\nStep 7: Ask personalised follow-up question\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nEXAMPLE INTERACTION FLOW\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nUser: \"What do you think about Apple stock?\"\n\nYou: \"[Call AnalyzeStock tool with 'AAPL']\"\n\n[Receive comprehensive data]\n\nYou: \"Great question! Let me break down Apple (AAPL) for you.\n\n\ud83d\udcca **Current Snapshot**\nApple is trading at $178.32, up 2.3% today. Looking at the technical picture, the RSI is at 62, which sits comfortably in the neutral zone - neither overbought nor oversold. The MACD recently crossed bullish, suggesting positive momentum building.\n\n\ud83d\udcc8 **Technical Signals**\nThe stock is riding nicely on its Bollinger Bands middle line with the ADX showing strong trend strength at 34. Volume has increased 15% above average, which I've learned over decades often signals institutional interest.\n\n\ud83d\udcbc **Fundamental Health**\nAt a P/E of 28.5, Apple trades at a premium, but their consistent revenue growth of 8% and strong profit margins justify it. The company's $2.8 trillion market cap makes it a market leader with solid fundamentals.\n\n\u26a0\ufe0f **Risk Assessment**\nRisk Score: 4/10 (Moderate-Low)\nKey Support: $172\nStop-Loss Recommendation: $170\n\n\ud83c\udfaf **[User's Name], my FINAL VERDICT: BUY**\n\nPrice Target: $195-200 (30-45 days)\nConfidence: High\n\nKey Reasoning:\n- Bullish MACD crossover with strong volume confirmation\n- Trading above all major moving averages\n- Institutional accumulation evident in volume patterns\n- Strong fundamentals support continued growth\n\nThis represents my professional assessment based on current data and 50+ years of market experience. It is not financial advice.\n\nWould you like me to compare Apple with any competitors, or analyse another stock for you?\"\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nRemember: Always use your tools when stock analysis is requested. Never provide generic advice - leverage the real-time Twelve Data to give specific, data-driven recommendations."
        },
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "8d723220-1610-4f97-9c00-eec19797b6e0",
      "name": "Conversation Memory1",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        880,
        368
      ],
      "parameters": {
        "sessionKey": "=7107208579",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "599903f5-275a-4443-b879-cbbc517f9388",
      "name": "Analytics_Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        752,
        368
      ],
      "parameters": {
        "model": "openai/o3-mini",
        "options": {
          "maxTokens": 4000
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c16b9f53-66eb-40a5-b103-b14d7defd10d",
      "name": "Stock Analysis Tool1",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        1008,
        368
      ],
      "parameters": {
        "name": "AnalyzeStock",
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "S6UMbAr36CghN8gkbUOtz",
          "cachedResultUrl": "/workflow/S6UMbAr36CghN8gkbUOtz",
          "cachedResultName": "TwelveData_Pro_Helper"
        },
        "description": "Call this tool to get comprehensive technical and fundamental analysis of any single stock. Provide only the stock ticker symbol (e.g., 'AAPL', 'TSLA', 'MSFT'). Returns detailed analysis including 70+ technical indicators, fundamentals, risk assessment, and buy/sell/hold recommendations. Use this for any question about a single stock.",
        "workflowInputs": {
          "value": {
            "query": "={{ $json.query }}"
          },
          "mappingMode": "defineBelow"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "0a16310c-bf79-4b6f-a812-12fd48158550",
      "name": "Stock Comparison Tool1",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        1136,
        368
      ],
      "parameters": {
        "name": "CompareStocks",
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "d6nkzYp6pVEE5aR4VZFY1",
          "cachedResultUrl": "/workflow/d6nkzYp6pVEE5aR4VZFY1",
          "cachedResultName": "Stock_Comparison_Tool"
        },
        "description": "Compare two or more stocks side-by-side. Provide ticker symbols separated by commas (e.g., 'AAPL,MSFT,GOOGL'). Returns comparative analysis including price performance, RSI momentum signals, P/E valuation, market cap, sector, 52-week range positioning, and a quick verdict per stock. Use this whenever a user wants to compare multiple stocks.",
        "workflowInputs": {
          "value": {
            "tickers": "={{ $json.tickers }}"
          },
          "mappingMode": "defineBelow"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a688d666-f6ec-474b-ada9-a0f859cb1965",
      "name": "Sector Analysis Tool1",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        1264,
        368
      ],
      "parameters": {
        "name": "SectorAnalysis",
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "lYh8bOAOFSRNNU5OjiWOj",
          "cachedResultUrl": "/workflow/lYh8bOAOFSRNNU5OjiWOj",
          "cachedResultName": "Sector_Analysis_Tool_Enhanced"
        },
        "description": "Get sector performance analysis, top and bottom performers, industry breakdown and investment recommendation for an entire market sector. Provide one of these exact sector names: Technology, Healthcare, Finance, Energy, Consumer, Real Estate, Utilities, Industrials. Returns sector strength, average P/E, average beta, top 3 performers, bottom 2 performers, industry breakdown, and a BUY/SELECTIVE BUY/AVOID recommendation for the whole sector.",
        "workflowInputs": {
          "value": {
            "sector": "={{ $json.sector }}"
          },
          "mappingMode": "defineBelow"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "43dd2f38-7e8a-4776-9750-489d6767d396",
      "name": "Risk Calculator Tool1",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        1392,
        368
      ],
      "parameters": {
        "name": "CalculateRisk",
        "workflowId": {
          "__rl": true,
          "mode": "list",
          "value": "Jug-KrHObfV-QNCsN3ojN",
          "cachedResultUrl": "/workflow/Jug-KrHObfV-QNCsN3ojN",
          "cachedResultName": "Risk_Calculator_Tool"
        },
        "description": "Calculate comprehensive risk metrics for a stock position. Provide a ticker symbol and optional position size in USD (default is $10,000 if not specified). Returns: annualised volatility, daily volatility, maximum drawdown with dates, Value at Risk (95% confidence 1-day), 5-component risk score out of 10, beta analysis, P/E valuation risk, leverage risk, debt-to-equity, free cash flow, short interest, institutional ownership, suggested stop-loss price, and position sizing recommendation. Use this whenever a user asks about risk, volatility, VaR, drawdown, how much they could lose, or portfolio exposure.",
        "workflowInputs": {
          "value": {
            "ticker": "={{ $json.ticker }}",
            "positionSize": "={{ $json.positionSize || 10000 }}"
          },
          "mappingMode": "defineBelow"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "35de563e-5773-4f35-8447-1948f4b8ba33",
      "name": "Send Response1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1600,
        144
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "chatId": "=",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7288d7d1-b7c4-4121-8ad5-309a2578bec6",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -848,
        96
      ],
      "parameters": {
        "color": 4,
        "width": 560,
        "height": 472,
        "content": "## TwelveData Pro Analyst v2\n### Elite Financial Analysis \u2014 Ade AI\n\n**Fixes Applied:**\n- \u2705 Dynamic chat.id (multi-user)\n- \u2705 Dynamic memory per user\n- \u2705 o3-mini model (upgraded from GPT-4o)\n- \u2705 Non-text message filter\n- \u2705 All 8 sectors in tool descriptions\n- \u2705 Improved tool descriptions\n- \u2705 Error handling in system prompt\n- \u2705 Temperature removed (o3-mini)\n- \u2705 Max tokens raised to 4000\n\n**Tools Wired:**\n- \ud83d\udcca AnalyzeStock \u2192 Helper\n- \ud83d\udcc8 CompareStocks \u2192 Comparison Tool\n- \ud83c\udfe2 SectorAnalysis \u2192 Sector Tool\n- \u26a0\ufe0f CalculateRisk \u2192 Risk Calculator"
      },
      "typeVersion": 1
    },
    {
      "id": "cbbe92d8-75e6-4be6-ae85-8257da8c22a8",
      "name": "Trigger",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        432,
        912
      ],
      "parameters": {
        "inputSource": "passthrough"
      },
      "typeVersion": 1.1
    },
    {
      "id": "b4595614-3b17-4cf1-a2ae-735ee031617d",
      "name": "Get Historical Data",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        832,
        848
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}{{ $json.chatInput }}",
        "operation": "getTimeSeries",
        "requestOptions": {},
        "additionalOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "48b453db-74c1-4e80-a760-fd16e64af248",
      "name": "Get Statistics",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        832,
        992
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}{{ $json.chatInput }}",
        "resource": "fundamentals",
        "operation": "getStatistics",
        "requestOptions": {},
        "fundamentalsOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "015583f2-c324-4186-bad8-b9dc347af301",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        1056,
        912
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3
    },
    {
      "id": "b5b011ff-bf3c-4168-9a54-cb4f562783fa",
      "name": "Calculate Risk Metrics",
      "type": "n8n-nodes-base.code",
      "position": [
        1280,
        912
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\n// \u2500\u2500 INPUTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst meta      = data.meta || {};\nconst timeSeries = data.values || [];\nconst stats     = data.statistics || {};\nconst val       = stats.valuations_metrics || {};\nconst fin       = stats.financials || {};\nconst inc       = fin.income_statement || {};\nconst bal       = fin.balance_sheet || {};\nconst cf        = fin.cash_flow || {};\nconst stk       = stats.stock_statistics || {};\nconst prc       = stats.stock_price_summary || {};\nconst div       = stats.dividends_and_splits || {};\n\nconst ticker        = meta.symbol || 'UNKNOWN';\nconst companyName   = meta.name || ticker;\nconst positionSize  = $input.first().json.positionSize || 10000;\nconst currentPrice  = parseFloat(timeSeries[0]?.close) || 0;\n\n// \u2500\u2500 DAILY RETURNS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst returns = [];\nfor (let i = 1; i < timeSeries.length; i++) {\n  const curr = parseFloat(timeSeries[i-1].close);\n  const prev = parseFloat(timeSeries[i].close);\n  if (prev > 0) returns.push(((curr - prev) / prev) * 100);\n}\n\n// \u2500\u2500 VOLATILITY (Annualised) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst mean = returns.reduce((s, r) => s + r, 0) / returns.length;\nconst variance = returns.reduce((s, r) => s + Math.pow(r - mean, 2), 0) / returns.length;\nconst dailyVol = Math.sqrt(variance);\nconst annualVol = dailyVol * Math.sqrt(252);\n\n// \u2500\u2500 MAXIMUM DRAWDOWN \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet peak = parseFloat(timeSeries[0]?.close) || 0;\nlet maxDrawdown = 0;\nlet drawdownStart = timeSeries[0]?.datetime;\nlet drawdownEnd = timeSeries[0]?.datetime;\nlet tempPeakDate = timeSeries[0]?.datetime;\n\ntimeSeries.forEach(day => {\n  const price = parseFloat(day.close);\n  if (price > peak) {\n    peak = price;\n    tempPeakDate = day.datetime;\n  }\n  const dd = ((peak - price) / peak) * 100;\n  if (dd > maxDrawdown) {\n    maxDrawdown = dd;\n    drawdownStart = tempPeakDate;\n    drawdownEnd = day.datetime;\n  }\n});\n\n// \u2500\u2500 VALUE AT RISK (95%, 1-day) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst sortedReturns = [...returns].sort((a, b) => a - b);\nconst varIndex = Math.floor(sortedReturns.length * 0.05);\nconst var95Pct  = Math.abs(sortedReturns[varIndex] || 0);\nconst var95Dollar = (positionSize * var95Pct) / 100;\n\n// \u2500\u2500 52-WEEK POSITIONING \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst low52  = prc.fifty_two_week_low || 0;\nconst high52 = prc.fifty_two_week_high || 0;\nconst pctFrom52Low  = (((currentPrice - low52) / low52) * 100).toFixed(2);\nconst pctFrom52High = (((high52 - currentPrice) / high52) * 100).toFixed(2);\nconst rangePosition = (((currentPrice - low52) / (high52 - low52)) * 100).toFixed(1);\n\n// \u2500\u2500 MOVING AVERAGE SIGNALS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst ma50  = prc.day_50_ma || 0;\nconst ma200 = prc.day_200_ma || 0;\nconst aboveMa50  = currentPrice > ma50;\nconst aboveMa200 = currentPrice > ma200;\nconst goldenCross = ma50 > ma200; // bullish long-term signal\n\n// \u2500\u2500 SHORT INTEREST \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst shortPct = (stk.short_percent_of_shares_outstanding * 100).toFixed(2);\nconst shortRatio = stk.short_ratio || 0;\nconst highShortInterest = stk.short_percent_of_shares_outstanding > 0.05;\n\n// \u2500\u2500 FINANCIAL HEALTH METRICS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst debtToEquity   = bal.total_debt_to_equity_mrq || 0;\nconst currentRatio   = bal.current_ratio_mrq || 0;\nconst profitMargin   = (fin.profit_margin * 100).toFixed(2);\nconst operatingMargin = (fin.operating_margin * 100).toFixed(2);\nconst grossMargin    = (fin.gross_margin * 100).toFixed(2);\nconst roe            = (fin.return_on_equity_ttm * 100).toFixed(2);\nconst roa            = (fin.return_on_assets_ttm * 100).toFixed(2);\nconst fcf            = cf.levered_free_cash_flow_ttm || 0;\nconst ocf            = cf.operating_cash_flow_ttm || 0;\nconst cashVsDebt     = bal.total_cash_mrq - bal.total_debt_mrq;\n\n// \u2500\u2500 VALUATION RISK \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst trailingPE  = val.trailing_pe || 0;\nconst forwardPE   = val.forward_pe || 0;\nconst pegRatio    = val.peg_ratio || 0;\nconst ptb         = val.price_to_book_mrq || 0;\nconst evEbitda    = val.enterprise_to_ebitda || 0;\n\n// \u2500\u2500 COMPOSITE RISK SCORE (1-10) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Volatility: >40% annualised = 10, <10% = 1\nconst volatilityScore = Math.min(Math.max((annualVol / 4), 1), 10);\n\n// Drawdown: >40% = 10, <5% = 1\nconst drawdownScore = Math.min(Math.max((maxDrawdown / 4), 1), 10);\n\n// Beta: far from 1 = higher risk\nconst beta = prc.beta || 1;\nconst betaScore = Math.min(Math.max(Math.abs(beta - 1) * 5 + 1, 1), 10);\n\n// Valuation risk: high PE + high PEG = overvaluation risk\nconst valuationScore = Math.min(Math.max((trailingPE / 10) + (pegRatio > 2 ? 2 : 0), 1), 10);\n\n// Leverage risk: high debt-to-equity\nconst leverageScore = Math.min(Math.max(debtToEquity / 20, 1), 10);\n\n// Composite (weighted)\nconst riskScore = Math.round(\n  (volatilityScore * 0.25) +\n  (drawdownScore   * 0.25) +\n  (betaScore       * 0.20) +\n  (valuationScore  * 0.20) +\n  (leverageScore   * 0.10)\n);\n\nconst riskLabel = riskScore <= 3 ? '\ud83d\udfe2 LOW RISK' :\n                  riskScore <= 5 ? '\ud83d\udfe1 MODERATE RISK' :\n                  riskScore <= 7 ? '\ud83d\udfe0 ELEVATED RISK' : '\ud83d\udd34 HIGH RISK';\n\n// \u2500\u2500 STOP-LOSS RECOMMENDATION \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst stopLoss = (currentPrice * (1 - (maxDrawdown / 200))).toFixed(2);\nconst stopLossPct = ((currentPrice - stopLoss) / currentPrice * 100).toFixed(2);\n\n// \u2500\u2500 POSITION SIZING \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst positionGuide = riskScore <= 3 ? '10\u201315% of portfolio' :\n                      riskScore <= 5 ? '5\u201310% of portfolio' :\n                      riskScore <= 7 ? '3\u20135% of portfolio' : '1\u20133% of portfolio (high risk)';\n\n// \u2500\u2500 FORMATTED REPORT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst formatted = `\nRISK ANALYSIS REPORT \u2014 ${ticker} (${companyName})\nGenerated: ${new Date().toISOString()}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPOSITION & PRICE CONTEXT\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nCurrent Price:      $${currentPrice}\nPosition Size:      $${positionSize.toLocaleString()}\n52-Week Low:        $${low52}  (+${pctFrom52Low}% current)\n52-Week High:       $${high52}  (-${pctFrom52High}% current)\nRange Position:     ${rangePosition}% between 52w low and high\n\n50-Day MA:          $${ma50}  ${aboveMa50 ? '\u2705 Price ABOVE (bullish)' : '\u26a0\ufe0f Price BELOW (bearish)'}\n200-Day MA:         $${ma200}  ${aboveMa200 ? '\u2705 Price ABOVE (bullish)' : '\u26a0\ufe0f Price BELOW (bearish)'}\nMA Cross Signal:    ${goldenCross ? '\u2705 Golden Cross (50MA > 200MA) \u2014 Bullish' : '\u26a0\ufe0f Death Cross (50MA < 200MA) \u2014 Bearish'}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nOVERALL RISK SCORE: ${riskScore}/10 \u2014 ${riskLabel}\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nComponent Breakdown:\n  Volatility Risk:   ${volatilityScore.toFixed(1)}/10\n  Drawdown Risk:     ${drawdownScore.toFixed(1)}/10\n  Beta Risk:         ${betaScore.toFixed(1)}/10\n  Valuation Risk:    ${valuationScore.toFixed(1)}/10\n  Leverage Risk:     ${leverageScore.toFixed(1)}/10\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPRICE RISK METRICS\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nAnnualised Volatility:    ${annualVol.toFixed(2)}%\nDaily Volatility:         ${dailyVol.toFixed(2)}%\n\nMaximum Drawdown:         ${maxDrawdown.toFixed(2)}%\n  Peak date:              ${drawdownStart}\n  Trough date:            ${drawdownEnd}\n\nValue at Risk (95%, 1d):  $${var95Dollar.toFixed(2)}  (${var95Pct.toFixed(2)}% of position)\n  Interpretation: On 95% of trading days, loss will not exceed $${var95Dollar.toFixed(2)}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nVALUATION RISK\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nTrailing P/E:       ${trailingPE.toFixed(2)}\nForward P/E:        ${forwardPE.toFixed(2)}  ${forwardPE < trailingPE ? '\u2705 Earnings growth expected' : '\u26a0\ufe0f Earnings expected to decline'}\nPEG Ratio:          ${pegRatio.toFixed(2)}  ${pegRatio < 1 ? '\u2705 Undervalued relative to growth' : pegRatio < 2 ? '\u26a0\ufe0f Fairly valued' : '\ud83d\udd34 Potentially overvalued'}\nPrice-to-Book:      ${ptb.toFixed(2)}\nEV/EBITDA:          ${evEbitda.toFixed(2)}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nFINANCIAL HEALTH\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nGross Margin:       ${grossMargin}%\nOperating Margin:   ${operatingMargin}%\nNet Profit Margin:  ${profitMargin}%\nReturn on Equity:   ${roe}%\nReturn on Assets:   ${roa}%\n\nOperating Cash Flow:    $${(ocf / 1e9).toFixed(2)}B\nFree Cash Flow:         $${(fcf / 1e9).toFixed(2)}B\nTotal Cash:             $${(bal.total_cash_mrq / 1e9).toFixed(2)}B\nTotal Debt:             $${(bal.total_debt_mrq / 1e9).toFixed(2)}B\nNet Cash Position:      ${cashVsDebt >= 0 ? '+' : ''}$${(cashVsDebt / 1e9).toFixed(2)}B  ${cashVsDebt >= 0 ? '\u2705 Net cash positive' : '\u26a0\ufe0f Net debt position'}\nDebt-to-Equity:         ${debtToEquity.toFixed(2)}\nCurrent Ratio:          ${currentRatio.toFixed(2)}  ${currentRatio >= 1 ? '\u2705 Adequate liquidity' : '\u26a0\ufe0f Liquidity concern'}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nMARKET SENTIMENT INDICATORS\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nBeta:                   ${beta}  ${beta > 1.2 ? '\u26a0\ufe0f More volatile than market' : beta < 0.8 ? '\u2705 Defensive \u2014 less volatile than market' : '\u2705 Moves broadly with market'}\nShort Interest:         ${shortPct}%  ${highShortInterest ? '\u26a0\ufe0f Elevated short interest' : '\u2705 Low short interest'}\nShort Ratio:            ${shortRatio} days to cover\nInstitutional Ownership: ${(stk.percent_held_by_institutions * 100).toFixed(1)}%  ${stk.percent_held_by_institutions > 0.6 ? '\u2705 Strong institutional backing' : '\u26a0\ufe0f Low institutional conviction'}\nInsider Ownership:      ${(stk.percent_held_by_insiders * 100).toFixed(2)}%\n\nDividend Yield:         ${(div.forward_annual_dividend_yield * 100).toFixed(2)}%\nPayout Ratio:           ${(div.payout_ratio * 100).toFixed(1)}%  ${div.payout_ratio < 0.5 ? '\u2705 Sustainable dividend' : '\u26a0\ufe0f High payout ratio'}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nRISK MANAGEMENT RECOMMENDATIONS\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nSuggested Stop-Loss:    $${stopLoss}  (${stopLossPct}% below current price)\nMax Downside to Stop:   $${((currentPrice - stopLoss) * (positionSize / currentPrice)).toFixed(2)} on $${positionSize.toLocaleString()} position\n\nRecommended Position Size: ${positionGuide}\n\nDiversification Note:\n${riskScore > 6 ? '\ud83d\udd34 CRITICAL \u2014 Ensure strong sector diversification. Do not concentrate.' :\n  riskScore > 3 ? '\ud83d\udfe1 Standard diversification across sectors recommended.' :\n  '\ud83d\udfe2 Well-suited as a core holding with standard diversification.'}\n`;\n\nreturn {\n  json: {\n    ticker,\n    companyName,\n    riskScore,\n    riskLabel,\n    annualizedVolatility: parseFloat(annualVol.toFixed(2)),\n    dailyVolatility: parseFloat(dailyVol.toFixed(2)),\n    maxDrawdown: parseFloat(maxDrawdown.toFixed(2)),\n    drawdownPeriod: { start: drawdownStart, end: drawdownEnd },\n    var95Percent: parseFloat(var95Pct.toFixed(2)),\n    var95Dollar: parseFloat(var95Dollar.toFixed(2)),\n    beta,\n    trailingPE,\n    forwardPE,\n    pegRatio,\n    profitMargin: parseFloat(profitMargin),\n    operatingMargin: parseFloat(operatingMargin),\n    freeCashFlow: fcf,\n    netCashPosition: cashVsDebt,\n    shortInterestPct: parseFloat(shortPct),\n    institutionalOwnership: parseFloat((stk.percent_held_by_institutions * 100).toFixed(1)),\n    stopLoss: parseFloat(stopLoss),\n    positionGuide,\n    rangePosition: parseFloat(rangePosition),\n    aboveMa50,\n    aboveMa200,\n    goldenCross,\n    formatted\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a7031505-8171-4091-9ced-a25ed46e72c9",
      "name": "Output",
      "type": "n8n-nodes-base.set",
      "position": [
        1504,
        912
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d8651f45-477c-4c0d-b737-74b711e048f4",
              "name": "ticker",
              "type": "string",
              "value": "={{ $json.ticker }}"
            },
            {
              "id": "019ff347-1eca-4f42-bba8-548d972b1479",
              "name": "companyName",
              "type": "string",
              "value": "={{ $json.companyName }}"
            },
            {
              "id": "413b7428-579f-4232-b472-15f9e0c023b2",
              "name": "response",
              "type": "string",
              "value": "={{ $json.formatted }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a8bd84c1-b4ef-4000-9b8e-67f1e54edfe7",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        672
      ],
      "parameters": {
        "color": 4,
        "width": 2192,
        "height": 600,
        "content": "## Risk Calculator Tool\n### Elite Financial Analysis \u2014 Ade AI\n\n\n- Triggered by a parent workflow passing a **stock ticker** and optional **position size** (default $10,000)\n- Fetches **historical price data** and **fundamental statistics** from Twelve Data in parallel\n- Calculates **annualised volatility**, **maximum drawdown** (with dates), and **Value at Risk** (95% confidence, 1-day)\n- Scores **valuation risk** using P/E, PEG, Price-to-Book and EV/EBITDA\n- Assesses **financial health** \u2014 margins, ROE, free cash flow, debt vs cash\n- Checks **sentiment** \u2014 beta, short interest, institutional ownership, dividend sustainability\n- Produces a **composite risk score out of 10** (volatility + drawdown + beta + valuation + leverage)\n- Recommends a **stop-loss price** and **position sizing** based on the risk score\n- Returns a formatted plain-text report plus structured JSON back to the calling workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "397713af-9cd5-4ab2-a15f-caddcc969959",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -400,
        1376
      ],
      "parameters": {
        "color": 4,
        "width": 2192,
        "height": 600,
        "content": "## Stock Comparison Tool\n### Elite Financial Analysis \u2014 Ade AI\n\n- Triggered by a parent workflow passing ticker symbols as a comma-separated string, natural language (e.g. \"compare AAPL and TSLA\"), or space-separated list\n- **Smart ticker extraction** strips common English words (AND, THE, VS, etc.) to isolate valid stock symbols, then deduplicates\n- Fetches **Quote**, **RSI (14)**, and **Company Profile** from Twelve Data in parallel for every ticker\n- Merges all three data sources and builds a structured comparison object per stock\n- Calculates **summary metrics** \u2014 best/worst performer, performance gap, average RSI, overbought/oversold stocks, sectors covered\n- Produces a **momentum ranking** sorted by daily % change\n- Generates a **Quick Verdict per stock** combining RSI signal, daily move strength, and 52-week range positioning\n- Returns a formatted plain-text comparison report plus structured JSON back to the calling workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "802624fd-afbc-499f-b090-58fd3da25ba8",
      "name": "Split Tickers1",
      "type": "n8n-nodes-base.code",
      "position": [
        608,
        1600
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json;\n\n// Handle Chat Trigger (chatInput) OR workflow trigger (tickers field)\nconst raw = input.chatInput || input.tickers || input.message || input.text || '';\n\n// Extract ticker symbols \u2014 handles:\n// \"AAPL, TSLA, MSFT\"         \u2192 comma separated\n// \"compare AAPL and TSLA\"    \u2192 natural language\n// \"AAPL TSLA MSFT\"           \u2192 space separated\nconst tickersInput = raw.toUpperCase();\n\n// Pull out anything that looks like a stock ticker (1-5 capital letters)\nconst extracted = tickersInput.match(/\\b[A-Z]{1,5}\\b/g) || [];\n\n// Filter out common English words that match ticker pattern\nconst stopWords = new Set([\n  'A', 'I', 'AND', 'THE', 'FOR', 'VS', 'OR', 'ME', 'MY',\n  'IN', 'ON', 'AT', 'TO', 'OF', 'IS', 'IT', 'BE', 'DO',\n  'CAN', 'GET', 'HOW', 'ARE', 'WAS', 'HAS', 'HAD', 'BUT',\n  'NOT', 'ALL', 'SO', 'IF', 'NO', 'UP', 'AN', 'AS', 'BY',\n  'WITH', 'COMPARE', 'STOCK', 'STOCKS', 'PRICE', 'SHOW'\n]);\n\nconst tickers = [...new Set(extracted.filter(t => !stopWords.has(t)))];\n\nif (tickers.length === 0) {\n  throw new Error('No stock tickers found. Please provide ticker symbols like: AAPL, TSLA, MSFT');\n}\n\nreturn tickers.map(ticker => ({\n  json: { ticker }\n}));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "eb1e0fe5-7bdf-4e8b-bdd3-d497acbb72ac",
      "name": "Get Quotes1",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        832,
        1408
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "requestOptions": {},
        "additionalOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5546b63f-7b9a-4d2d-8ec7-298893de5774",
      "name": "Get RSI1",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        832,
        1600
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "technicalIndicators",
        "operation": "rsi",
        "requestOptions": {},
        "indicatorOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "cf0b5d2a-aac5-4808-8e88-4a11a9373fdc",
      "name": "Get Profile1",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        832,
        1792
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "fundamentals",
        "requestOptions": {},
        "fundamentalsOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "064cfaa5-9b78-4262-baa5-d1ad17df10a2",
      "name": "Merge Stock Data1",
      "type": "n8n-nodes-base.merge",
      "position": [
        1056,
        1584
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition",
        "numberInputs": 3
      },
      "typeVersion": 3
    },
    {
      "id": "d332e75f-f8f3-4108-aa5d-89a1a6ddc2c6",
      "name": "Format Comparison1",
      "type": "n8n-nodes-base.code",
      "position": [
        1280,
        1600
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\nconst comparison = items.map(item => {\n  const d = item.json;\n\n  // Skip errors\n  if (d.status === 'error' || d.code === 403) return null;\n\n  const sym = d.symbol || d.meta?.symbol || 'N/A';\n\n  // \u2500\u2500 Price data \u2014 direct from root \u2500\u2500\n  const price      = parseFloat(d.close || 0);\n  const change     = parseFloat(d.percent_change || 0);\n  const volume     = parseInt(d.volume || 0);\n  const avgVolume  = parseInt(d.average_volume || 0);\n  const fiftyTwo   = d.fifty_two_week || {};\n\n  // \u2500\u2500 RSI \u2014 from values[] array, first entry = most recent \u2500\u2500\n  const rsi = d.values && d.values[0]?.rsi\n    ? parseFloat(d.values[0].rsi).toFixed(2)\n    : 'N/A';\n\n  // \u2500\u2500 Profile data \u2014 direct from root \u2500\u2500\n  const sector   = d.sector   || 'N/A';\n  const industry = d.industry || 'N/A';\n  const ceo      = d.CEO      || d.ceo || 'N/A';\n  const employees = parseInt(d.employees || 0);\n  const description = d.description || '';\n  const website  = d.website  || '';\n  const exchange = d.exchange || 'N/A';\n  const name     = d.name     || sym;\n\n  return {\n    symbol: sym,\n    name,\n    sector,\n    industry,\n    exchange,\n    ceo,\n    employees,\n    description,\n    website,\n    price,\n    change,\n    volume,\n    avgVolume,\n    previousClose: parseFloat(d.previous_close || 0),\n    open:  parseFloat(d.open  || 0),\n    high:  parseFloat(d.high  || 0),\n    low:   parseFloat(d.low   || 0),\n    fiftyTwoWeekLow:  parseFloat(fiftyTwo.low  || 0),\n    fiftyTwoWeekHigh: parseFloat(fiftyTwo.high || 0),\n    rsi,\n    isMarketOpen: d.is_market_open || false\n  };\n}).filter(Boolean);\n\n// Sort by % change descending\ncomparison.sort((a, b) => b.change - a.change);\n\n// \u2500\u2500 Summary metrics \u2500\u2500\nconst bestPerformer  = comparison[0] || {};\nconst worstPerformer = comparison[comparison.length - 1] || {};\nconst perfGap        = (bestPerformer.change - worstPerformer.change).toFixed(2);\n\nconst validRSI = comparison.filter(s => s.rsi !== 'N/A');\nconst avgRSI   = validRSI.length > 0\n  ? validRSI.reduce((sum, s) => sum + parseFloat(s.rsi), 0) / validRSI.length\n  : 0;\n\nconst sectors    = [...new Set(comparison.map(s => s.sector).filter(s => s !== 'N/A'))];\nconst overbought = comparison.filter(s => s.rsi !== 'N/A' && parseFloat(s.rsi) > 70);\nconst oversold   = comparison.filter(s => s.rsi !== 'N/A' && parseFloat(s.rsi) < 30);\n\n// \u2500\u2500 Helper functions \u2500\u2500\nconst fmt = (n, d=2) => (!isNaN(n) && n !== null) ? Number(n).toFixed(d) : 'N/A';\n\nconst rsiSignal = (rsi) => {\n  if (rsi === 'N/A') return 'N/A';\n  const r = parseFloat(rsi);\n  return r > 70 ? '\ud83d\udd34 Overbought' : r < 30 ? '\ud83d\udfe2 Oversold' : r >= 50 ? '\ud83d\udfe1 Neutral/Bullish' : '\ud83d\udfe1 Neutral/Bearish';\n};\n\nconst rangePosition = (price, low, high) => {\n  if (!low || !high || high === low) return 'N/A';\n  return (((price - low) / (high - low)) * 100).toFixed(0) + '%';\n};\n\nconst fmtChange = (n) => (n >= 0 ? '+' : '') + fmt(n) + '%';\n\n// \u2500\u2500 Build report \u2500\u2500\nconst formatted = `\nSTOCK COMPARISON ANALYSIS\nGenerated: ${new Date().toISOString()}\nStocks Compared: ${comparison.map(s => s.symbol).join(' vs ')}\nMarket Status: ${comparison[0]?.isMarketOpen ? '\ud83d\udfe2 Market Open' : '\ud83d\udd34 Market Closed'}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPERFORMANCE SNAPSHOT\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n${comparison.map((s, i) => `${i + 1}. ${s.symbol} \u2014 ${s.name}\n   Price:        $${fmt(s.price)} (${fmtChange(s.change)})\n   Open/High/Low: $${fmt(s.open)} / $${fmt(s.high)} / $${fmt(s.low)}\n   Prev Close:   $${fmt(s.previousClose)}\n   Sector:       ${s.sector} | ${s.industry}\n   Exchange:     ${s.exchange}\n   RSI (14):     ${s.rsi} \u2014 ${rsiSignal(s.rsi)}\n   Volume:       ${(s.volume / 1e6).toFixed(2)}M (Avg: ${(s.avgVolume / 1e6).toFixed(1)}M)\n   52-Week:      $${fmt(s.fiftyTwoWeekLow)} \u2013 $${fmt(s.fiftyTwoWeekHigh)}\n   Range Pos:    ${rangePosition(s.price, s.fiftyTwoWeekLow, s.fiftyTwoWeekHigh)} of 52w range\n   CEO:          ${s.ceo}\n   Employees:    ${s.employees > 0 ? s.employees.toLocaleString() : 'N/A'}`\n).join('\\n\\n')}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nRELATIVE PERFORMANCE SUMMARY\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nBest Performer:  ${bestPerformer.symbol} (${fmtChange(bestPerformer.change)})\nWorst Performer: ${worstPerformer.symbol} (${fmtChange(worstPerformer.change)})\nPerformance Gap: ${perfGap}%\n\nAverage RSI:     ${fmt(avgRSI, 1)}\nOverbought:      ${overbought.length > 0 ? overbought.map(s => s.symbol).join(', ') : 'None'}\nOversold:        ${oversold.length > 0 ? oversold.map(s => s.symbol).join(', ') : 'None'}\nSectors Covered: ${sectors.join(', ') || 'N/A'}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nMOMENTUM RANKING (by % change today)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n${comparison.map((s, i) =>\n  `${i + 1}. ${s.symbol.padEnd(6)} ${fmtChange(s.change).padEnd(10)} | RSI: ${String(s.rsi).padEnd(7)} ${rsiSignal(s.rsi)}`\n).join('\\n')}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nQUICK VERDICT\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n${comparison.map(s => {\n  const signals = [];\n  if (s.rsi !== 'N/A') {\n    const r = parseFloat(s.rsi);\n    if (r > 70)      signals.push('\u26a0\ufe0f Overbought \u2014 potential pullback');\n    else if (r < 30) signals.push('\u2705 Oversold \u2014 potential bounce');\n    else if (r >= 50)signals.push('\u2705 Bullish momentum');\n    else             signals.push('\u26a0\ufe0f Weakening momentum');\n  }\n  if (s.change > 1)       signals.push('\ud83d\udcc8 Strong daily gain');\n  else if (s.change < -1) signals.push('\ud83d\udcc9 Significant daily loss');\n  const pctOfHigh = s.fiftyTwoWeekHigh > 0 ? (s.price / s.fiftyTwoWeekHigh) * 100 : 0;\n  if (pctOfHigh > 90)     signals.push('\ud83d\udd25 Near 52-week high');\n  else if (pctOfHigh < 30)signals.push('\ud83d\udca1 Near 52-week low \u2014 value zone');\n  return `${s.symbol}: ${signals.join(' | ') || 'Insufficient data'}`;\n}).join('\\n')}\n`;\n\nreturn {\n  json: {\n    response: formatted,\n    comparison,\n    bestPerformer: bestPerformer.symbol,\n    worstPerformer: worstPerformer.symbol,\n    perfGap: parseFloat(perfGap),\n    avgRSI: parseFloat(fmt(avgRSI, 1)),\n    overbought: overbought.map(s => s.symbol),\n    oversold: oversold.map(s => s.symbol),\n    stockCount: comparison.length\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "cac85015-70a5-46fe-b972-b199d6db9128",
      "name": "Output1",
      "type": "n8n-nodes-base.set",
      "position": [
        1504,
        1600
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "0b897bf2-1f6e-49ee-b65c-140235ae7da4",
              "name": "response",
              "type": "string",
              "value": "={{ $json.response }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "70076583-bd97-4b5f-8ea2-6a8872aa6159",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1872,
        672
      ],
      "parameters": {
        "color": 4,
        "width": 2960,
        "height": 600,
        "content": "## Sector Analysis Tool\n### Elite Financial Analysis \u2014 Ade AI\n\n\n- Triggered by a parent workflow passing a sector name (Technology, Healthcare, Finance, Energy, Consumer, Real Estate, Utilities, or Industrials)\n- **Seeds 5 representative stocks** per sector from a built-in lookup table with known fallback data (CEO, industry, beta, P/E, market cap, employees)\n- Fetches live **Quote** and **Statistics** from Twelve Data in parallel for each stock, then merges and enriches with fallback values where live data is unavailable\n- Sorts stocks by market cap and keeps the top 5, building a fully structured object per stock covering price, valuation, margins, cash flow, dividends, moving averages and share stats\n- Calculates **sector-wide metrics** \u2014 average % change, combined market cap & revenue, total employees, average P/E, average beta, average profit margin and revenue growth\n- Rates **sector strength** on a 5-tier scale: Very Strong \u2192 Strong \u2192 Neutral \u2192 Weak \u2192 Very Weak\n- Identifies **top 3 performers** and **bottom 2 performers** with full fundamental detail\n- Breaks down the sector by **industry sub-group** with average performance and combined market cap per industry\n- Generates a **Bullish / Mixed / Bearish investment recommendation** with top pick and stock to avoid\n- Returns a formatted plain-text sector report plus structured JSON back to the calling workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "57846b38-073f-45a3-95fe-84c7439e7631",
      "name": "Set Sector",
      "type": "n8n-nodes-base.set",
      "position": [
        3184,
        880
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "73afd3dd-da46-4266-af04-f6d6dbd77af3",
              "name": "sector",
              "type": "string",
              "value": "={{ ($json.query || $json.chatInput || $json.ticker || '').toString().trim().toUpperCase() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8468d785-15d1-4f60-9a69-8a185689e0fc",
      "name": "Get Sector Stocks",
      "type": "n8n-nodes-base.code",
      "position": [
        3408,
        880
      ],
      "parameters": {
        "jsCode": "// Dynamic sector stock seeding with known fallback data\n// Get Quote will enrich with live prices, sector, industry, marketcap, CEO etc.\nconst sectorData = {\n  'Technology': [\n    { ticker: 'AAPL', knownIndustry: 'Consumer Electronics', knownMarketCap: 3665126494685, knownCeo: 'Mr. Timothy D. Cook', knownEmployees: 166000, knownBeta: 1.093, knownPeRatio: 33.20 },\n    { ticker: 'MSFT', knownIndustry: 'Software - Infrastructure', knownMarketCap: 3461+1234567890, knownCeo: 'Mr. Satya Nadella', knownEmployees: 228000, knownBeta: 0.89, knownPeRatio: 34.5 },\n    { ticker: 'NVDA', knownIndustry: 'Semiconductors', knownMarketCap: 4586+1234567890, knownCeo: 'Mr. Jensen Huang', knownEmployees: 29600, knownBeta: 1.68, knownPeRatio: 52.3 },\n    { ticker: 'GOOGL', knownIndustry: 'Internet Content & Information', knownMarketCap: 2065+1234567890, knownCeo: 'Mr. Sundar Pichai', knownEmployees: 182502, knownBeta: 1.04, knownPeRatio: 24.8 },\n    { ticker: 'META', knownIndustry: 'Social Media', knownMarketCap: 1674+1234567890, knownCeo: 'Mr. Mark Zuckerberg', knownEmployees: 86482, knownBeta: 1.21, knownPeRatio: 28.7 }\n  ],\n  'Healthcare': [\n    { ticker: 'JNJ', knownIndustry: 'Drug Manufacturers - General', knownMarketCap: 38+1234567890, knownCeo: 'Mr. Joaquin Duato', knownEmployees: 130800, knownBeta: 0.56, knownPeRatio: 15.4 },\n    { ticker: 'UNH', knownIndustry: 'Healthcare Plans', knownMarketCap: 545+1234567890, knownCeo: 'Mr. Andrew Witty', knownEmployees: 440000, knownBeta: 0.73, knownPeRatio: 25.1 },\n    { ticker: 'LLY', knownIndustry: 'Drug Manufacturers - General', knownMarketCap: 885+1234567890, knownCeo: 'Mr. David A. Ricks', knownEmployees: 43000, knownBeta: 0.48, knownPeRatio: 62.8 },\n    { ticker: 'PFE', knownIndustry: 'Drug Manufacturers - General', knownMarketCap: 156+1234567890, knownCeo: 'Dr. Albert Bourla', knownEmployees: 83000, knownBeta: 0.61, knownPeRatio: 12.1 },\n    { ticker: 'ABBV', knownIndustry: 'Drug Manufacturers - General', knownMarketCap: 32+1234567890, knownCeo: 'Mr. Richard A. Gonzalez', knownEmployees: 50000, knownBeta: 0.70, knownPeRatio: 18.3 }\n  ],\n  'Finance': [\n    { ticker: 'JPM', knownIndustry: 'Banks - Diversified', knownMarketCap: 7+1234567890, knownCeo: 'Mr. Jamie Dimon', knownEmployees: 309926, knownBeta: 1.10, knownPeRatio: 13.2 },\n    { ticker: 'BAC', knownIndustry: 'Banks - Diversified', knownMarketCap: 35+1234567890, knownCeo: 'Mr. Brian Moynihan', knownEmployees: 217000, knownBeta: 1.35, knownPeRatio: 14.1 },\n    { ticker: 'WFC', knownIndustry: 'Banks - Diversified', knownMarketCap: 26+1234567890, knownCeo: 'Mr. Charles W. Scharf', knownEmployees: 227608, knownBeta: 1.20, knownPeRatio: 13.8 },\n    { ticker: 'GS', knownIndustry: 'Capital Markets', knownMarketCap: 185+1234567890, knownCeo: 'Mr. David Solomon', knownEmployees: 46000, knownBeta: 1.40, knownPeRatio: 15.2 },\n    { ticker: 'MS', knownIndustry: 'Capital Markets', knownMarketCap: 175+1234567890, knownCeo: 'Mr. James P. Gorman', knownEmployees: 81000, knownBeta: 1.30, knownPeRatio: 16.4 }\n  ],\n  'Energy': [\n    { ticker: 'XOM', knownIndustry: 'Oil & Gas Integrated', knownMarketCap: 48+1234567890, knownCeo: 'Mr. Darren Woods', knownEmployees: 61500, knownBeta: 0.90, knownPeRatio: 14.5 },\n    { ticker: 'CVX', knownIndustry: 'Oil & Gas Integrated', knownMarketCap: 29+1234567890, knownCeo: 'Mr. Michael K. Wirth', knownEmployees: 42595, knownBeta: 0.88, knownPeRatio: 15.2 },\n    { ticker: 'COP', knownIndustry: 'Oil & Gas E&P', knownMarketCap: 138+1234567890, knownCeo: 'Mr. Ryan Lance', knownEmployees: 9500, knownBeta: 0.95, knownPeRatio: 13.1 },\n    { ticker: 'SLB', knownIndustry: 'Oil & Gas Equipment & Services', knownMarketCap: 72+1234567890, knownCeo: 'Mr. Olivier Le Peuch', knownEmployees: 95000, knownBeta: 1.25, knownPeRatio: 16.8 },\n    { ticker: 'EOG', knownIndustry: 'Oil & Gas E&P', knownMarketCap: 68+1234567890, knownCeo: 'Mr. Ezra Yacob', knownEmployees: 2900, knownBeta: 0.93, knownPeRatio: 11.4 }\n  ],\n  'Consumer': [\n    { ticker: 'AMZN', knownIndustry: 'Internet Retail', knownMarketCap: 23+1234567890, knownCeo: 'Mr. Andrew R. Jassy', knownEmployees: 1540000, knownBeta: 1.15, knownPeRatio: 45.2 },\n    { ticker: 'TSLA', knownIndustry: 'Auto Manufacturers', knownMarketCap: 11+1234567890, knownCeo: 'Mr. Elon Musk', knownEmployees: 140473, knownBeta: 2.30, knownPeRatio: 80.1 },\n    { ticker: 'HD', knownIndustry: 'Home Improvement Retail', knownMarketCap: 4+1234567890, knownCeo: 'Mr. Edward P. Decker', knownEmployees: 465000, knownBeta: 1.00, knownPeRatio: 26.3 },\n    { ticker: 'NKE', knownIndustry: 'Footwear & Accessories', knownMarketCap: 95+1234567890, knownCeo: 'Mr. John Donahoe', knownEmployees: 83700, knownBeta: 0.98, knownPeRatio: 22.5 },\n    { ticker: 'MCD', knownIndustry: 'Restaurants', knownMarketCap: 22+1234567890, knownCeo: 'Mr. Christopher J. Kempczinski', knownEmployees: 200000, knownBeta: 0.72, knownPeRatio: 24.1 }\n  ],\n  'Real Estate': [\n    { ticker: 'PLD', knownIndustry: 'REIT - Industrial', knownMarketCap: 105+1234567890, knownCeo: 'Mr. Hamid R. Moghadam', knownEmployees: 1800, knownBeta: 1.00, knownPeRatio: 35.2 },\n    { ticker: 'AMT', knownIndustry: 'REIT - Specialty', knownMarketCap: 92+1234567890, knownCeo: 'Mr. Tom Bartlett', knownEmployees: 5200, knownBeta: 0.78, knownPeRatio: 40.1 },\n    { ticker: 'EQIX', knownIndustry: 'REIT - Specialty', knownMarketCap: 75+1234567890, knownCeo: 'Mr. Charles Meyers', knownEmployees: 13000, knownBeta: 0.85, knownPeRatio: 88.3 },\n    { ticker: 'SPG', knownIndustry: 'REIT - Retail', knownMarketCap: 58+1234567890, knownCeo: 'Mr. David E. Simon', knownEmployees: 3100, knownBeta: 1.30, knownPeRatio: 22.4 },\n    { ticker: 'O', knownIndustry: 'REIT - Retail', knownMarketCap: 5+1234567890, knownCeo: 'Mr. Sumit Roy', knownEmployees: 500, knownBeta: 0.60, knownPeRatio: 38.2 }\n  ],\n  'Utilities': [\n    { ticker: 'NEE', knownIndustry: 'Utilities - Regulated Electric', knownMarketCap: 15+1234567890, knownCeo: 'Mr. John W. Ketchum', knownEmployees: 15000, knownBeta: 0.55, knownPeRatio: 22.1 },\n    { ticker: 'DUK', knownIndustry: 'Utilities - Regulated Electric', knownMarketCap: 82+1234567890, knownCeo: 'Mr. Harry Sideris', knownEmployees: 28000, knownBeta: 0.42, knownPeRatio: 19.3 },\n    { ticker: 'SO', knownIndustry: 'Utilities - Regulated Electric', knownMarketCap: 78+1234567890, knownCeo: 'Mr. Christopher C. Womack', knownEmployees: 28000, knownBeta: 0.45, knownPeRatio: 20.8 },\n    { ticker: 'AEP', knownIndustry: 'Utilities - Regulated Electric', knownMarketCap: 52+1234567890, knownCeo: 'Ms. Julie Sloat', knownEmployees: 17000, knownBeta: 0.48, knownPeRatio: 18.5 },\n    { ticker: 'EXC', knownIndustry: 'Utilities - Regulated Electric', knownMarketCap: 42+1234567890, knownCeo: 'Mr. Calvin G. Butler Jr.', knownEmployees: 20000, knownBeta: 0.50, knownPeRatio: 17.2 }\n  ],\n  'Industrials': [\n    { ticker: 'CAT', knownIndustry: 'Farm & Heavy Construction Machinery', knownMarketCap: 19+1234567890, knownCeo: 'Mr. D. James Umpleby III', knownEmployees: 109600, knownBeta: 0.95, knownPeRatio: 17.8 },\n    { ticker: 'BA', knownIndustry: 'Aerospace & Defense', knownMarketCap: 14+1234567890, knownCeo: 'Mr. Kelly Ortberg', knownEmployees: 150000, knownBeta: 1.35, knownPeRatio: 0 },\n    { ticker: 'HON', knownIndustry: 'Conglomerates', knownMarketCap: 13+1234567890, knownCeo: 'Mr. Vimal Kapur', knownEmployees: 99000, knownBeta: 1.05, knownPeRatio: 24.3 },\n    { ticker: 'UPS', knownIndustry: 'Integrated Freight & Logistics', knownMarketCap: 11+1234567890, knownCeo: 'Ms. Carol Tome', knownEmployees: 500000, knownBeta: 1.10, knownPeRatio: 18.9 },\n    { ticker: 'RTX', knownIndustry: 'Aerospace & Defense', knownMarketCap: 175+1234567890, knownCeo: 'Mr. Gregory J. Hayes', knownEmployees: 185000, knownBeta: 0.90, knownPeRatio: 35.1 }\n  ]\n};\n\nconst sector = $input.first().json.sector || 'Technology';\nconst stocks = sectorData[sector] || sectorData['Technology'];\n\nreturn stocks.map(stock => ({\n  json: {\n    ticker: stock.ticker,\n    requestedSector: sector,\n    knownIndustry: stock.knownIndustry,\n    knownMarketCap: stock.knownMarketCap,\n    knownCeo: stock.knownCeo,\n    knownEmployees: stock.knownEmployees,\n    knownBeta: stock.knownBeta,\n    knownPeRatio: stock.knownPeRatio\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "6734d2e4-b627-408d-90a0-9d0a87b6fc4e",
      "name": "Get Quote",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3632,
        784
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "requestOptions": {},
        "additionalOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1e082627-3fec-499d-81db-af7f7482bf36",
      "name": "Merge & Enrich Data",
      "type": "n8n-nodes-base.code",
      "position": [
        4080,
        880
      ],
      "parameters": {
        "jsCode": "// Merge & Enrich Data\n// Input is ONE combined object per stock containing both quote + statistics\nconst items = $input.all();\nconst sector = $('Set Sector').first().json.sector || 'Technology';\n\n// Pull knownData from Get Sector Stocks for fallback\nconst knownData = {};\n$('Get Sector Stocks').all().forEach(item => {\n  const d = item.json;\n  if (d.ticker) knownData[d.ticker.toUpperCase()] = d;\n});\n\nconst merged = items.map(item => {\n  const d = item.json;\n\n  // Skip errors\n  if (d.code === 403 || d.status === 'error') return null;\n\n  // Symbol\n  const sym = (d.symbol || d.meta?.symbol || '').toUpperCase();\n  if (!sym) return null;\n\n  const k = knownData[sym] || {};\n\n  // Statistics nested paths \u2014 all on same object\n  const stats        = d.statistics || {};\n  const valuations   = stats.valuations_metrics || {};\n  const financials   = stats.financials || {};\n  const stockStats   = stats.stock_price_summary || {};\n  const stockInfo    = stats.stock_statistics || {};\n  const income       = financials.income_statement || {};\n  const balanceSheet = financials.balance_sheet || {};\n  const cashFlow     = financials.cash_flow || {};\n  const dividends    = stats.dividends_and_splits || {};\n\n  // \u2500\u2500 Core fields \u2014 live first, fallback second \u2500\u2500\n  const marketCap  = parseFloat(valuations.market_capitalization || k.knownMarketCap || 0);\n  const peRatio    = parseFloat(valuations.trailing_pe || k.knownPeRatio || 0);\n  const beta       = parseFloat(stockStats.beta || k.knownBeta || 0);\n  const industry   = d.industry || k.knownIndustry || 'Unknown';\n  const ceo        = d.ceo || d.CEO || k.knownCeo || 'N/A';\n  const employees  = parseInt(d.employees || k.knownEmployees || 0);\n\n  return {\n    json: {\n      ticker: sym,\n      requestedSector: sector,\n      name: d.name || sym,\n      exchange: d.exchange || '',\n      currency: d.currency || 'USD',\n      sector: d.sector || sector,\n      industry,\n      ceo,\n      employees,\n      description: d.description || '',\n      website: d.website || '',\n\n      // \u2500\u2500 Live price data \u2500\u2500\n      price:         parseFloat(d.close || 0),\n      change:        parseFloat(d.change || 0),\n      percentChange: parseFloat(d.percent_change || 0),\n      open:          parseFloat(d.open || 0),\n      high:          parseFloat(d.high || 0),\n      low:           parseFloat(d.low || 0),\n      volume:        parseInt(d.volume || 0),\n      avgVolume:     parseInt(d.average_volume || 0),\n      previousClose: parseFloat(d.previous_close || 0),\n      isMarketOpen:  d.is_market_open || false,\n\n      // \u2500\u2500 52-week range \u2500\u2500\n      fiftyTwoWeekHigh: parseFloat(d.fifty_two_week?.high || stockStats.fifty_two_week_high || 0),\n      fiftyTwoWeekLow:  parseFloat(d.fifty_two_week?.low  || stockStats.fifty_two_week_low  || 0),\n\n      // \u2500\u2500 Valuation metrics \u2500\u2500\n      marketCap,\n      peRatio,\n      forwardPE:    parseFloat(valuations.forward_pe || 0),\n      pegRatio:     parseFloat(valuations.peg_ratio || 0),\n      priceToSales: parseFloat(valuations.price_to_sales_ttm || 0),\n      priceToBook:  parseFloat(valuations.price_to_book_mrq || 0),\n\n      // \u2500\u2500 Fundamentals \u2500\u2500\n      beta,\n      revenue:        parseFloat(income.revenue_ttm || 0),\n      netIncome:      parseFloat(income.net_income_to_common_ttm || 0),\n      eps:            parseFloat(income.diluted_eps_ttm || 0),\n      revenueGrowth:  parseFloat(income.quarterly_revenue_growth || 0),\n      earningsGrowth: parseFloat(income.quarterly_earnings_growth_yoy || 0),\n      grossMargin:    parseFloat(financials.gross_margin || 0),\n      profitMargin:   parseFloat(financials.profit_margin || 0),\n      operatingMargin:parseFloat(financials.operating_margin || 0),\n\n      // \u2500\u2500 Balance sheet \u2500\u2500\n      totalCash:  parseFloat(balanceSheet.total_cash_mrq || 0),\n      totalDebt:  parseFloat(balanceSheet.total_debt_mrq || 0),\n      debtToEquity: parseFloat(balanceSheet.total_debt_to_equity_mrq || 0),\n\n      // \u2500\u2500 Cash flow \u2500\u2500\n      operatingCashFlow: parseFloat(cashFlow.operating_cash_flow_ttm || 0),\n      freeCashFlow:      parseFloat(cashFlow.levered_free_cash_flow_ttm || 0),\n\n      // \u2500\u2500 Dividends \u2500\u2500\n      dividendYield: parseFloat(dividends.forward_annual_dividend_yield || 0),\n      dividendRate:  parseFloat(dividends.forward_annual_dividend_rate || 0),\n      payoutRatio:   parseFloat(dividends.payout_ratio || 0),\n\n      // \u2500\u2500 Moving averages \u2500\u2500\n      ma50:  parseFloat(stockStats.day_50_ma || 0),\n      ma200: parseFloat(stockStats.day_200_ma || 0),\n\n      // \u2500\u2500 Share stats \u2500\u2500\n      sharesOutstanding: parseInt(stockInfo.shares_outstanding || 0),\n      sharesFloat:       parseInt(stockInfo.float_shares || 0),\n      shortRatio:        parseFloat(stockInfo.short_ratio || 0),\n      institutionalHeld: parseFloat(stockInfo.percent_held_by_institutions || 0)\n    }\n  };\n}).filter(Boolean); // remove any nulls from errors\n\nmerged.sort((a, b) => b.json.marketCap - a.json.marketCap);\nreturn merged.slice(0, 5);\n"
      },
      "typeVersion": 2
    },
    {
      "id": "34c4e8b8-2826-4c9a-a5bf-787f5b49b02e",
      "name": "Analyze Sector",
      "type": "n8n-nodes-base.code",
      "position": [
        4304,
        880
      ],
      "parameters": {
        "jsCode": "const stocks = $input.all().map(i => i.json);\nconst requestedSector = stocks[0]?.requestedSector || 'Unknown';\n\n// Sort by % change for performance ranking\nstocks.sort((a, b) => b.percentChange - a.percentChange);\n\n// \u2500\u2500 Sector-wide calculations \u2500\u2500\nconst avgChange      = stocks.reduce((sum, s) => sum + s.percentChange, 0) / stocks.length;\nconst totalMarketCap = stocks.reduce((sum, s) => sum + s.marketCap, 0);\nconst totalRevenue   = stocks.reduce((sum, s) => sum + s.revenue, 0);\nconst totalEmployees = stocks.reduce((sum, s) => sum + s.employees, 0);\n\nconst validPE   = stocks.filter(s => s.peRatio > 0);\nconst avgPE     = validPE.length > 0 ? validPE.reduce((sum, s) => sum + s.peRatio, 0) / validPE.length : 0;\n\nconst validBeta = stocks.filter(s => s.beta > 0);\nconst avgBeta   = validBeta.length > 0 ? validBeta.reduce((sum, s) => sum + s.beta, 0) / validBeta.length : 0;\n\nconst avgProfitMargin  = stocks.reduce((sum, s) => sum + s.profitMargin, 0) / stocks.length;\nconst avgRevenueGrowth = stocks.reduce((sum, s) => sum + s.revenueGrowth, 0) / stocks.length;\n\nconst gainers   = stocks.filter(s => s.percentChange > 0).length;\nconst losers    = stocks.filter(s => s.percentChange < 0).length;\nconst unchanged = stocks.length - gainers - losers;\n\nconst industries = [...new Set(stocks.map(s => s.industry))];\n\n// \u2500\u2500 Sector strength rating \u2500\u2500\nconst sectorStrength =\n  avgChange > 2   ? 'Very Strong \ud83d\ude80' :\n  avgChange > 0.5 ? 'Strong \ud83d\udcc8'      :\n  avgChange > -0.5? 'Neutral \u27a1\ufe0f'     :\n  avgChange > -2  ? 'Weak \ud83d\udcc9'        : 'Very Weak \u26a0\ufe0f';\n\n// \u2500\u2500 Helper: format numbers \u2500\u2500\nconst fmt    = (n, d=2) => (n !== undefined && n !== null && !isNaN(n)) ? Number(n).toFixed(d) : 'N/A';\nconst fmtB   = (n)      => (n > 0) ? `$${(n / 1e9).toFixed(1)}B` : 'N/A';\nconst fmtT   = (n)      => (n > 0) ? `$${(n / 1e12).toFixed(2)}T` : 'N/A';\nconst fmtPct = (n)      => (n !== undefined && !isNaN(n)) ? `${(n * 100).toFixed(1)}%` : 'N/A';\nconst fmtChg = (n)      => (n >= 0 ? '+' : '') + fmt(n) + '%';\n\n// \u2500\u2500 Investment recommendation \u2500\u2500\nconst top    = stocks[0];\nconst second = stocks[1];\nconst worst  = stocks[stocks.length - 1];\n\nconst recommendation =\n  avgChange > 1.5\n    ? `\ud83d\udfe2 BULLISH OUTLOOK\n${requestedSector.toUpperCase()} sector showing strong momentum with average gain of ${fmtChg(avgChange)}.\nTop Picks: ${top?.ticker} (${fmtChg(top?.percentChange)}), ${second?.ticker} (${fmtChg(second?.percentChange)})\nStrategy: Consider sector rotation into ${requestedSector} for momentum play.`\n    : avgChange > 0\n    ? `\ud83d\udfe1 MIXED OUTLOOK\n${requestedSector.toUpperCase()} sector showing modest performance (${fmtChg(avgChange)}).\nSelective Opportunity: ${top?.ticker} showing relative strength at ${fmtChg(top?.percentChange)}.\nStrategy: Stock-picking approach recommended. Focus on ${top?.industry} subsector.`\n    : `\ud83d\udd34 BEARISH OUTLOOK\n${requestedSector.toUpperCase()} sector under pressure with average decline of ${fmtChg(avgChange)}.\nWeakness: Broad-based selling across ${losers} of ${stocks.length} stocks.\nStrategy: Defensive positioning recommended. Wait for reversal signals before entry.`;\n\n// \u2500\u2500 Build formatted report \u2500\u2500\nconst formatted = `\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n   SECTOR ANALYSIS: ${requestedSector.toUpperCase()}\n   Generated: ${new Date().toISOString()}\n   Source: Twelve Data API (Live)\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n\n\ud83d\udcca SECTOR OVERVIEW\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nSector:              ${requestedSector}\nStocks Analysed:     ${stocks.length} (Top 5 by Market Cap)\nCombined Mkt Cap:    ${fmtT(totalMarketCap)}\nCombined Revenue:    ${fmtT(totalRevenue)}\nTotal Employees:     ${totalEmployees.toLocaleString()}\nAverage Change:      ${fmtChg(avgChange)}\nSector Strength:     ${sectorStrength}\nAvg P/E Ratio:       ${avgPE > 0 ? fmt(avgPE, 1) : 'N/A'}\nAvg Beta:            ${avgBeta > 0 ? fmt(avgBeta) : 'N/A'}\nAvg Profit Margin:   ${fmtPct(avgProfitMargin)}\nAvg Revenue Growth:  ${fmtPct(avgRevenueGrowth)}\n\n\ud83c\udfc6 TOP PERFORMERS\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${stocks.slice(0, 3).map((s, i) => `${i + 1}. ${s.ticker} \u2014 ${s.name}\n   Industry:          ${s.industry}\n   Price:             $${fmt(s.price)} (${fmtChg(s.percentChange)})\n   Market Cap:        ${fmtB(s.marketCap)}\n   Revenue:           ${fmtB(s.revenue)}\n   P/E (TTM):         ${fmt(s.peRatio, 1)} | Forward P/E: ${fmt(s.forwardPE, 1)}\n   EPS:               $${fmt(s.eps)}\n   Beta:              ${fmt(s.beta)}\n   52W Range:         $${fmt(s.fiftyTwoWeekLow)} \u2013 $${fmt(s.fiftyTwoWeekHigh)}\n   MA50/MA200:        $${fmt(s.ma50)} / $${fmt(s.ma200)}\n   Profit Margin:     ${fmtPct(s.profitMargin)}\n   Revenue Growth:    ${fmtPct(s.revenueGrowth)}\n   Earnings Growth:   ${fmtPct(s.earningsGrowth)}\n   Free Cash Flow:    ${fmtB(s.freeCashFlow)}\n   Dividend Yield:    ${s.dividendYield > 0 ? fmtPct(s.dividendYield) : 'None'}\n   CEO:               ${s.ceo}\n   Employees:         ${s.employees?.toLocaleString()}`\n).join('\\n\\n')}\n\n\ud83d\udcc9 BOTTOM PERFORMERS\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${stocks.slice(-2).reverse().map(s => `\u2022 ${s.ticker} \u2014 ${s.name}\n  Price: $${fmt(s.price)} (${fmtChg(s.percentChange)}) | Mkt Cap: ${fmtB(s.marketCap)} | P/E: ${fmt(s.peRatio, 1)}`).join('\\n')}\n\n\ud83c\udfed SECTOR COMPOSITION BY INDUSTRY\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${industries.map(ind => {\n  const indStocks = stocks.filter(s => s.industry === ind);\n  const indAvg = indStocks.reduce((sum, s) => sum + s.percentChange, 0) / indStocks.length;\n  const indCap = indStocks.reduce((sum, s) => sum + s.marketCap, 0);\n  return `${ind}\n  Stocks: ${indStocks.map(s => s.ticker).join(', ')} | Avg Change: ${fmtChg(indAvg)} | Cap: ${fmtB(indCap)}`;\n}).join('\\n')}\n\n\ud83d\udcc8 PERFORMANCE DISTRIBUTION\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nGainers:   ${gainers} stock${gainers !== 1 ? 's' : ''}\nLosers:    ${losers} stock${losers !== 1 ? 's' : ''}\nUnchanged: ${unchanged} stock${unchanged !== 1 ? 's' : ''}\nBest:      ${top?.ticker} \u2014 ${top?.name} (${fmtChg(top?.percentChange)})\nWorst:     ${worst?.ticker} \u2014 ${worst?.name} (${fmtChg(worst?.percentChange)})\n\n\ud83d\udcbc INVESTMENT RECOMMENDATION\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n${recommendation}\n\nTop Pick:  ${top?.ticker} \u2014 ${top?.name}\n           Price: $${fmt(top?.price)} | P/E: ${fmt(top?.peRatio, 1)} | Beta: ${fmt(top?.beta)}\n\nAvoid:     ${worst?.ticker} \u2014 ${worst?.name}\n           Price: $${fmt(worst?.price)} | P/E: ${fmt(worst?.peRatio, 1)} | Beta: ${fmt(worst?.beta)}\n`;\n\nreturn [{\n  json: {\n    sector: requestedSector,\n    sectorStrength,\n    avgChange,\n    totalMarketCap,\n    totalRevenue,\n    totalEmployees,\n    avgPE,\n    avgBeta,\n    avgProfitMargin,\n    avgRevenueGrowth,\n    stockCount: stocks.length,\n    gainers,\n    losers,\n    unchanged,\n    industries,\n    topPerformer: top || null,\n    worstPerformer: worst || null,\n    stocks,\n    response: formatted\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4419b861-1932-4f0f-bc53-c5fe2810c065",
      "name": "Merge2",
      "type": "n8n-nodes-base.merge",
      "position": [
        3856,
        880
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "988626e1-9857-4660-91e4-71ee3c6a9a08",
      "name": "Get Statistics1",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3632,
        976
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "fundamentals",
        "operation": "getStatistics",
        "requestOptions": {},
        "fundamentalsOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7b5b70df-193c-46c7-867c-70ce4f360e1d",
      "name": "Output2",
      "type": "n8n-nodes-base.set",
      "position": [
        4528,
        880
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8abfef83-9577-4b54-9326-8d8e99b086a5",
              "name": "response",
              "type": "string",
              "value": "={{ $json.response }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6b538ff3-02bf-466a-a988-2fe4c785ac70",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1872,
        1376
      ],
      "parameters": {
        "color": 4,
        "width": 3472,
        "height": 1576,
        "content": "## Sector Analysis Tool\n### Elite Financial Analysis \u2014 Ade AI\n\n\n## TwelveData_Pro_Helper (Stock Analysis Tool)\n\n- Triggered by a parent workflow passing a single stock ticker symbol\n- Fetches **7 data streams in parallel** from Twelve Data: real-time quote, 90-day price time series, RSI, MACD, Bollinger Bands, company profile, and income statement\n- Merges all 7 streams into a single object, then calculates:\n  - **5-day price momentum** vs the prior 5-day average (Strong Bullish \u2192 Strong Bearish)\n  - **Volume trend** \u2014 recent 5-day average vs prior 5-day average\n  - **RSI signal** (Overbought / Oversold / Neutral)\n  - **MACD direction** (Bullish / Bearish based on histogram)\n  - **Bollinger Band position** (Above Upper / Below Lower / Inside Bands)\n  - **Revenue & net income YoY growth** from the two most recent fiscal years\n- Formats all data into a structured plain-text prompt including company overview, live market data, technical readings, fundamentals, and a 10-day price history table\n- Passes the formatted prompt to **GPT-4o** with a detailed institutional analyst system prompt instructing it to perform a pre-analysis scan before writing,\n then produce a structured report covering: Technical Analysis, Fundamental Assessment, Risk Assessment (with support/resistance/stop-loss), \nand a Final Verdict table with BUY/HOLD/SELL, price target, timeframe, conviction level and 3 data-backed reasons\n- Returns the AI-generated analysis plus the ticker back to the calling workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "d7c23b21-2392-47d1-a5d6-a33600b6a008",
      "name": "Set Stock Ticker",
      "type": "n8n-nodes-base.set",
      "position": [
        3152,
        2080
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "08c75286-f81d-4665-b72f-092906af4997",
              "name": "ticker",
              "type": "string",
              "value": "={{ ($json.query || $json.chatInput || $json.ticker || '').toString().trim().toUpperCase() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8b221d9b-1ff2-4d56-ad73-5ae5de6cdb07",
      "name": "Get Real-Time Quote",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3680,
        1408
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "requestOptions": {},
        "additionalOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "47980839-b498-491a-84ba-04d64cd73cf8",
      "name": "Get Time Series (90 days)",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3680,
        1632
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "operation": "getTimeSeries",
        "requestOptions": {},
        "additionalOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "48347bba-8aad-425b-8962-82fa9a495064",
      "name": "RSI Indicator",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3680,
        1856
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "technicalIndicators",
        "operation": "rsi",
        "requestOptions": {},
        "indicatorOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3d6214b4-950f-4764-9066-7ff7f0b3766d",
      "name": "MACD Indicator",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3680,
        2080
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "technicalIndicators",
        "operation": "macd",
        "requestOptions": {},
        "indicatorOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2d4e439d-8c07-402d-bc3b-a2cac28b1239",
      "name": "Bollinger Bands",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3680,
        2304
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "technicalIndicators",
        "operation": "bbands",
        "requestOptions": {},
        "indicatorOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5db304ed-5e01-4fff-9f48-0a25ea4fc9c8",
      "name": "Get Company Profile",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3680,
        2528
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "fundamentals",
        "requestOptions": {},
        "fundamentalsOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4f73ff69-42b4-43d4-8555-c15c8667ad4d",
      "name": "Get Income Statement",
      "type": "n8n-nodes-twelve-data.twelveData",
      "position": [
        3680,
        2752
      ],
      "parameters": {
        "symbol": "={{ $json.ticker }}",
        "resource": "fundamentals",
        "operation": "getIncomeStatement",
        "requestOptions": {},
        "fundamentalsOptions": {}
      },
      "credentials": {
        "twelveDataApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "61547754-81f5-415c-99d0-104eeedd6709",
      "name": "Merge All Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        4208,
        2048
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition",
        "numberInputs": 7
      },
      "typeVersion": 3,
      "alwaysOutputData": true
    },
    {
      "id": "3c40f494-839a-4839-a960-b25518f6ca60",
      "name": "Format Data for AI",
      "type": "n8n-nodes-base.code",
      "position": [
        4416,
        2128
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\n// \u2500\u2500 QUOTE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst quote = {\n  symbol:           data.symbol,\n  price:            parseFloat(data.close),\n  change:           parseFloat(data.change),\n  changePercent:    parseFloat(data.percent_change),\n  volume:           parseInt(data.volume),\n  avgVolume:        parseInt(data.average_volume),\n  prevClose:        parseFloat(data.previous_close),\n  high52w:          parseFloat(data.fifty_two_week?.high),\n  low52w:           parseFloat(data.fifty_two_week?.low),\n  isMarketOpen:     data.is_market_open\n};\n\n// \u2500\u2500 COMPANY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst company = {\n  name:        data.name,\n  ceo:         data.CEO,\n  sector:      data.sector,\n  industry:    data.industry,\n  employees:   data.employees,\n  website:     data.website,\n  description: data.description,\n  address:     `${data.address}, ${data.city}, ${data.state} ${data.zip}, ${data.country}`\n};\n\n// \u2500\u2500 TECHNICALS (most recent day = values[0]) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst t = data.values[0];\nconst technicals = {\n  date:         t.datetime,\n  rsi:          parseFloat(t.rsi),\n  macd:         parseFloat(t.macd),\n  macdSignal:   parseFloat(t.macd_signal),\n  macdHist:     parseFloat(t.macd_hist),\n  upperBand:    parseFloat(t.upper_band),\n  middleBand:   parseFloat(t.middle_band),\n  lowerBand:    parseFloat(t.lower_band),\n  rsiSignal:    parseFloat(t.rsi) > 70 ? 'OVERBOUGHT' : parseFloat(t.rsi) < 30 ? 'OVERSOLD' : 'NEUTRAL',\n  macdSignalDir: parseFloat(t.macd_hist) > 0 ? 'BULLISH' : 'BEARISH',\n  bbPosition:   parseFloat(data.close) > parseFloat(t.upper_band) ? 'ABOVE UPPER' :\n                parseFloat(data.close) < parseFloat(t.lower_band) ? 'BELOW LOWER' : 'INSIDE BANDS'\n};\n\n// \u2500\u2500 PRICE MOMENTUM (5-day vs prior 5-day) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst recent5  = data.values.slice(0, 5).map(d => parseFloat(d.close));\nconst prior5   = data.values.slice(5, 10).map(d => parseFloat(d.close));\nconst recentAvg = recent5.reduce((a, b) => a + b) / 5;\nconst priorAvg  = prior5.reduce((a, b) => a + b) / 5;\nconst momentum  = ((recentAvg - priorAvg) / priorAvg) * 100;\nconst momentumLabel = momentum > 5 ? 'Strong Bullish' : momentum > 2 ? 'Bullish' :\n                      momentum > -2 ? 'Neutral' : momentum > -5 ? 'Bearish' : 'Strong Bearish';\n\n// \u2500\u2500 VOLUME TREND \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst recentVol = data.values.slice(0, 5).map(d => parseInt(d.volume));\nconst priorVol  = data.values.slice(5, 10).map(d => parseInt(d.volume));\nconst recentVolAvg = recentVol.reduce((a, b) => a + b) / 5;\nconst priorVolAvg  = priorVol.reduce((a, b) => a + b) / 5;\nconst volChange = ((recentVolAvg - priorVolAvg) / priorVolAvg) * 100;\nconst volLabel = volChange > 20 ? 'Increasing Significantly' : volChange > 5 ? 'Increasing' :\n                 volChange > -5 ? 'Stable' : volChange > -20 ? 'Decreasing' : 'Decreasing Significantly';\n\n// \u2500\u2500 FUNDAMENTALS (most recent year = [0]) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst inc = data.income_statement[0];\nconst prev = data.income_statement[1];\nconst revenueGrowth = ((inc.sales - prev.sales) / prev.sales * 100).toFixed(2);\nconst netIncomeGrowth = ((inc.net_income - prev.net_income) / prev.net_income * 100).toFixed(2);\n\nconst fundamentals = {\n  fiscalYear:       inc.fiscal_date,\n  revenue:          inc.sales,\n  grossProfit:      inc.gross_profit,\n  operatingIncome:  inc.operating_income,\n  netIncome:        inc.net_income,\n  ebitda:           inc.ebitda,\n  epsBasic:         inc.eps_basic,\n  epsDiluted:       inc.eps_diluted,\n  revenueGrowthYoY: `${revenueGrowth}%`,\n  netIncomeGrowthYoY: `${netIncomeGrowth}%`,\n  rd:               inc.operating_expense?.research_and_development\n};\n\n// \u2500\u2500 30-DAY PRICE HISTORY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst priceHistory = data.values.map(d => ({\n  date:       d.datetime,\n  close:      parseFloat(d.close),\n  volume:     parseInt(d.volume),\n  rsi:        parseFloat(d.rsi),\n  macd:       parseFloat(d.macd),\n  upperBand:  parseFloat(d.upper_band),\n  lowerBand:  parseFloat(d.lower_band)\n}));\n\n// \u2500\u2500 FORMATTED PROMPT FOR AI \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst formattedData = `\nCOMPREHENSIVE STOCK ANALYSIS \u2014 ${quote.symbol}\nGenerated: ${new Date().toISOString()}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nCOMPANY OVERVIEW\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nCompany:    ${company.name}\nCEO:        ${company.ceo}\nSector:     ${company.sector}\nIndustry:   ${company.industry}\nEmployees:  ${company.employees?.toLocaleString()}\nWebsite:    ${company.website}\nAddress:    ${company.address}\n${company.description}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nCURRENT MARKET DATA\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPrice:          $${quote.price}\nChange:         $${quote.change} (${quote.changePercent}%)\nPrevious Close: $${quote.prevClose}\nVolume:         ${quote.volume?.toLocaleString()}\nAvg Volume:     ${quote.avgVolume?.toLocaleString()}\n52-Week High:   $${quote.high52w}\n52-Week Low:    $${quote.low52w}\nMarket Open:    ${quote.isMarketOpen ? 'Yes' : 'No'}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nTECHNICAL INDICATORS (${technicals.date})\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nRSI (14):         ${technicals.rsi} \u2192 ${technicals.rsiSignal}\nMACD:             ${technicals.macd}\nMACD Signal:      ${technicals.macdSignal}\nMACD Histogram:   ${technicals.macdHist} \u2192 ${technicals.macdSignalDir}\nBollinger Upper:  $${technicals.upperBand}\nBollinger Middle: $${technicals.middleBand}\nBollinger Lower:  $${technicals.lowerBand}\nPrice vs BB:      ${technicals.bbPosition}\n\nPrice Momentum (5d):  ${momentumLabel} (${momentum.toFixed(2)}%)\nVolume Trend (5d):    ${volLabel} (${volChange.toFixed(2)}%)\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nFUNDAMENTALS \u2014 FY ${fundamentals.fiscalYear}\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nRevenue:          $${(fundamentals.revenue / 1e9).toFixed(2)}B  (YoY: ${fundamentals.revenueGrowthYoY})\nGross Profit:     $${(fundamentals.grossProfit / 1e9).toFixed(2)}B\nOperating Income: $${(fundamentals.operatingIncome / 1e9).toFixed(2)}B\nNet Income:       $${(fundamentals.netIncome / 1e9).toFixed(2)}B  (YoY: ${fundamentals.netIncomeGrowthYoY})\nEBITDA:           $${(fundamentals.ebitda / 1e9).toFixed(2)}B\nEPS (Basic):      $${fundamentals.epsBasic}\nEPS (Diluted):    $${fundamentals.epsDiluted}\nR&D Spend:        $${(fundamentals.rd / 1e9).toFixed(2)}B\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n30-DAY PRICE HISTORY (10 Most Recent)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n${priceHistory.slice(0, 10).map(d =>\n  `${d.date}: Close $${d.close} | RSI ${d.rsi} | Vol ${d.volume?.toLocaleString()}`\n).join('\\n')}\n`;\n\nreturn {\n  json: {\n    ticker:        quote.symbol,\n    formattedData: formattedData,\n    quote,\n    company,\n    technicals,\n    fundamentals,\n    priceHistory,\n    momentum:      { label: momentumLabel, value: momentum.toFixed(2) },\n    volumeTrend:   { label: volLabel, value: volChange.toFixed(2) }\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "10788863-1d94-4a23-8fc7-ca1723f815c2",
      "name": "AI Analysis",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        4640,
        2128
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "GPT-4O"
        },
        "options": {
          "topP": 1,
          "maxTokens": 4000,
          "temperature": 0.3
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "=You are ADE (Advanced Data Engine) \u2014 an institutional-grade stock analyst with deep expertise in technical pattern recognition, fundamental valuation, and risk-adjusted portfolio strategy. You analyse with the rigour of a hedge fund PM and communicate with the clarity of a Bloomberg senior analyst.\n\nYour analysis must be grounded entirely in the data provided. Never reference indicators or metrics that are not present in the data. Every price level, every signal, every recommendation must trace back to a specific number in the report.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPRE-ANALYSIS PROTOCOL (do this silently first)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nBefore writing a single word of output, scan the full 30-day price history and note:\n1. RSI trajectory \u2014 was RSI rising or falling into today's reading? When was the last extreme (>70 or <30)?\n2. MACD history \u2014 when did MACD last cross the signal line? Is the histogram expanding or contracting?\n3. Bollinger Band width \u2014 are bands squeezing (narrowing) or expanding? Squeeze = breakout incoming\n4. Volume spikes \u2014 identify any days with volume significantly above average and note price direction that day\n5. Price structure \u2014 identify the highest close and lowest close in the 30-day window as resistance and support\n6. 52-week positioning \u2014 calculate where current price sits as a % between 52-week low and high\n\nOnly after completing this scan should you begin writing your output.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nANALYTICAL FRAMEWORK\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nTECHNICAL ANALYSIS (40% weight)\nUse historical context, not just the current reading:\n- RSI: Current value + 30-day trend + last extreme + what the recovery/deterioration pattern suggests\n- MACD: Current reading + last crossover direction + histogram trend (expanding = strengthening, contracting = weakening momentum)\n- Bollinger Bands: Band width trend + price position + squeeze detection + breakout probability\n- Volume: 5-day average vs full 30-day average + any institutional accumulation/distribution signals\n\nFUNDAMENTAL ANALYSIS (30% weight)\nUse only the metrics present in the data:\n- Revenue growth quality: Is 6-year trend accelerating or decelerating?\n- Margin expansion: Is net income growing faster than revenue? Calculate net margin.\n- EBITDA margin: Calculate from provided EBITDA and Revenue\n- EPS trajectory: Growth rate + what it implies for shareholder value\n- R&D investment: Calculate as % of revenue + comment on innovation commitment\n\nRISK ASSESSMENT (20% weight)\n- Risk Score 1-10 (define clearly: 1=minimal risk, 10=extreme risk)\n- Support level: use the lowest close in the 30-day price history\n- Resistance level: use the highest close in the 30-day price history\n- Stop-loss: set below the identified support level with specific reasoning\n- Maximum downside: % from current price to stop-loss\n\nMARKET SENTIMENT (10% weight)\n- 5-day volume vs 30-day volume average (calculate both)\n- Price momentum direction and strength\n- Overall positioning assessment\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nOUTPUT FORMAT \u2014 MANDATORY, DO NOT DEVIATE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n## \ud83d\udcca [TICKER] STOCK ANALYSIS REPORT\n**Analyst:** ADE | **Date:** [date from data] | **Price:** $[current price]\n\n---\n\n### \ud83c\udfe2 COMPANY SNAPSHOT\n[2 sentences max \u2014 what they do and why it matters to investors right now]\n\n---\n\n### \ud83d\udcc8 TECHNICAL ANALYSIS\n\n**RSI (14): [value] \u2014 [NEUTRAL/OVERBOUGHT/OVERSOLD]**\n[Comment on current reading, 30-day trajectory, and last extreme reached with date]\n\n**MACD: [BULLISH/BEARISH]**\n[Comment on last crossover, current histogram value, and whether momentum is strengthening or weakening]\n\n**Bollinger Bands**\n[Comment on band position, whether bands are squeezing or expanding, and breakout implication]\n\n**Volume Analysis**\n[5-day avg vs 30-day avg with calculated numbers \u2014 comment on accumulation or distribution pattern]\n\n**Key Price Levels**\n- \ud83d\udcd7 Support: $[lowest close from 30-day history] ([date])\n- \ud83d\udcd5 Resistance: $[highest close from 30-day history] ([date])\n- \ud83d\udccd 52-Week Range Position: [X]% above 52-week low | [Y]% below 52-week high\n\n---\n\n### \ud83d\udcb0 FUNDAMENTAL ASSESSMENT\n\n**Revenue:** $[X]B | YoY Growth: [X]% | Trend: [Accelerating/Decelerating/Stable]\n**Net Income:** $[X]B | YoY Growth: [X]% | Net Margin: [calculated]%\n**EBITDA Margin:** [calculated]%\n**EPS (Diluted):** $[X] | Growth: [comment]\n**R&D Investment:** $[X]B ([calculated]% of revenue) | [Innovation commitment comment]\n\n[2-3 sentence narrative on overall fundamental health]\n\n---\n\n### \u26a0\ufe0f RISK ASSESSMENT\n\n**Risk Score: [X]/10 \u2014 [LOW / MEDIUM / HIGH / VERY HIGH]**\n\n| Risk Factor | Detail |\n|---|---|\n| \ud83d\udd34 Primary Risk | [specific, data-backed] |\n| \ud83d\udfe1 Secondary Risk | [specific, data-backed] |\n| \ud83d\udfe2 Key Upside | [specific, data-backed] |\n\n- **Support Level:** $[price] | **Stop-Loss:** $[price] ([X]% below current)\n- **Max Downside to Stop:** [X]%\n\n---\n\n### \ud83c\udfaf FINAL VERDICT\n\n| Decision | Price Target | Timeframe | Conviction |\n|---|---|---|---|\n| **[BUY / HOLD / SELL]** | $[price] | [X weeks/months] | [HIGH / MEDIUM / LOW] |\n\n**Position Size:** [X]% of portfolio\n\n**Thesis \u2014 3 Data-Backed Reasons:**\n1. [Specific reason with numbers from the report]\n2. [Specific reason with numbers from the report]\n3. [Specific reason with numbers from the report]\n\n**Action Plan:**\n1. [Entry/exit instruction with specific price level]\n2. [Stop-loss execution instruction]\n3. [What metric to monitor and at what threshold to reassess]\n\n---\n*\u26a0\ufe0f This report is generated by ADE for informational purposes only. It does not constitute financial advice. Past performance is not indicative of future results. Always conduct independent research and assess your personal risk tolerance before making investment decisions.*\n"
            },
            {
              "content": "={{ $json.formattedData }}"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "590bb634-b05a-42b9-99aa-65d00fb12e43",
      "name": "Response Output",
      "type": "n8n-nodes-base.set",
      "position": [
        5040,
        2128
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ec15bd68-ba6f-429b-bb67-1cbfab5106bc",
              "name": "response",
              "type": "string",
              "value": "={{ $json.message.content }}"
            },
            {
              "id": "9b863d18-bcdf-4939-bbff-ef09f098e2a6",
              "name": "ticker",
              "type": "string",
              "value": "={{ $('Set Stock Ticker').item.json.ticker }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c6efbd47-ca29-4023-bd01-216efdd96d20",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -288
      ],
      "parameters": {
        "color": 4,
        "width": 2408,
        "height": 856,
        "content": ""
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "5367cad7-76f8-489d-ad70-b685835aa33a",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Calculate Risk Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge2": {
      "main": [
        [
          {
            "node": "Merge & Enrich Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger": {
      "main": [
        [
          {
            "node": "Get Historical Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Statistics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get RSI1": {
      "main": [
        [
          {
            "node": "Merge Stock Data1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "AI Agent1": {
      "main": [
        [
          {
            "node": "Send Response1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Quote": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Sector": {
      "main": [
        [
          {
            "node": "Get Sector Stocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Analysis": {
      "main": [
        [
          {
            "node": "Response Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Quotes1": {
      "main": [
        [
          {
            "node": "Merge Stock Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Profile1": {
      "main": [
        [
          {
            "node": "Merge Stock Data1",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "RSI Indicator": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Analyze Sector": {
      "main": [
        [
          {
            "node": "Output2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Statistics": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "MACD Indicator": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Merge All Data": {
      "main": [
        [
          {
            "node": "Format Data for AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PreProcessing1": {
      "main": [
        [
          {
            "node": "Filter Message Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Tickers1": {
      "main": [
        [
          {
            "node": "Get Quotes1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get RSI1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Profile1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bollinger Bands": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "Get Statistics1": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Analytics_Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Set Stock Ticker": {
      "main": [
        [
          {
            "node": "Get Real-Time Quote",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Time Series (90 days)",
            "type": "main",
            "index": 0
          },
          {
            "node": "RSI Indicator",
            "type": "main",
            "index": 0
          },
          {
            "node": "MACD Indicator",
            "type": "main",
            "index": 0
          },
          {
            "node": "Bollinger Bands",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Company Profile",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Income Statement",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Sector Stocks": {
      "main": [
        [
          {
            "node": "Get Quote",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Statistics1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Valid Message?": {
      "main": [
        [
          {
            "node": "Send Unsupported Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Stock Data1": {
      "main": [
        [
          {
            "node": "Format Comparison1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger1": {
      "main": [
        [
          {
            "node": "PreProcessing1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Comparison1": {
      "main": [
        [
          {
            "node": "Output1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Data for AI": {
      "main": [
        [
          {
            "node": "AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Message Type": {
      "main": [
        [
          {
            "node": "Is Valid Message?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Company Profile": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 5
          }
        ]
      ]
    },
    "Get Historical Data": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Real-Time Quote": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge & Enrich Data": {
      "main": [
        [
          {
            "node": "Analyze Sector",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Conversation Memory1": {
      "ai_memory": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Get Income Statement": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 6
          }
        ]
      ]
    },
    "Stock Analysis Tool1": {
      "ai_tool": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Risk Calculator Tool1": {
      "ai_tool": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Sector Analysis Tool1": {
      "ai_tool": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Risk Metrics": {
      "main": [
        [
          {
            "node": "Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stock Comparison Tool1": {
      "ai_tool": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Get Time Series (90 days)": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  }
}