{
  "id": "GJFkNrN20sj0s5Y4",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Portfolio Analysis & Reporting",
  "tags": [],
  "nodes": [
    {
      "id": "d68fe1dd-3f16-4a04-8a47-84558b3b14c3",
      "name": "Fetch Portfolio (Google Sheets)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -16,
        -800
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 2038550601,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1b5Th5axHDdj0L1xX8QUDmNeeV7jdapWGPeXxJcoXOPk/edit#gid=2038550601",
          "cachedResultName": "Portfolio"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1b5Th5axHDdj0L1xX8QUDmNeeV7jdapWGPeXxJcoXOPk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1b5Th5axHDdj0L1xX8QUDmNeeV7jdapWGPeXxJcoXOPk/edit?usp=drivesdk",
          "cachedResultName": "sample_portfolio"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "646c2c19-0343-4490-9809-357ed80ad65c",
      "name": "Iterate Stocks",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        432,
        -624
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "22ef2a13-f3d5-4106-a325-19075347d535",
      "name": "Fetch Stock News (RSS)",
      "type": "n8n-nodes-base.rssFeedRead",
      "position": [
        688,
        -608
      ],
      "parameters": {
        "url": "=https://news.google.com/rss/search?q={{ $json.symbol }}%20stock",
        "options": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "494a20d1-90b7-49e3-9a49-e7645079dd40",
      "name": "Sort News by Date",
      "type": "n8n-nodes-base.sort",
      "position": [
        912,
        -608
      ],
      "parameters": {
        "options": {},
        "sortFieldsUi": {
          "sortField": [
            {
              "order": "descending",
              "fieldName": "isoDate"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "202b2f85-2916-47fc-b5f6-4e386e28532f",
      "name": "Limit News (Top 5)",
      "type": "n8n-nodes-base.limit",
      "position": [
        1168,
        -608
      ],
      "parameters": {
        "maxItems": 5
      },
      "typeVersion": 1
    },
    {
      "id": "7d51d54c-6886-47db-945d-820e232846be",
      "name": "Merge Stock Data + News",
      "type": "n8n-nodes-base.code",
      "position": [
        1424,
        -608
      ],
      "parameters": {
        "jsCode": "const symbol = $('Iterate Stocks').first().json.symbol;\nconst quantity = $('Iterate Stocks').first().json.quantity;\nconst avg_price = $('Iterate Stocks').first().json.avg_price;\nconst current_price = $('Iterate Stocks').first().json.current_price;\nconst invested = $('Iterate Stocks').first().json.invested;\nconst value = $('Iterate Stocks').first().json.value;\nconst pnl = $('Iterate Stocks').first().json.pnl;\nconst return_pct = $('Iterate Stocks').first().json.return_pct;\n\nconst portfolio = {\n  quantity,\n  avg_price,\n  current_price,\n  invested,\n  value,\n  pnl,\n  return_pct\n}\n\nlet news = [];\n\nitems.forEach(element=>{\n  news.push(\n    {\n      title: element.json.title,\n      link: element.json.link\n    }\n  );\n});\n\nreturn [{\n  json: {\n    symbol,\n    portfolio,\n    news\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "ed8ff42b-0f9e-40a0-98e5-1bfad11d54a2",
      "name": "Generate Stock Insight",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1696,
        -608
      ],
      "parameters": {
        "text": "=You are an experienced Portfolio Manager managing equity portfolios in the Indian stock market.\n\nAnalyze the following SINGLE STOCK strictly based on:\n1. Its performance metrics\n2. Recent news headlines\n3. Portfolio impact\n\nINPUT:\n- Stock: {{ $json.symbol }}\n- Performance Data: {{ JSON.stringify($json) }}\n\nINSTRUCTIONS:\n\n1. PERFORMANCE ANALYSIS:\n- Evaluate return %, pnl and position size\n- Classify performance as Strong / Moderate / Weak\n\n2. NEWS INTERPRETATION:\n- Identify overall sentiment (Positive / Negative / Neutral)\n- Extract key themes (e.g., earnings, market fall, sector pressure)\n\n3. INSIGHT GENERATION:\n- Explain WHY the stock is moving (connect data + news)\n- Mention if news supports or contradicts performance\n\n4. PORTFOLIO IMPACT:\n- Assess if this stock is helping or hurting the portfolio\n- Highlight risk if negative trend continues\n\n5. ACTIONABLE VIEW:\n- Give a professional stance: Hold / Monitor / Caution / Positive Bias\n- Do NOT give direct buy/sell advice\n\nRULES:\n- Be concise (80\u2013120 words)\n- No generic statements\n- No repetition\n- Sound like a real fund manager note\n\nOUTPUT FORMAT (STRICT JSON ONLY):\n{\n  \"symbol\": \"{{ $json.symbol }}\",\n  \"performance_label\": \"\",\n  \"sentiment\": \"\",\n  \"insight\": \"\",\n  \"portfolio_impact\": \"\",\n  \"action\": \"\"\n}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "7d6f0e09-c41a-4573-8105-ebef9c0a5152",
      "name": "LLM (Stock Analysis)",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        1760,
        -368
      ],
      "parameters": {
        "model": "openai/gpt-oss-120b",
        "options": {}
      },
      "credentials": {
        "groqApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4f4078f3-0e8a-4cf5-85cb-5c6c0d5b2a32",
      "name": "Rate Limit (Wait)",
      "type": "n8n-nodes-base.wait",
      "position": [
        2096,
        -608
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "87add841-89ad-412a-85de-04feda51018d",
      "name": "Aggregate Stock Insights",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        656,
        -1184
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "output"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "64993235-88c2-4fe0-bc86-d9c3286073e8",
      "name": "Parse Stock AI Output",
      "type": "n8n-nodes-base.code",
      "position": [
        880,
        -1184
      ],
      "parameters": {
        "jsCode": "const rawOutputs = $json.output;\n\n// Convert string \u2192 JSON\nconst parsed = rawOutputs.map(str => JSON.parse(str));\n\nreturn [{\n  json: {\n    stocks: parsed\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "400e3f83-e865-4093-97ce-9ce08f7e1adf",
      "name": "Generate Portfolio Insight",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1088,
        -1184
      ],
      "parameters": {
        "text": "=You are a senior portfolio manager.\n\nYou are given structured stock-level insights.\n\nINPUT:\n{{ JSON.stringify($json.stocks) }}\n\nTASK:\n\n1. Classify overall portfolio performance (Positive / Mixed / Negative)\n2. Identify:\n   - Top contributors\n   - Key laggards\n3. Extract common themes (sector trends, risks, sentiment shifts)\n4. Highlight risks (negative sentiment, weak stocks)\n5. Provide a professional strategy view\n\nRULES:\n- 120 words max\n- No repetition\n- No listing each stock\n- Think like a hedge fund PM\n\nOUTPUT STRICT JSON:\n{\n  \"portfolio_view\": \"\",\n  \"drivers\": \"\",\n  \"risks\": \"\",\n  \"strategy\": \"\"\n}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "96e514cf-0a6a-46f7-8087-64af7a04d50d",
      "name": "LLM (Portfolio Analysis)",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        976,
        -976
      ],
      "parameters": {
        "model": "openai/gpt-oss-20b",
        "options": {}
      },
      "credentials": {
        "groqApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2de431f0-5e67-4397-b7f7-1900d7623cd2",
      "name": "Parse Portfolio AI Output",
      "type": "n8n-nodes-base.code",
      "position": [
        1392,
        -1184
      ],
      "parameters": {
        "jsCode": "const aiRaw = $json.output;\n\n// Remove markdown formatting\nconst clean = aiRaw\n  .replace(/```json/g, '')\n  .replace(/```/g, '')\n  .trim();\n\nconst ai = JSON.parse(clean);\n\nreturn [{\n  json: {\n    portfolio_ai: ai\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "6a8163a8-7b04-4244-ab62-8db131160bd6",
      "name": "Merge Stocks + Portfolio Insight",
      "type": "n8n-nodes-base.merge",
      "position": [
        1712,
        -1024
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "7a428ce5-e672-4f3b-beac-c32d614fcbc4",
      "name": "Format Email Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1952,
        -1024
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\n// Extract portfolio AI (first item)\nconst ai = items[0].json.portfolio_ai || {};\n\n// Extract stocks (second item)\nconst stocks = items[1].json.stocks || [];\n\nlet report = `Portfolio Report\\n\\n`;\n\n// Portfolio Section\nreport += `Portfolio Insights\\n`;\nreport += `\u2022 View: ${ai.portfolio_view || 'N/A'}\\n`;\nreport += `\u2022 Drivers: ${ai.drivers || 'N/A'}\\n`;\nreport += `\u2022 Risks: ${ai.risks || 'N/A'}\\n`;\nreport += `\u2022 Strategy: ${ai.strategy || 'N/A'}\\n\\n`;\n\n// Stock Section\nreport += `Stock-wise Analysis\\n`;\n\nstocks.forEach(s => {\n\n  report += `\\n${s.symbol}\\n`;\n  report += `Sentiment: ${s.sentiment || 'N/A'}\\n`;\n  report += `${s.insight || 'No insight available'}\\n`;\n});\n\nreturn [{\n  json: {\n    report_text: report\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "33b6885f-6594-4f88-b008-571652545c34",
      "name": "Send Email (Gmail)",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2208,
        -1024
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{ $json.report_text }}",
        "options": {},
        "subject": "Portfolio Performance Update",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "da3e30d1-b102-4e18-8b10-67f3bcfc8ced",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        -1664
      ],
      "parameters": {
        "width": 656,
        "height": 576,
        "content": "## AI Portfolio Performance & Insight Workflow\n\nThis workflow analyzes a stock portfolio using Google Sheets data and enhances it with AI-driven insights and market news.\n\n### How it works:\nThe workflow runs automatically every day at 4 PM using a schedule trigger, then reads portfolio data from Google Sheets. It calculates key metrics such as invested value, current value, profit/loss and return percentage. Each stock is processed individually, where recent news is fetched via RSS, filtered and combined with stock data. An AI model then generates professional stock-level insights.\n\nAfter all stocks are processed, the results are aggregated and passed to another AI step that generates an overall portfolio-level summary including performance view, key drivers, risks and strategy.\n\nFinally, both stock-level and portfolio-level insights are merged and formatted into a structured report, which is sent via email.\n\n### Setup steps:\n1. Configure Schedule Trigger (set time to 4 PM daily)\n2. Connect your Google Sheets account and update sheet structure\n3. Configure Groq/OpenAI credentials for AI nodes\n4. Verify RSS feed access (no auth required)\n5. Set your email in the final Gmail node"
      },
      "typeVersion": 1
    },
    {
      "id": "266ec9c5-35ce-4df5-90d5-c603e4fce165",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        -1296
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 1040,
        "content": "## Data Source & Trigger\nRuns automatically every day at 4 PM and fetches portfolio data (symbols, quantity, pricing) from Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "02c3db0c-783b-4e95-a398-859fa48ecc66",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        -1296
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 1040,
        "content": "## Portfolio Metrics Calculation\nComputes invested value, current value, profit/loss and return percentage for each stock."
      },
      "typeVersion": 1
    },
    {
      "id": "d6837552-1a77-4b3e-acd1-d98643d61110",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -816
      ],
      "parameters": {
        "color": 7,
        "width": 1216,
        "height": 560,
        "content": "## Stock Enrichment Pipeline\nProcesses each stock, fetches latest news, filters relevant articles and combines it with stock performance data."
      },
      "typeVersion": 1
    },
    {
      "id": "b706522f-7183-4ed6-9fb3-bbf076f3af7c",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1616,
        -1296
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 464,
        "content": "## Reporting & Delivery\nMerges all insights into a final report, formats it and sends it via email automatically after execution."
      },
      "typeVersion": 1
    },
    {
      "id": "dede6972-7e50-439c-91e6-7e1eba4e5464",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1616,
        -816
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 560,
        "content": "## Stock Enrichment Pipeline\nProcesses each stock, fetches latest news, filters relevant articles and combines it with stock performance data."
      },
      "typeVersion": 1
    },
    {
      "id": "44004e67-e6e9-420a-b53e-dd322c63fe92",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -1296
      ],
      "parameters": {
        "color": 7,
        "width": 1216,
        "height": 464,
        "content": "## Aggregation & Portfolio Analysis\nCombines all stock insights and generates an overall portfolio view, key drivers, risks and strategy."
      },
      "typeVersion": 1
    },
    {
      "id": "1214021a-b82a-492d-bf7b-ac0eb3e9fa9d",
      "name": "Calculate Portfolio Metrics",
      "type": "n8n-nodes-base.set",
      "position": [
        192,
        -800
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "89d24755-f2c8-4619-8bf0-9b7c5cb88bbe",
              "name": "symbol",
              "type": "string",
              "value": "={{ $json.Symbol }}"
            },
            {
              "id": "cf279431-dc12-40cb-9af0-f866ab58d291",
              "name": "quantity",
              "type": "number",
              "value": "={{ $json.Quantity }}"
            },
            {
              "id": "3b7b1037-9dfa-4ec1-9792-7bd4a2b8bd9f",
              "name": "avg_price",
              "type": "number",
              "value": "={{ $json[\"Avg Price\"] }}"
            },
            {
              "id": "9774e298-f994-48f5-a8b1-aec27e54aa43",
              "name": "current_price",
              "type": "number",
              "value": "={{ $json[\"Current Price\"] }}"
            },
            {
              "id": "14bca858-c7b3-4b69-833e-81f9bdad5eed",
              "name": "invested",
              "type": "number",
              "value": "={{ $json.Quantity * $json[\"Avg Price\"] }}"
            },
            {
              "id": "4996082e-4bcc-4a69-a20e-683847e14d1f",
              "name": "value",
              "type": "number",
              "value": "={{ $json.Quantity *  $json[\"Current Price\"]}}"
            },
            {
              "id": "15e1b7c6-d0c4-41d9-ac32-d1ab56288f6f",
              "name": "pnl",
              "type": "number",
              "value": "={{ ($json.Quantity *  $json[\"Current Price\"]) - ($json.Quantity * $json[\"Avg Price\"]) }}"
            },
            {
              "id": "2ca8751e-8ae3-42e3-abac-52539982aa14",
              "name": "return_pct",
              "type": "number",
              "value": "={{ ((($json.Quantity *  $json[\"Current Price\"]) - ($json.Quantity * $json[\"Avg Price\"])) / ($json.Quantity * $json[\"Avg Price\"])) * 100 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b2a6d137-ce11-4f93-a7b5-ca0329498bad",
      "name": "Executes everyday at 4 PM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -240,
        -800
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 16
            }
          ]
        }
      },
      "typeVersion": 1.3
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "9e77af2e-3f29-4334-b170-66952128179a",
  "connections": {
    "Iterate Stocks": {
      "main": [
        [
          {
            "node": "Aggregate Stock Insights",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch Stock News (RSS)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit (Wait)": {
      "main": [
        [
          {
            "node": "Iterate Stocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sort News by Date": {
      "main": [
        [
          {
            "node": "Limit News (Top 5)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit News (Top 5)": {
      "main": [
        [
          {
            "node": "Merge Stock Data + News",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Email Report": {
      "main": [
        [
          {
            "node": "Send Email (Gmail)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM (Stock Analysis)": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Stock Insight",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Parse Stock AI Output": {
      "main": [
        [
          {
            "node": "Generate Portfolio Insight",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Stocks + Portfolio Insight",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Fetch Stock News (RSS)": {
      "main": [
        [
          {
            "node": "Sort News by Date",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Stock Insight": {
      "main": [
        [
          {
            "node": "Rate Limit (Wait)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Stock Data + News": {
      "main": [
        [
          {
            "node": "Generate Stock Insight",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Stock Insights": {
      "main": [
        [
          {
            "node": "Parse Stock AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM (Portfolio Analysis)": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Portfolio Insight",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Executes everyday at 4 PM": {
      "main": [
        [
          {
            "node": "Fetch Portfolio (Google Sheets)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Portfolio AI Output": {
      "main": [
        [
          {
            "node": "Merge Stocks + Portfolio Insight",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Portfolio Insight": {
      "main": [
        [
          {
            "node": "Parse Portfolio AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Portfolio Metrics": {
      "main": [
        [
          {
            "node": "Iterate Stocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Portfolio (Google Sheets)": {
      "main": [
        [
          {
            "node": "Calculate Portfolio Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Stocks + Portfolio Insight": {
      "main": [
        [
          {
            "node": "Format Email Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}