{"workflow":{"id":14059,"name":"Generate institutional-style stock price targets and BUY/HOLD/SELL alerts with ChatGPT and Gemini","views":535,"recentViews":8,"totalViews":535,"createdAt":"2026-03-15T10:36:03.528Z","description":"AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets\n\nA professional-grade AI equity analysis automation built on n8n that ingests live financial data and news, runs it through a dual-LLM valuation engine with a built-in tiebreaker, and delivers disciplined Bear/Base/Bull price targets, BUY/HOLD/SELL verdicts, and risk-adjusted confidence scores — stored in Google Sheets and pushed via Telegram.\n\nKey Features\n\nDual-Model Parallel Analysis (ChatGPT + Gemini 2.5 Pro): Both models independently analyze each stock as senior equity analysts — conservative, risk-first, and evidence-based — then their outputs are compared for agreement.\n\nIntelligent Tiebreaker System: When models disagree on verdict or their base price targets diverge by more than 20%, a dedicated tiebreaker round activates: ChatGPT takes the bull case, Gemini takes the bear case, and the final output is averaged — eliminating coin-flip results.\n\nLive Financial Data Pipeline (Alpha Vantage): Automatically fetches balance sheet, income statement, cash flow, company profile, and real-time price — rate-limited with Wait nodes to respect API quotas across batch runs.\n\nNews Sentiment Ingestion (Seeking Alpha): Parses article feeds (XML/JSON), cleans and normalizes news text, and feeds it into the valuation prompt as qualitative context alongside hard financials.\n\nSmart Caching Layer: Checks for valid cached financial data before hitting APIs — reduces redundant calls, speeds up batch processing, and cuts API costs for unchanged fundamentals.\n\nPiotroski F-Score Integration: Calculates a fundamental quality score from balance sheet signals to separate financially strong companies from deteriorating ones — feeds directly into the confidence index.\n\nBear / Base / Bull Scenario Targets: Every stock receives three price targets with full institutional logic: sector-aware multiples, phase recognition (growth vs. mature), implied P/E sanity checks, and discount rate tiering.\n\nConfidence Scoring (20–90): Combines F-Score, model agreement gap, news sentiment, and financial health into a single index — giving you a read on how conviction-worthy the output is.\n\nGoogle Sheets I/O: Reads a watchlist of tickers from a sheet, upserts results (insert or update by row), and maintains a full historical log of valuations per stock per date.\n\nTelegram Delivery: Pushes formatted valuation summaries directly to a Telegram channel or group after each batch cycle completes.\n\nIdeal For\n\nPortfolio managers and analysts running nightly watchlist reviews\nRetail investors who want institutional-grade structure, not chatbot guesses\nFintech builders integrating a structured valuation API into their stack\nEducators and research teams studying systematic equity valuation frameworks\nTechnical Notes\n\nRate Limiting: Wait nodes are placed before each Alpha Vantage API call to prevent throttling during multi-ticker batch runs. Adjust wait durations based on your API plan tier.\n\nAPI Requirements: Alpha Vantage (financial data), OpenAI (ChatGPT), Google Gemini, Seeking Alpha (news), Google Sheets OAuth.\n\nCaching: Financial fundamentals are cached and validated before fetching — cache TTL logic is handled via a dedicated cache lookup and validity check branch.\n\nTiebreaker Logic: Triggered when BUY/SELL/HOLD verdicts differ OR when pt_base gap exceeds 20% of current price — ensures no ambiguous output reaches the sheet without resolution.\n\nUse Cases\n\nNightly Watchlist Runner — Schedule daily runs across a portfolio, log targets and verdicts to Sheets, alert via Telegram when signals shift.\nConfidence Threshold Alerts — Trigger notifications only when confidence drops below a threshold or a new BUY signal appears.\nAPI Backend — Expose results via webhook for downstream dashboards, apps, or research tools.\nBacktesting Feed — Build a historical record of AI-generated targets vs. actual price movement over time.\nWhy This Is Different\n\nMost AI stock tools are wrappers around a single prompt. This workflow encodes a full institutional valuation pipeline: dual-model consensus, a structured tiebreaker, scenario banding, F-Score filtering, and news-aware risk extraction — all running automatically on a schedule with no manual input required. The output isn't a narrative guess. It's a structured, defensible JSON record designed to be acted on.","workflow":{"id":"nNjotMFYRKbGYBkO","meta":{"instanceId":"a9257e0e2e00fabdb2f2a4df265d2618b826a1bf1ed8d32f3d74e95583416b08","templateCredsSetupCompleted":true},"name":"AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets V10","tags":[{"id":"MrLFqPuujEL6vkPD","name":"Institutional-Grade Stoc","createdAt":"2026-02-24T19:56:14.685Z","updatedAt":"2026-02-24T19:56:14.685Z"},{"id":"T6peYIB3ixkHZuR5","name":"Structured Targets. Quan","createdAt":"2026-02-24T19:56:06.627Z","updatedAt":"2026-02-24T19:56:06.627Z"}],"nodes":[{"id":"a3d2e9f0-323a-48cc-9d8f-7b03dc294628","name":"loop_over_tickers","type":"n8n-nodes-base.splitInBatches","position":[-576,1296],"parameters":{"options":{}},"typeVersion":3},{"id":"a42ffbce-a597-4c14-a529-8a9843b8cf06","name":"Read_tickers_from_Sheet","type":"n8n-nodes-base.googleSheets","position":[-800,1296],"parameters":{"options":{},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1fHwDfRMnMZAIhE6LIo3yofVX2hwcAnBkfE7ibyBT-z4/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1fHwDfRMnMZAIhE6LIo3yofVX2hwcAnBkfE7ibyBT-z4","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1fHwDfRMnMZAIhE6LIo3yofVX2hwcAnBkfE7ibyBT-z4/edit?usp=drivesdk","cachedResultName":"List of stocks"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"typeVersion":4.6},{"id":"eb1cd019-3eda-419b-9777-ab8b40a2588c","name":"write_sentiment_to_sheets","type":"n8n-nodes-base.googleSheets","position":[7744,1392],"parameters":{"columns":{"value":{"date":"={{ $json.date }}","ma_50":"={{ $json.ma_50 || '' }}","model":"={{ $json.model }}","stock":"={{ $json.stock }}","ma_200":"={{ $json.ma_200 || '' }}","rsi_14":"={{ $json.rsi_14 || '' }}","f_score":"={{ $json.f_score || '' }}","gap_pct":"={{ $json.gap_pct }}","pt_base":"={{ $json.pt_base }}","pt_bear":"={{ $json.pt_bear }}","pt_bull":"={{ $json.pt_bull }}","ma_signal":"={{ $json.ma_signal || '' }}","rationale":"={{ $json.rationale }}","confidence":"={{ $json.confidence }}","conviction":"={{ $json.conviction }}","rsi_signal":"={{ $json.rsi_signal || '' }}","trend_tier":"={{ $json.trend_tier || '' }}","current_price":"={{ $json.current_price || '' }}","verdict_gemini":"={{ $json.verdict_gemini || '' }}","thesis_reversal":"={{ $json.thesis_reversal || false }}","verdict_chatgpt":"={{ $json.verdict_chatgpt || '' }}","previous_verdict":"={{ $json.previous_verdict || '' }}","resolution_method":"={{ $json.resolution_method }}","next_earnings_date":"={{ $json.next_earnings_date || '' }}"},"schema":[{"id":"stock","type":"string","display":true,"required":false,"displayName":"stock","defaultMatch":false,"canBeUsedToMatch":true},{"id":"date","type":"string","display":true,"required":false,"displayName":"date","defaultMatch":false,"canBeUsedToMatch":true},{"id":"current_price","type":"string","display":true,"removed":false,"required":false,"displayName":"current_price","defaultMatch":false,"canBeUsedToMatch":true},{"id":"pt_bear","type":"string","display":true,"required":false,"displayName":"pt_bear","defaultMatch":false,"canBeUsedToMatch":true},{"id":"pt_base","type":"string","display":true,"required":false,"displayName":"pt_base","defaultMatch":false,"canBeUsedToMatch":true},{"id":"pt_bull","type":"string","display":true,"required":false,"displayName":"pt_bull","defaultMatch":false,"canBeUsedToMatch":true},{"id":"f_score","type":"string","display":true,"removed":false,"required":false,"displayName":"f_score","defaultMatch":false,"canBeUsedToMatch":true},{"id":"confidence","type":"string","display":true,"required":false,"displayName":"confidence","defaultMatch":false,"canBeUsedToMatch":true},{"id":"gap_pct","type":"string","display":true,"removed":false,"required":false,"displayName":"gap_pct","defaultMatch":false,"canBeUsedToMatch":true},{"id":"resolution_method","type":"string","display":true,"removed":false,"required":false,"displayName":"resolution_method","defaultMatch":false,"canBeUsedToMatch":true},{"id":"rationale","type":"string","display":true,"required":false,"displayName":"rationale","defaultMatch":false,"canBeUsedToMatch":true},{"id":"conviction","type":"string","display":true,"removed":false,"required":false,"displayName":"conviction","defaultMatch":false,"canBeUsedToMatch":true},{"id":"model","type":"string","display":true,"removed":false,"required":false,"displayName":"model","defaultMatch":false,"canBeUsedToMatch":true},{"id":"verdict_chatgpt","type":"string","display":true,"removed":false,"required":false,"displayName":"verdict_chatgpt","defaultMatch":false,"canBeUsedToMatch":true},{"id":"verdict_gemini","type":"string","display":true,"removed":false,"required":false,"displayName":"verdict_gemini","defaultMatch":false,"canBeUsedToMatch":true},{"id":"ma_50","type":"string","display":true,"removed":false,"required":false,"displayName":"ma_50","defaultMatch":false,"canBeUsedToMatch":true},{"id":"ma_signal","type":"string","display":true,"removed":false,"required":false,"displayName":"ma_signal","defaultMatch":false,"canBeUsedToMatch":true},{"id":"rsi_14","type":"string","display":true,"removed":false,"required":false,"displayName":"rsi_14","defaultMatch":false,"canBeUsedToMatch":true},{"id":"rsi_signal","type":"string","display":true,"removed":false,"required":false,"displayName":"rsi_signal","defaultMatch":false,"canBeUsedToMatch":true},{"id":"trend_tier","type":"string","display":true,"removed":false,"required":false,"displayName":"trend_tier","defaultMatch":false,"canBeUsedToMatch":true},{"id":"ma_200","type":"string","display":true,"removed":false,"required":false,"displayName":"ma_200","defaultMatch":false,"canBeUsedToMatch":true},{"id":"next_earnings_date","type":"string","display":true,"removed":false,"required":false,"displayName":"next_earnings_date","defaultMatch":false,"canBeUsedToMatch":true},{"id":"previous_verdict","type":"string","display":true,"removed":false,"required":false,"displayName":"previous_verdict","defaultMatch":false,"canBeUsedToMatch":true},{"id":"thesis_reversal","type":"string","display":true,"removed":false,"required":false,"displayName":"thesis_reversal","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE/edit?usp=drivesdk","cachedResultName":"Sentiments of my stocks"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"typeVersion":4.6},{"id":"ac80fd3d-5d01-41b8-8897-7a32cbbeaf24","name":"Schedule Trigger","type":"n8n-nodes-base.scheduleTrigger","position":[-1024,1296],"parameters":{"rule":{"interval":[{"daysInterval":3,"triggerAtHour":16}]}},"typeVersion":1.2},{"id":"8d041788-4107-48be-9c1f-1107b4e549e4","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-656,192],"parameters":{"width":480,"height":440,"content":"# Workflow Overview \n**This workflow automates the process of analyzing the sentiment of stock market news.**\n\n- retrieves a list of stock tickers from a Google Sheet \n- fetchs recent news articles for each ticker\n- uses a 2 large language model to perform sentiment analysis on the articles\n- records the sentiment scores and rationale back into a Google Sheet."},"typeVersion":1},{"id":"35eff45b-2fe7-477e-9bb5-592a12db4d5d","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[-1056,704],"parameters":{"color":4,"width":1152,"height":992,"content":"# 1. Daily Trigger and Stock Ticker Retrieval\n- **Schedule Trigger:** This workflow is set to run automatically every three day at 4:00 PM (Asia/Jerusalem time). This ensures that the script runs just before the markets open and you get a daily update on the sentiment of the stocks you are tracking.\n\n- **Read_tickers_from_Sheet:** This node connects to a Google Sheet named \"Stock Sentiment\" and reads the list of stock tickers from the \"stocks\" sheet. This is the source of the stocks that the workflow will analyze. I have make every three days to avoid extra fees from Alphavintage since, and you can modify it if you need.\n\n- **loop_over_tickers:** This node takes the list of tickers from the Google Sheet and processes them one by one. This allows the workflow to perform the same set of actions for each stock ticker individually."},"typeVersion":1},{"id":"4ebc103a-c5b8-4825-b2de-2629d007c762","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[128,-48],"parameters":{"color":5,"width":2760,"height":1420,"content":"# 2. Financial and News Data Retrieval & Validation\n2.1 Financial Data Fetching (Alpha Vantage APIs)\nFetch Financial Data (Alpha Vantage):\nFor each ticker, the workflow sends multiple HTTP requests to Alpha Vantage to retrieve structured financial data, including:\nCompany overview (EPS, BVPS, margins, shares, revenue).\nIncome statement (quarterly revenue and net income).\nBalance sheet (debt and cash position).\nCash flow statement (operating cash flow and capital expenditures for FCF calculation).\nCurrent stock price.\nData Normalization and Processing:\nThe responses from the APIs are processed using code nodes to:\nExtract required financial fields.\nCalculate derived metrics such as revenue growth, gross margins, total debt, cash position, and free cash flow.\nStandardize numeric values and prepare structured financial data for analysis.\nFinancial Cache Validation:\nThe workflow checks Google Sheets to determine whether financial data for the ticker already exists and whether it is still fresh (within a defined time window).\nIf the data is missing or outdated → financial APIs are called.\nIf the data is still valid → cached data is reused to avoid unnecessary API calls.\nUpdate or Insert Financial Records:\nBased on the cache result:\nNew records are inserted for first-time tickers.\nExisting records are updated when refreshed data is retrieved."},"typeVersion":1},{"id":"5c6bf418-f4d5-49c8-911a-6c93d3e0f636","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[2928,576],"parameters":{"color":6,"width":2004,"height":1536,"content":"# 3. Sentiment Analysis with AI\n\n- **AI Agent & Google Gemini Chat Model/ChatGPT:** This is the core of the sentiment analysis. The \"AI Agent\" node is configured with a detailed prompt that instructs the \"Google Gemini Chat Model & ChatGPT\" to act as a stock sentiment analyzer. The prompt specifies the input format (stock symbol, price targets,confidance, rational), and the desired JSON output format. The combined text of the news articles and the current stock ticker are passed to the model.l analyze all the articles at once."},"typeVersion":1},{"id":"ff55f562-a968-41c4-be97-f8493a9b8612","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[4944,576],"parameters":{"color":2,"width":544,"height":1532,"content":"# 4. Output Formatting and Error Handling\n\n- **format_output_as_json:** The output from the AI models is a raw string that includes a JSON object. This code node extracts the clean JSON from the string and prepares it for the next steps.\n\n- **if_format_succesful:** This conditional node checks if the previous step of formatting the AI's output into a clean JSON was successful. If there was an error, it sends the workflow back to the \"AI Agent\" to try again."},"typeVersion":1},{"id":"a0e69d33-1308-40ed-92be-ba9100f08133","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[7600,576],"parameters":{"color":3,"width":520,"height":1528,"content":"# 5. Storing the Results\n\n\n- **write_sentiment_to_sheets:** Once a valid sentiment analysis result is obtained and formatted, this node appends the data to \"Sheet1\" of the \"Stock Sentiment\" Google Sheet. It records the current date, the stock ticker, the sentiment score, and the rationale provided by the AI. After this step, the workflow loops back to process the next ticker from the initial list.\n\n\n\n\n\n\n"},"typeVersion":1},{"id":"5de75ec0-9b5d-499a-9599-1ea79e3821be","name":"Code in JavaScript","type":"n8n-nodes-base.code","position":[4064,1344],"parameters":{"jsCode":"// ── TIEBREAKER ENRICHMENT ─────────────────────────────────────────────\nconst needs_tiebreaker = $input.first().json?.needs_tiebreaker || false;\nconst gap_pct          = $input.first().json?.gap_pct || 0;\nconst verdict_chatgpt  = $input.first().json?.verdict_chatgpt || null;\nconst verdict_gemini   = $input.first().json?.verdict_gemini  || null;\n\nfunction pickValid(x) {\n  if (!x) return null;\n  const base = Number(x.pt_base);\n  const bear = Number(x.pt_bear);\n  const bull = Number(x.pt_bull);\n  const conf = Number(x.confidence);\n  if (!x.stock) return null;\n  if (!isFinite(base) || base <= 0) return null;\n  if (!isFinite(bear) || bear <= 0) return null;\n  if (!isFinite(bull) || bull <= 0) return null;\n  if (!isFinite(conf) || conf <= 0) return null;\n  return x;\n}\n\nconst byStock = new Map();\n\nfor (const it of $input.all()) {\n  const j = it.json || {};\n\n  const candidates = [];\n\n  if (j.chatgpt_result && typeof j.chatgpt_result === \"object\") {\n    candidates.push({ ...j.chatgpt_result, model: j.chatgpt_result.model || \"CHATGPT\" });\n  }\n  if (j.gemini_result && typeof j.gemini_result === \"object\") {\n    candidates.push({ ...j.gemini_result, model: j.gemini_result.model || \"GEMINI\" });\n  }\n\n  // Fallback: already flattened\n  if (candidates.length === 0 && j.stock && (j.pt_base || j.pt_bear || j.pt_bull)) {\n    candidates.push({ ...j, model: j.model || \"UNKNOWN\" });\n  }\n\n  for (const cand of candidates) {\n    const stock = cand.stock || j.stock || \"\";\n    const normalized = {\n      stock,\n      date:       cand.date || null,\n      pt_bear:    Number(cand.pt_bear   ?? NaN),\n      pt_base:    Number(cand.pt_base   ?? NaN),\n      pt_bull:    Number(cand.pt_bull   ?? NaN),\n      f_score:    cand.f_score ?? null,\n      confidence: Number(cand.confidence ?? NaN),\n      rationale:  cand.rationale || \"\",\n      model:      cand.model || \"UNKNOWN\"\n    };\n\n    const valid = pickValid(normalized);\n    if (!valid) continue;\n\n    if (!byStock.has(stock)) byStock.set(stock, []);\n    byStock.get(stock).push(valid);\n  }\n}\n\nconst today = new Date().toISOString().slice(0, 10);\n\n// ── Conviction label ──────────────────────────────────────────────────\nfunction getConviction(gap) {\n  if (gap < 10) return \"HIGH\";\n  if (gap < 20) return \"MEDIUM\";\n  return \"LOW\";\n}\n\n// ── Build output rows ─────────────────────────────────────────────────\nconst out = [];\n\nfor (const [stock, arr] of byStock.entries()) {\n\n  // Keep highest confidence per model\n  const bestByModel = new Map();\n  for (const r of arr) {\n    const key  = r.model;\n    const prev = bestByModel.get(key);\n    if (!prev || (r.confidence ?? 0) > (prev.confidence ?? 0)) {\n      bestByModel.set(key, r);\n    }\n  }\n\n  for (const r of bestByModel.values()) {\n    const date = r.date || today;\n\n    out.push({\n      json: {\n        stock,\n        date,\n        pt_bear:    Number(r.pt_bear.toFixed(2)),\n        pt_base:    Number(r.pt_base.toFixed(2)),\n        pt_bull:    Number(r.pt_bull.toFixed(2)),\n        f_score:    r.f_score,\n        confidence: Math.max(20, Math.min(90, Number(r.confidence.toFixed(0)))),\n        rationale:  r.rationale || \"No rationale provided\",\n        model:      r.model,\n        row_key:    `${stock}_${date}_${r.model}`,\n        skip_row:   false,\n        // ── Hybrid enrichment ────────────────────────────────────────\n        resolution_method: needs_tiebreaker ? \"TIEBREAKER\" : \"CONSENSUS\",\n        conviction:        getConviction(gap_pct),\n        gap_pct,\n        verdict_chatgpt,\n        verdict_gemini\n      }\n    });\n  }\n}\n\n// ── Fallback if nothing valid came through ────────────────────────────\nif (out.length === 0) {\n  return [{\n    json: {\n      stock:             \"\",\n      date:              today,\n      pt_bear:           0,\n      pt_base:           0,\n      pt_bull:           0,\n      confidence:        0,\n      rationale:         \"No valid analyst data\",\n      model:             \"ERROR\",\n      row_key:           `ERROR_${today}`,\n      skip_row:          true,\n      resolution_method: \"ERROR\",\n      conviction:        \"NONE\",\n      gap_pct:           0,\n      verdict_chatgpt:   null,\n      verdict_gemini:    null\n    }\n  }];\n}\n\nreturn out;"},"typeVersion":2},{"id":"9ab1cf9e-5ea9-4d72-b59a-0cd37c20962f","name":"Merge","type":"n8n-nodes-base.merge","position":[3440,1120],"parameters":{"mode":"combine","options":{},"joinMode":"keepEverything","fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"00b14e09-f606-48a3-a026-ca8953c96fbe","name":"XML","type":"n8n-nodes-base.xml","position":[1360,1632],"parameters":{"options":{},"dataPropertyName":"sa_xml"},"typeVersion":1},{"id":"bd8b7af5-003c-40cc-b895-77d78ccf476f","name":"Merge1","type":"n8n-nodes-base.merge","position":[2720,1152],"parameters":{"mode":"combine","options":{},"joinMode":"keepEverything","fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"f4fcca1b-bc7a-4d58-8bfc-1da7e3648e5e","name":"If","type":"n8n-nodes-base.if","position":[5008,1328],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"8659bdf4-3891-4cf4-9187-ea800a3810f8","operator":{"type":"boolean","operation":"false","singleValue":true},"leftValue":"={{$json.skip_row}}","rightValue":""}]}},"typeVersion":2.3},{"id":"9760091c-547a-4501-bd4f-1d43a1a91250","name":"If1","type":"n8n-nodes-base.if","position":[912,1776],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"bf9993a5-39a5-4344-a1a6-5ba38e67e47c","operator":{"type":"string","operation":"notEmpty","singleValue":true},"leftValue":"sa_xml","rightValue":""}]}},"typeVersion":2.3},{"id":"7ae74b4b-33c2-4cf6-999e-c3c49f96f5a6","name":"Edit Fields2","type":"n8n-nodes-base.set","position":[1552,1632],"parameters":{"options":{},"assignments":{"assignments":[{"id":"44ff26a3-5901-4e3b-9b15-9844a2d207b5","name":"stock","type":"string","value":"={{ $('Clean the news').item.json.stock }}"},{"id":"fca99ba0-11fd-40a3-acf7-ea25579a653e","name":"rss","type":"object","value":"={{ $json.rss }}"}]}},"typeVersion":3.4},{"id":"f7a5b448-14cc-4204-8541-24ded9f9a1b9","name":"Merge2","type":"n8n-nodes-base.merge","position":[1552,432],"parameters":{"mode":"combine","options":{},"joinMode":"keepEverything","fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"70216662-4f36-432f-a5c9-2e479495227f","name":"Is Cache Valid?","type":"n8n-nodes-base.if","position":[384,768],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"8579a7cf-3060-4026-b94a-5e5f370c63ae","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{$json.shouldFetch}}","rightValue":""}]}},"typeVersion":2.3},{"id":"a1929b6f-2930-4d9f-a040-310c94e9a123","name":"Cache Lookup","type":"n8n-nodes-base.googleSheets","maxTries":3,"position":[-320,1184],"parameters":{"options":{"returnFirstMatch":true},"filtersUI":{"values":[{"lookupValue":"={{ $json.stock }}","lookupColumn":"stock"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk","cachedResultName":"Financial_Data_Cache"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"retryOnFail":false,"typeVersion":4.7,"alwaysOutputData":true},{"id":"705966f9-7e33-45d1-9cf0-35ddad297aa2","name":"alphavantage - Balance Sheet","type":"n8n-nodes-base.httpRequest","position":[976,416],"parameters":{"url":"=https://www.alphavantage.co/query?function=BALANCE_SHEET&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.4},{"id":"8682d4b9-a044-4565-a0c5-4c114c8303a9","name":"alphavantage - Profile","type":"n8n-nodes-base.httpRequest","position":[976,576],"parameters":{"url":"=https://www.alphavantage.co/query?function=OVERVIEW&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.4},{"id":"6f43cf2e-7258-4f4e-b907-101d0a07eda4","name":"alphavantage - Income Statement","type":"n8n-nodes-base.httpRequest","position":[976,736],"parameters":{"url":"=https://www.alphavantage.co/query?function=INCOME_STATEMENT&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.4},{"id":"819fcbdc-7ba3-498e-903b-3aac8257aed6","name":"Merge3","type":"n8n-nodes-base.merge","position":[1392,704],"parameters":{"mode":"combine","options":{},"joinMode":"keepEverything","fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"3abc3ce3-087b-439c-8bed-b0645971d84e","name":"Get row(s) in sheet","type":"n8n-nodes-base.googleSheets","position":[1792,1216],"parameters":{"options":{},"filtersUI":{"values":[{"lookupValue":"={{ $('loop_over_tickers').item.json.stock }}","lookupColumn":"stock"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk","cachedResultName":"Financial_Data_Cache"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"typeVersion":4.7},{"id":"f35dc3c3-73ea-4e14-bd74-48f21425bf3a","name":"If2","type":"n8n-nodes-base.if","position":[2080,448],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"5ac312fa-a607-405d-9ab0-6993e2f45b19","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{$json.cacheHit}}","rightValue":""}]}},"typeVersion":2.3},{"id":"069ebe17-84c4-485e-b767-ee898685af07","name":"Update row in sheet","type":"n8n-nodes-base.googleSheets","position":[2288,432],"parameters":{"columns":{"value":{"stock":"={{ $json.stock }}","sector":"={{ $('Clean Read Financial').item.json.sector }}","f_score":"={{ $('Clean Read Financial').item.json.f_score }}","fcf_ttm":"={{ $('Clean Read Financial').item.json.fcf_ttm }}","row_number":0,"eps_current":"={{ $('Clean Read Financial').item.json.eps_current }}","revenue_ttm":"={{ $('Clean Read Financial').item.json.revenue_ttm }}","bvps_current":"={{ $('Clean Read Financial').item.json.bvps_current }}","cash_last_4q":"={{ $('Clean Read Financial').item.json.cash_last_4q }}","last_updated":"={{ new Date().toISOString().split(\"T\")[0] }}","current_price":"={{ $('Clean Read Financial').item.json.current_price }}","net_cash_flag":"={{ $('Clean Read Financial').item.json.net_cash_flag }}","net_income_ttm":"={{ $('Clean Read Financial').item.json.net_income_ttm }}","total_debt_ttm":"={{ $('Clean Read Financial').item.json.total_debt_ttm }}","f_score_data_ok":"={{ $('Clean Read Financial').item.json.f_score_data_ok }}","net_debt_latest":"={{ $('Clean Read Financial').item.json.net_debt_latest }}","revenue_last_4q":"={{ $('Clean Read Financial').item.json.revenue_last_4q }}","gross_margin_ttm":"={{ $('Clean Read Financial').item.json.gross_margin_ttm }}","net_income_last_4q":"={{ $('Clean Read Financial').item.json.net_income_last_4q }}","revenue_growth_yoy":"={{ $('Clean Read Financial').item.json.revenue_growth_yoy }}","shares_outstanding":"={{ $('Clean Read Financial').item.json.shares_outstanding }}","total_debt_last_4q":"={{ $('Clean Read Financial').item.json.total_debt_last_4q }}","gross_margin_last_4q":"={{ $('Clean Read Financial').item.json.gross_margin_last_4q }}","operating_margin_ttm":"={{ $('Clean Read Financial').item.json.operating_margin_ttm }}"},"schema":[{"id":"stock","type":"string","display":true,"removed":false,"required":false,"displayName":"stock","defaultMatch":false,"canBeUsedToMatch":true},{"id":"cache_key","type":"string","display":true,"removed":false,"required":false,"displayName":"cache_key","defaultMatch":false,"canBeUsedToMatch":true},{"id":"data_source","type":"string","display":true,"removed":false,"required":false,"displayName":"data_source","defaultMatch":false,"canBeUsedToMatch":true},{"id":"last_updated","type":"string","display":true,"required":false,"displayName":"last_updated","defaultMatch":false,"canBeUsedToMatch":true},{"id":"eps_current","type":"string","display":true,"required":false,"displayName":"eps_current","defaultMatch":false,"canBeUsedToMatch":true},{"id":"bvps_current","type":"string","display":true,"required":false,"displayName":"bvps_current","defaultMatch":false,"canBeUsedToMatch":true},{"id":"shares_outstanding","type":"string","display":true,"required":false,"displayName":"shares_outstanding","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_ttm","type":"string","display":true,"required":false,"displayName":"revenue_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"total_debt_ttm","type":"string","display":true,"required":false,"displayName":"total_debt_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"gross_margin_ttm","type":"string","display":true,"required":false,"displayName":"gross_margin_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"operating_margin_ttm","type":"string","display":true,"required":false,"displayName":"operating_margin_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"fcf_ttm","type":"string","display":true,"required":false,"displayName":"fcf_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_last_4q","type":"string","display":true,"required":false,"displayName":"revenue_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_income_last_4q","type":"string","display":true,"required":false,"displayName":"net_income_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"gross_margin_last_4q","type":"string","display":true,"required":false,"displayName":"gross_margin_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"total_debt_last_4q","type":"string","display":true,"required":false,"displayName":"total_debt_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"cash_last_4q","type":"string","display":true,"required":false,"displayName":"cash_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_income_ttm","type":"string","display":true,"removed":false,"required":false,"displayName":"net_income_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"current_price","type":"string","display":true,"removed":false,"required":false,"displayName":"current_price","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_growth_yoy","type":"string","display":true,"required":false,"displayName":"revenue_growth_yoy","defaultMatch":false,"canBeUsedToMatch":true},{"id":"f_score","type":"string","display":true,"removed":false,"required":false,"displayName":"f_score","defaultMatch":false,"canBeUsedToMatch":true},{"id":"f_score_data_ok","type":"string","display":true,"removed":false,"required":false,"displayName":"f_score_data_ok","defaultMatch":false,"canBeUsedToMatch":true},{"id":"sector","type":"string","display":true,"removed":false,"required":false,"displayName":"sector","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"number","display":true,"removed":false,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_debt_latest","type":"string","display":true,"removed":false,"required":false,"displayName":"net_debt_latest","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_cash_flag","type":"string","display":true,"removed":false,"required":false,"displayName":"net_cash_flag","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["stock"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk","cachedResultName":"Financial_Data_Cache"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"typeVersion":4.7},{"id":"308167a0-30f0-4216-894c-1e6abfeb4a22","name":"Insert Row","type":"n8n-nodes-base.googleSheets","position":[2304,896],"parameters":{"columns":{"value":{"stock":"={{ $json.stock }}","sector":"={{ $json.sector }}","f_score":"={{ $json.f_score }}","fcf_ttm":"={{ $json.fcf_ttm }}","eps_current":"={{ $json.eps_current }}","revenue_ttm":"={{ $json.revenue_ttm }}","bvps_current":"={{ $json.bvps_current }}","cash_last_4q":"={{ $json.cash_last_4q }}","last_updated":"={{ new Date().toISOString().split(\"T\")[0] }}","current_price":"={{ $json.current_price }}","net_cash_flag":"={{ $json.net_cash_flag }}","net_income_ttm":"={{ $json.net_income_ttm }}","total_debt_ttm":"={{ $json.total_debt_ttm }}","f_score_data_ok":"={{ $json.f_score_data_ok }}","net_debt_latest":"={{ $json.net_debt_latest }}","revenue_last_4q":"={{ $json.revenue_last_4q }}","gross_margin_ttm":"={{ $json.gross_margin_ttm }}","net_income_last_4q":"={{ $json.net_income_last_4q }}","revenue_growth_yoy":"={{ $json.revenue_growth_yoy }}","shares_outstanding":"={{ $json.shares_outstanding }}","total_debt_last_4q":"={{ $json.total_debt_last_4q }}","gross_margin_last_4q":"={{ $json.gross_margin_last_4q }}","operating_margin_ttm":"={{ $json.operating_margin_ttm }}"},"schema":[{"id":"stock","type":"string","display":true,"removed":false,"required":false,"displayName":"stock","defaultMatch":false,"canBeUsedToMatch":true},{"id":"cache_key","type":"string","display":true,"removed":false,"required":false,"displayName":"cache_key","defaultMatch":false,"canBeUsedToMatch":true},{"id":"data_source","type":"string","display":true,"removed":false,"required":false,"displayName":"data_source","defaultMatch":false,"canBeUsedToMatch":true},{"id":"last_updated","type":"string","display":true,"required":false,"displayName":"last_updated","defaultMatch":false,"canBeUsedToMatch":true},{"id":"eps_current","type":"string","display":true,"required":false,"displayName":"eps_current","defaultMatch":false,"canBeUsedToMatch":true},{"id":"bvps_current","type":"string","display":true,"required":false,"displayName":"bvps_current","defaultMatch":false,"canBeUsedToMatch":true},{"id":"shares_outstanding","type":"string","display":true,"required":false,"displayName":"shares_outstanding","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_ttm","type":"string","display":true,"required":false,"displayName":"revenue_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"total_debt_ttm","type":"string","display":true,"required":false,"displayName":"total_debt_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"gross_margin_ttm","type":"string","display":true,"required":false,"displayName":"gross_margin_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"operating_margin_ttm","type":"string","display":true,"required":false,"displayName":"operating_margin_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"fcf_ttm","type":"string","display":true,"required":false,"displayName":"fcf_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_last_4q","type":"string","display":true,"required":false,"displayName":"revenue_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_income_last_4q","type":"string","display":true,"required":false,"displayName":"net_income_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"gross_margin_last_4q","type":"string","display":true,"required":false,"displayName":"gross_margin_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"total_debt_last_4q","type":"string","display":true,"required":false,"displayName":"total_debt_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"cash_last_4q","type":"string","display":true,"required":false,"displayName":"cash_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_income_ttm","type":"string","display":true,"removed":false,"required":false,"displayName":"net_income_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"current_price","type":"string","display":true,"removed":false,"required":false,"displayName":"current_price","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_growth_yoy","type":"string","display":true,"required":false,"displayName":"revenue_growth_yoy","defaultMatch":false,"canBeUsedToMatch":true},{"id":"f_score","type":"string","display":true,"removed":false,"required":false,"displayName":"f_score","defaultMatch":false,"canBeUsedToMatch":true},{"id":"f_score_data_ok","type":"string","display":true,"removed":false,"required":false,"displayName":"f_score_data_ok","defaultMatch":false,"canBeUsedToMatch":true},{"id":"sector","type":"string","display":true,"removed":false,"required":false,"displayName":"sector","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_debt_latest","type":"string","display":true,"removed":false,"required":false,"displayName":"net_debt_latest","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_cash_flag","type":"string","display":true,"removed":false,"required":false,"displayName":"net_cash_flag","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["stock"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk","cachedResultName":"Financial_Data_Cache"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"typeVersion":4.7},{"id":"31efd328-56ed-42fb-995f-0af16f09f28e","name":"alphavantage - CashFlow","type":"n8n-nodes-base.httpRequest","position":[976,912],"parameters":{"url":"=https://www.alphavantage.co/query?function=CASH_FLOW&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.4},{"id":"95fe5434-3334-41f3-8f71-0115b01774ac","name":"Merge4","type":"n8n-nodes-base.merge","position":[1728,800],"parameters":{"mode":"combine","options":{},"fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"724d38b0-4b1a-4df7-900f-905217fa24b9","name":"Check the cache","type":"n8n-nodes-base.code","position":[208,768],"parameters":{"jsCode":"const now = Date.now();\n\nconst row = items?.[0]?.json ?? null;\n\n// Read the manual refresh flag from Google Sheet or input\nconst rawNeedFreshFinancial =\n  row?.need_fresh_financial ??\n  $json.need_fresh_financial ??\n  false;\n\n// Convert different formats (true/false, 1/0, yes/no) safely to boolean\nfunction toBoolean(value) {\n  if (typeof value === 'boolean') return value;\n  if (typeof value === 'number') return value === 1;\n  if (typeof value === 'string') {\n    const v = value.trim().toLowerCase();\n    return ['true','1','yes','y'].includes(v);\n  }\n  return false;\n}\n\nconst needFreshFinancial = toBoolean(rawNeedFreshFinancial);\n\n// Check if cache exists\nconst cacheHit = !!(row && row.stock);\n\nlet shouldFetch = false;\nlet reason = '';\n\nif (!cacheHit) {\n  shouldFetch = true;\n  reason = 'cache_miss';\n} \nelse if (needFreshFinancial) {\n  shouldFetch = true;\n  reason = 'manual_financial_refresh';\n} \nelse {\n  shouldFetch = false;\n  reason = 'use_cached_financials';\n}\n\nreturn [{\n  json: {\n    stock: row?.stock || $json.stock,\n    cacheHit,\n    needFreshFinancial,\n    shouldFetch,\n    reason,\n    cacheRow: row || null,\n    nowISO: new Date(now).toISOString(),\n  }\n}];"},"typeVersion":2},{"id":"7bc7610f-155f-4a3f-8087-ac00d61116c8","name":"Seekingalpha Articles","type":"n8n-nodes-base.httpRequest","position":[496,1760],"parameters":{"url":"=https://seekingalpha.com/api/sa/combined/{{ $json.stock }}.xml","options":{"response":{"response":{"responseFormat":"text","outputPropertyName":"sa_xml"}}}},"typeVersion":4.4},{"id":"d2a2d325-97a4-40c4-8f73-a310cab253cd","name":"Clean balance sheet","type":"n8n-nodes-base.code","position":[1184,416],"parameters":{"jsCode":"return {\n  json: {\n    stock: $json.stock || $json.Symbol || $json.symbol || $input.item.json.stock || \"UNKNOWN\",\n    balance_data: $json\n  }\n};"},"typeVersion":2},{"id":"e30294e8-4a3c-4a63-a468-54b623fea606","name":"Clean Profile","type":"n8n-nodes-base.code","position":[1184,576],"parameters":{"jsCode":"return {\n  json: {\n    stock: $json.stock || $json.Symbol || $json.symbol || $input.item.json.stock || \"UNKNOWN\",\n    overview_data: $json\n  }\n};"},"typeVersion":2},{"id":"52731b5a-c076-497a-9e36-2a2f5aed8595","name":"Clean Income statement","type":"n8n-nodes-base.code","position":[1184,736],"parameters":{"jsCode":"const inJson = $json || {};\nconst stock =\n  inJson.stock ||\n  inJson.Symbol ||\n  inJson.symbol ||\n  (items && items[0] && items[0].json && items[0].json.stock) ||\n  \"UNKNOWN\";\n\nreturn [\n  {\n    json: {\n      stock: stock,\n      income_data: inJson\n    }\n  }\n];"},"typeVersion":2},{"id":"0d1e825c-fa1f-49fc-b119-e809b8dec76e","name":"Clean Ccashflow","type":"n8n-nodes-base.code","position":[1168,896],"parameters":{"jsCode":"const inJson = $json || {};\nconst stock =\n  inJson.stock ||\n  inJson.Symbol ||\n  inJson.symbol ||\n  (items && items[0] && items[0].json && items[0].json.stock) ||\n  \"UNKNOWN\";\n\nreturn [\n  {\n    json: {\n      stock: stock,\n      cashflow_data: inJson\n    }\n  }\n];"},"typeVersion":2},{"id":"14e3bdd5-d3e8-43cb-afee-044b6ae9a43b","name":"Clean Read Financial","type":"n8n-nodes-base.code","position":[1888,800],"parameters":{"mode":"runOnceForEachItem","jsCode":"// Helper function to safely get numbers\nfunction num(value) {\n  if (value === null || value === undefined || value === 'None' || value === '') return null;\n  const n = Number(String(value).replace(/[,$%]/g, ''));\n  return isFinite(n) ? n : null;\n}\n\n// Get data from the API responses\nconst overview = $json.overview_data || {};\nconst income = $json.income_data || {};\nconst balance = $json.balance_data || {};\nconst cashflow = $json.cashflow_data || {}; // optional (only if you add CASH_FLOW endpoint)\nconst current_price = num($json.current_price); // null if missing\n\n//sector\nconst sector= overview.sector;\n// Extract reports\nconst quarterlyReports = income.quarterlyReports || [];\nconst balanceQuarterly = balance.quarterlyReports || [];\n\n// Revenue last 4 quarters\nconst revenue_last_4q = quarterlyReports.slice(0, 4).map(q => num(q.totalRevenue));\n\n// Net income last 4 quarters\nconst net_income_last_4q = quarterlyReports.slice(0, 4).map(q => num(q.netIncome));\n\n// Gross margin last 4 quarters (calculate from revenue and cost)\nconst gross_margin_last_4q = quarterlyReports.slice(0, 4).map(q => {\n  const rev = num(q.totalRevenue);\n  const cogs = num(q.costOfRevenue);\n  if (rev !== null && cogs !== null && rev > 0) {\n    return Number((((rev - cogs) / rev) * 100).toFixed(2));\n  }\n  return null;\n});\n\n// Total debt last 4 quarters\nconst total_debt_last_4q = balanceQuarterly.slice(0, 4).map(q => {\n  const shortTerm = num(q.shortTermDebt) || 0;\n  const longTerm = num(q.longTermDebt) || 0;\n  const total = shortTerm + longTerm;\n  return total > 0 ? total : null;\n});\n\n// Cash last 4 quarters\nconst cash_last_4q = balanceQuarterly\n  .slice(0, 4)\n  .map(q => num(q.cashAndCashEquivalentsAtCarryingValue));\n\n\nconst debt_latest =\n  total_debt_last_4q && total_debt_last_4q.length\n    ? total_debt_last_4q[0]\n    : null;\n\nconst cash_latest =\n  cash_last_4q && cash_last_4q.length\n    ? cash_last_4q[0]\n    : null;\n\nlet net_debt_latest = null;\nlet net_cash_flag = null;\n\nif (debt_latest !== null && cash_latest !== null) {\n  net_debt_latest = debt_latest - cash_latest;\n  net_cash_flag = net_debt_latest < 0;\n}\n// Issue 3 fix: Revenue growth YoY = TTM vs prior TTM (sum q0-q3 vs sum q4-q7)\nlet revenue_growth_yoy = null;\nconst rev_8q = quarterlyReports.slice(0, 8).map(q => num(q.totalRevenue));\nconst current_ttm_rev = rev_8q.slice(0, 4).every(v => v !== null) ? rev_8q.slice(0, 4).reduce((a, b) => a + b, 0) : null;\nconst prior_ttm_rev = rev_8q.slice(4, 8).every(v => v !== null) ? rev_8q.slice(4, 8).reduce((a, b) => a + b, 0) : null;\nif (current_ttm_rev !== null && prior_ttm_rev !== null && prior_ttm_rev !== 0) {\n  revenue_growth_yoy = Number((((current_ttm_rev - prior_ttm_rev) / prior_ttm_rev) * 100).toFixed(2));\n}\n\n// ---- FIX 1: total_debt_ttm should come from BALANCE sheet (latest quarter) ----\nconst latestBQ = balanceQuarterly && balanceQuarterly.length ? balanceQuarterly[0] : null;\n\nlet total_debt_ttm = null;\nif (latestBQ) {\n  const shortTerm = num(latestBQ.shortTermDebt) || 0;\n  const longTerm = num(latestBQ.longTermDebt) || 0;\n  const total = shortTerm + longTerm;\n  total_debt_ttm = total > 0 ? total : null;\n}\n\n// ---- FIX 2: operating margin can be decimal (0.12) OR percent (12.0) ----\nlet operating_margin_ttm = num(overview.OperatingMarginTTM);\nif (operating_margin_ttm !== null) {\n  // if it's a fraction, convert to percent\n  if (Math.abs(operating_margin_ttm) <= 1.5) {\n    operating_margin_ttm = operating_margin_ttm * 100;\n  }\n  operating_margin_ttm = Number(operating_margin_ttm.toFixed(2));\n}\n\n// ---- FIX 3: fcf_ttm (real FCF) only if you have CASH_FLOW endpoint ----\n// Alpha Vantage doesn't reliably give FCF in OVERVIEW.\n// Compute: FCF = OperatingCashflow + CapitalExpenditures (capex is usually negative)\nlet fcf_ttm = null;\n\nconst cfAnnual = cashflow.annualReports || [];\nconst cfQuarterly = cashflow.quarterlyReports || [];\n\nlet ocf = null;\nlet capex = null;\n\nif (cfAnnual.length) {\n  ocf = num(cfAnnual[0].operatingCashflow);\n  capex = num(cfAnnual[0].capitalExpenditures);\n}\n\nif ((ocf === null || capex === null) && cfQuarterly.length >= 4) {\n  const last4 = cfQuarterly.slice(0,4);\n  const ocfList = last4.map(r=>num(r.operatingCashflow));\n  const capexList = last4.map(r=>num(r.capitalExpenditures));\n\n  if (ocfList.every(v=>v!==null) && capexList.every(v=>v!==null)) {\n    ocf = ocfList.reduce((a,b)=>a+b,0);\n    capex = capexList.reduce((a,b)=>a+b,0);\n  }\n}\n\nif (ocf!==null && capex!==null) fcf_ttm = ocf + capex;\n\n// Gross margin TTM (from overview fields)\nlet gross_margin_ttm = null;\nconst grossProfitTTM = num(overview.GrossProfitTTM);\nconst revenueTTM = num(overview.RevenueTTM);\nif (grossProfitTTM !== null && revenueTTM !== null && revenueTTM !== 0) {\n  gross_margin_ttm = Number(((grossProfitTTM / revenueTTM) * 100).toFixed(2));\n}\n// Bug 6 fix: only compute TTM if all 4 quarters have valid data\nconst net_income_ttm = net_income_last_4q.length === 4 && net_income_last_4q.every(v => v !== null)\n  ? net_income_last_4q.reduce((a, b) => a + b, 0)\n  : null;\n// --------------------\n// Piotroski F-Score (0-9) using annualReports[0] vs annualReports[1]\n// If a component can't be computed, it scores 0 (conservative).\n// --------------------\nfunction safeDiv(a, b) {\n  if (a === null || b === null || b === 0) return null;\n  return a / b;\n}\n\nconst incA = income.annualReports || [];\nconst balA = balance.annualReports || [];\nconst cfA  = cashflow.annualReports || [];\n\nconst y0_inc = incA.length >= 1 ? incA[0] : null;\nconst y1_inc = incA.length >= 2 ? incA[1] : null;\n\nconst y0_bal = balA.length >= 1 ? balA[0] : null;\nconst y1_bal = balA.length >= 2 ? balA[1] : null;\n\nconst y0_cf  = cfA.length >= 1 ? cfA[0] : null;\nconst y1_cf  = cfA.length >= 2 ? cfA[1] : null;\n\nlet f_score = 0;\nconst f_detail = {}; // optional debug\n\n// Helper getters\nconst NI0 = y0_inc ? num(y0_inc.netIncome) : null;\nconst NI1 = y1_inc ? num(y1_inc.netIncome) : null;\n\nconst TA0 = y0_bal ? num(y0_bal.totalAssets) : null;\nconst TA1 = y1_bal ? num(y1_bal.totalAssets) : null;\n\nconst CFO0 = y0_cf ? num(y0_cf.operatingCashflow) : null;\nconst CFO1 = y1_cf ? num(y1_cf.operatingCashflow) : null;\n\nconst LTD0 = y0_bal ? num(y0_bal.longTermDebt) : null;\nconst LTD1 = y1_bal ? num(y1_bal.longTermDebt) : null;\n\nconst CA0 = y0_bal ? num(y0_bal.totalCurrentAssets) : null;\nconst CA1 = y1_bal ? num(y1_bal.totalCurrentAssets) : null;\n\nconst CL0 = y0_bal ? num(y0_bal.totalCurrentLiabilities) : null;\nconst CL1 = y1_bal ? num(y1_bal.totalCurrentLiabilities) : null;\n\nconst REV0 = y0_inc ? num(y0_inc.totalRevenue) : null;\nconst REV1 = y1_inc ? num(y1_inc.totalRevenue) : null;\n\nconst GP0 = y0_inc ? num(y0_inc.grossProfit) : null;\nconst GP1 = y1_inc ? num(y1_inc.grossProfit) : null;\n\n// Shares outstanding (dilution check)\n// Prefer annual balance sheet shares if available; otherwise we can't score it reliably.\nconst SH0 = y0_bal ? num(y0_bal.commonStockSharesOutstanding) : null;\nconst SH1 = y1_bal ? num(y1_bal.commonStockSharesOutstanding) : null;\n\n// ---- PROFITABILITY (4) ----\n// F1: Positive net income\nif (NI0 !== null && NI0 > 0) { f_score++; f_detail.F1_NI_pos = 1; } else f_detail.F1_NI_pos = 0;\n\n// F2: Positive CFO\nif (CFO0 !== null && CFO0 > 0) { f_score++; f_detail.F2_CFO_pos = 1; } else f_detail.F2_CFO_pos = 0;\n\n// F3: ROA improving (NI/TA)\nconst ROA0 = safeDiv(NI0, TA0);\nconst ROA1 = safeDiv(NI1, TA1);\nif (ROA0 !== null && ROA1 !== null && ROA0 > ROA1) { f_score++; f_detail.F3_ROA_up = 1; } else f_detail.F3_ROA_up = 0;\n\n// F4: Accruals: CFO > NI\nif (CFO0 !== null && NI0 !== null && CFO0 > NI0) { f_score++; f_detail.F4_CFO_gt_NI = 1; } else f_detail.F4_CFO_gt_NI = 0;\n\n// ---- LEVERAGE / LIQUIDITY (3) ----\n// F5: Lower long-term debt\nif (LTD0 !== null && LTD1 !== null && LTD0 < LTD1) { f_score++; f_detail.F5_LTD_down = 1; } else f_detail.F5_LTD_down = 0;\n\n// F6: Higher current ratio (CA/CL)\nconst CR0 = safeDiv(CA0, CL0);\nconst CR1 = safeDiv(CA1, CL1);\nif (CR0 !== null && CR1 !== null && CR0 > CR1) { f_score++; f_detail.F6_CR_up = 1; } else f_detail.F6_CR_up = 0;\n\n// F7: No dilution (shares not increased)\nif (SH0 !== null && SH1 !== null && SH0 <= SH1) { f_score++; f_detail.F7_no_dilution = 1; } else f_detail.F7_no_dilution = 0;\n\n// ---- OPERATING EFFICIENCY (2) ----\n// F8: Gross margin improved (GP/REV)\nconst GM0 = (GP0 !== null && REV0) ? safeDiv(GP0, REV0) : null;\nconst GM1 = (GP1 !== null && REV1) ? safeDiv(GP1, REV1) : null;\nif (GM0 !== null && GM1 !== null && GM0 > GM1) { f_score++; f_detail.F8_GM_up = 1; } else f_detail.F8_GM_up = 0;\n\n// F9: Asset turnover improved (REV/TA)\nconst AT0 = safeDiv(REV0, TA0);\nconst AT1 = safeDiv(REV1, TA1);\nif (AT0 !== null && AT1 !== null && AT0 > AT1) { f_score++; f_detail.F9_AT_up = 1; } else f_detail.F9_AT_up = 0;\n\n// If we lack the minimum annual data, f_score is still computed conservatively (likely low).\n// You can also flag completeness:\nconst f_score_data_ok = !!(y0_inc && y1_inc && y0_bal && y1_bal && y0_cf && y1_cf);\n\n// ─── IDEA 1: Graham Number ───────────────────────────────────────────────\nconst eps_val  = num(overview.EPS);\nconst bvps_val = num(overview.BookValue);\nlet graham_number = null;\nif (eps_val !== null && bvps_val !== null && eps_val > 0 && bvps_val > 0) {\n  graham_number = Number(Math.sqrt(22.5 * eps_val * bvps_val).toFixed(2));\n}\n\n// ─── IDEA 4: DCF Anchor (for growth/transition stocks) ───────────────────\nconst disc_rate_map = {\n  'Technology': 0.10, 'Financials': 0.09, 'Utilities': 0.08,\n  'Consumer Staples': 0.09, 'Consumer Discretionary': 0.11,\n  'Health Care': 0.10, 'Industrials': 0.10, 'Materials': 0.10,\n  'Energy': 0.11, 'Communication Services': 0.10, 'Real Estate': 0.09\n};\nconst disc_rate = disc_rate_map[sector] || 0.10;\nconst terminal_growth = 0.03;\nlet dcf_anchor = null;\nif (fcf_ttm !== null && fcf_ttm > 0 && disc_rate > terminal_growth) {\n  const fcf_growth = (current_ttm_rev !== null && prior_ttm_rev !== null && prior_ttm_rev > 0)\n    ? Math.min((current_ttm_rev - prior_ttm_rev) / prior_ttm_rev, 0.30)\n    : 0.08; // default 8% if growth unknown\n  const fcf5 = fcf_ttm * Math.pow(1 + fcf_growth, 5);\n  dcf_anchor = Math.round(fcf5 / (disc_rate - terminal_growth));\n}\n\n// ─── IDEA 5: Sector-relative P/E ─────────────────────────────────────────\nconst sectorPE = {\n  'Technology': 25, 'Financials': 12, 'Utilities': 15,\n  'Consumer Staples': 19, 'Consumer Discretionary': 22,\n  'Health Care': 20, 'Industrials': 18, 'Materials': 16,\n  'Energy': 14, 'Communication Services': 21, 'Real Estate': 17\n};\nconst sector_median_pe = sectorPE[sector] || 18;\n\nreturn {\n  json: {\n    stock: $json.stock || overview.Symbol || overview.symbol || \"UNKNOWN\",\n    eps_current: num(overview.EPS),\n    bvps_current: num(overview.BookValue),\n    shares_outstanding: num(overview.SharesOutstanding),\n    revenue_ttm: revenueTTM,\n\n    // fixed\n    total_debt_ttm: total_debt_ttm,\n\n    gross_margin_ttm: gross_margin_ttm,\n    operating_margin_ttm: operating_margin_ttm,\n\n    // fixed: real FCF only if CASH_FLOW is available; otherwise stays null\n    fcf_ttm: fcf_ttm,\n    net_income_ttm,\n    revenue_last_4q: revenue_last_4q.every(v => v !== null) ? revenue_last_4q : null,\n    net_income_last_4q: net_income_last_4q.every(v => v !== null) ? net_income_last_4q : null,\n    gross_margin_last_4q: gross_margin_last_4q.some(v => v !== null) ? gross_margin_last_4q : null,\n    total_debt_last_4q: total_debt_last_4q.some(v => v !== null) ? total_debt_last_4q : null,\n    cash_last_4q: cash_last_4q.some(v => v !== null) ? cash_last_4q : null,\n    revenue_growth_yoy: revenue_growth_yoy,\n    current_price,\nnet_debt_latest,\ncash_latest,\ndebt_latest,\nnet_cash_flag,\n    f_score,\nf_score_data_ok,\n    sector:sector,\n    graham_number,\n    dcf_anchor,\n    sector_median_pe,\n    cache: {\n      status: \"refreshed\",\n      source: \"api\",\n      last_updated: $json.last_updated || $json.nowISO || null\n    }\n  }\n};\n"},"typeVersion":2},{"id":"ab0c83f7-372f-486f-9d55-6dc9efb4aeeb","name":"Clean Old Financial","type":"n8n-nodes-base.code","position":[2256,1216],"parameters":{"jsCode":"const r = $json.cacheRow || $json;\n\nreturn [{\n  json: {\n    stock: r.stock,\n      eps_current: Number(r.eps_current) || null,\n      bvps_current: Number(r.bvps_current) || null,\n      shares_outstanding: Number(r.shares_outstanding) || null,\n      revenue_ttm: Number(r.revenue_ttm) || null,\n      total_debt_ttm: Number(r.total_debt_ttm) || null,\n      gross_margin_ttm: Number(r.gross_margin_ttm) || null,\n      operating_margin_ttm: Number(r.operating_margin_ttm) || null,\n      fcf_ttm: r.fcf_ttm !== \"\" ? Number(r.fcf_ttm) : null,\n      revenue_last_4q: r.revenue_last_4q || null,\n      net_income_last_4q: r.net_income_last_4q || null,\n      gross_margin_last_4q: r.gross_margin_last_4q || null,\n      total_debt_last_4q: r.total_debt_last_4q || null,\n      cash_last_4q: r.cash_last_4q || null,\n      revenue_growth_yoy: Number(r.revenue_growth_yoy) || null,\n      current_price: Number(r.current_price) || null,\n      net_income_ttm: Number(r.net_income_ttm) || null,\n      f_score: Number(r.f_score) || null,\n      f_score_data_ok: Boolean(r.f_score_data_ok)|| null,\n      sector: r.sector || null,\n      net_debt_latest: (r.net_debt_latest !== undefined && r.net_debt_latest !== '') ? Number(r.net_debt_latest) : null,\n      net_cash_flag: (r.net_cash_flag !== undefined && r.net_cash_flag !== '') ? (r.net_cash_flag === 'true' || r.net_cash_flag === true) : null,\n      graham_number: r.graham_number !== undefined && r.graham_number !== '' ? Number(r.graham_number) : null,\n      dcf_anchor: r.dcf_anchor !== undefined && r.dcf_anchor !== '' ? Number(r.dcf_anchor) : null,\n      sector_median_pe: r.sector_median_pe !== undefined && r.sector_median_pe !== '' ? Number(r.sector_median_pe) : 18,\n    cache: {\n      status: \"fresh\",\n      source: \"cache\",\n      last_updated: r.last_updated || null\n    }\n  }\n}];"},"typeVersion":2},{"id":"9fe24b32-26ac-43f8-9a90-4acd0668fb2a","name":"Merge New Financial","type":"n8n-nodes-base.code","position":[2576,864],"parameters":{"jsCode":"return $input.all();"},"typeVersion":2},{"id":"f9ba099d-aba3-4ff4-81e0-00da596157bc","name":"alphavantage - Current Price","type":"n8n-nodes-base.httpRequest","position":[976,1056],"parameters":{"url":"=https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.4},{"id":"cca34beb-fd09-4e4d-b741-d59c739bf215","name":"Clean Current Price","type":"n8n-nodes-base.code","position":[1168,1040],"parameters":{"jsCode":"const inJson = $json || {};\nconst q = inJson[\"Global Quote\"] || {};   // ← this was missing\n\nconst stock =\n  q[\"01. symbol\"] ||\n  inJson.stock ||\n  inJson.Symbol ||\n  inJson.symbol ||\n  \"UNKNOWN\";\n\nreturn [\n  {\n    json: {\n      stock: stock,\n      current_price: Number(q[\"05. price\"]) || null\n    }\n  }\n];"},"typeVersion":2},{"id":"f828aa53-5314-4e78-bf0b-064e81d27d5b","name":"Merge5","type":"n8n-nodes-base.merge","position":[1520,928],"parameters":{"mode":"combine","options":{},"fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"4a42def3-aad5-4e9b-b317-6aa5665c2360","name":"Clean the news","type":"n8n-nodes-base.code","position":[688,1760],"parameters":{"mode":"runOnceForEachItem","jsCode":"// Carry stock forward\nconst stock =\n  $json.stock ??\n  $json.ticker ??\n  ($node[\"loop_over_tickers\"]?.json?.stock ?? \"\");\n\n// Find XML - CHECK sa_xml FIRST (your custom output field)\nlet saXml =\n  (typeof $json.sa_xml === \"string\" && $json.sa_xml.trim()) ? $json.sa_xml.trim() :\n  (typeof $json.body === \"string\" && $json.body.trim()) ? $json.body.trim() :\n  (typeof $json.data === \"string\" && $json.data.trim()) ? $json.data.trim() :\n  (typeof $json.responseBody === \"string\" && $json.responseBody.trim()) ? $json.responseBody.trim() :\n  (typeof $json.text === \"string\" && $json.text.trim()) ? $json.text.trim() :\n  null;\n\n// If your HTTP node returns binary, try decode first binary field\nif (!saXml && $binary) {\n  const key = Object.keys($binary)[0];\n  const b64 = key && $binary[key] && $binary[key].data ? $binary[key].data : null;\n  if (b64) {\n    saXml = Buffer.from(b64, \"base64\").toString(\"utf8\").trim();\n    if (!saXml) saXml = null;\n  }\n}\n\n// ALWAYS return json as an OBJECT\nreturn {\n  json: {\n    stock,\n    sa_xml: saXml,\n    sa_ok: !!saXml\n  }\n};"},"typeVersion":2},{"id":"140b736e-ee4f-4f28-a720-6081177547b6","name":"return the news","type":"n8n-nodes-base.code","position":[1344,1856],"parameters":{"mode":"runOnceForEachItem","jsCode":"return {\n  json: {\n    stock: $json.stock ?? \"\",\n    seekingAlphaWindowHours: 80,\n    seekingAlphaCount: 0,\n    seekingAlphaNews: [],\n    seekingAlphaNewsText: \"\",\n    seekingAlphaError: $json.sa_error ?? \"No XML to parse\"\n  }\n};\n"},"typeVersion":2},{"id":"15b3be64-066e-4118-b9a6-a138109287f2","name":"Final version of news","type":"n8n-nodes-base.code","position":[1792,1744],"parameters":{"jsCode":"// ---- CONFIG ----\nconst windowHours = 96;                 // recent window (Issue 4: 4-day safety window)\nconst fallbackCount = 3;               // if no recent news, return latest N anyway\nconst now = Date.now();\nconst cutoff = now - windowHours * 3600 * 1000;\n\n// ---- GET STOCK FROM INPUT ----\nconst stock =\n  $input.first()?.json?.stock ??\n  $json.stock ??\n  \"\";\n\n// ---- INPUT PATH ----\nconst items =\n  $json?.rss?.channel?.item ??\n  $json?.channel?.item ??\n  $json?.item ??\n  [];\n\nconst arr = Array.isArray(items) ? items : [items];\n\n// ---- HELPERS ----\nfunction cleanHtml(s) {\n  if (!s) return \"\";\n  return String(s)\n    .replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n    .replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n    .replace(/<\\/?[^>]+>/g, \" \")\n    .replace(/\\s+/g, \" \")\n    .trim();\n}\n\nfunction toMillis(dateStr) {\n  const t = Date.parse(dateStr);\n  return Number.isFinite(t) ? t : NaN;\n}\n\nfunction getLink(i) {\n  const l = i.link;\n  if (!l) return \"\";\n  if (typeof l === \"string\") return l;\n  if (Array.isArray(l)) return getLink({ link: l[0] });\n  return l[\"#text\"] || l.href || l?.$?.href || \"\";\n}\n\n// ---- PARSE ALL ITEMS (no time filter yet) ----\nconst parsedAll = arr\n  .map(i => {\n    const publishedAt = i.pubDate || i.published || i.date || \"\";\n    const t = toMillis(publishedAt);\n    return {\n      source: \"SeekingAlpha_RSS\",\n      title: cleanHtml(i.title),\n      link: getLink(i),\n      publishedAt,\n      publishedAtMs: t,\n      snippet: cleanHtml(i.description || i.summary || i[\"content:encoded\"])\n    };\n  })\n  .filter(n => n.title && n.link && Number.isFinite(n.publishedAtMs))\n  .sort((a, b) => b.publishedAtMs - a.publishedAtMs);\n\n// ---- DEDUPE (keep newest first) ----\nconst seen = new Set();\nconst dedupedAll = [];\nfor (const n of parsedAll) {\n  if (seen.has(n.link)) continue;\n  seen.add(n.link);\n  dedupedAll.push(n);\n}\n\n// ---- RECENT WINDOW ----\nconst recent = dedupedAll.filter(n => n.publishedAtMs >= cutoff);\n\n// ---- FINAL SELECTION ----\n// If no recent items, fallback to latest N overall\nconst selected = (recent.length > 0) ? recent : dedupedAll.slice(0, fallbackCount);\nconst hasRecent = recent.length > 0;\n\n// ---- CREATE COMPACT TEXT ----\nconst seekingAlphaNewsText = selected.map(n => {\n  const shortSnippet = n.snippet.length > 240 ? n.snippet.slice(0, 240) + \"…\" : n.snippet;\n  return `- ${n.publishedAt} | ${n.title}\\n  ${n.link}\\n  Snippet: ${shortSnippet}`;\n}).join(\"\\n\\n\");\n\nreturn [\n  {\n    json: {\n      stock,\n      seekingAlphaWindowHours: windowHours,\n\n      // signals for the AI prompt later\n      seekingAlphaHasRecent: hasRecent,\n      seekingAlphaRecentCount: recent.length,\n\n      // what you actually pass forward\n      seekingAlphaCount: selected.length,\n      seekingAlphaNews: selected.map(({ publishedAtMs, ...rest }) => rest),\n      seekingAlphaNewsText,\n\n      // optional: last coverage date (useful for prompt)\n      seekingAlphaLatestDate: dedupedAll[0]?.publishedAt ?? null\n    }\n  }\n];"},"typeVersion":2},{"id":"114089bf-0ee2-4b90-81d4-4f3312dde47b","name":"Clean Up ChatGPT","type":"n8n-nodes-base.code","position":[3248,944],"parameters":{"mode":"runOnceForEachItem","jsCode":"let parsed = $json;\nconst t = $json?.output?.[0]?.content?.[0]?.text;\nif (t) {\n  if (typeof t === \"string\") {\n    try { parsed = JSON.parse(t); } catch(e) { parsed = $json; }\n  } else if (typeof t === \"object\") {\n    parsed = t;\n  }\n}\n\nconst stock = parsed?.stock || $json?.stock || \"UNKNOWN\";\nconst source = $('Merge1').first().json;\n// Carry all financial fields forward from the original input\nconst financials = {\n  current_price:        source?.current_price        ?? null,\n  eps_current:          source?.eps_current          ?? null,\n  bvps_current:         source?.bvps_current         ?? null,\n  revenue_last_4q:      source?.revenue_last_4q      ?? null,\n  net_income_last_4q:   source?.net_income_last_4q   ?? null,\n  gross_margin_last_4q: source?.gross_margin_last_4q ?? null,\n  total_debt_last_4q:   source?.total_debt_last_4q   ?? null,\n  net_debt_latest:      source?.net_debt_latest      ?? null,\n  net_cash_flag:        source?.net_cash_flag        ?? null,\n  operating_margin_ttm: source?.operating_margin_ttm ?? null,\n  fcf_ttm:              source?.fcf_ttm              ?? null,\n  revenue_growth_yoy:   source?.revenue_growth_yoy   ?? null,\n  f_score_data_ok:      source?.f_score_data_ok      ?? null,\n  sector:               source?.sector               ?? null,\n  seekingAlphaNewsText: source?.seekingAlphaNewsText ?? null,\n};\n\nreturn {\n  json: {\n    chatgpt_result: parsed,\n    stock,\n    ...financials\n  }\n};"},"typeVersion":2},{"id":"44ad2133-7030-4be8-8be5-abedd0bcaa53","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[128,1392],"parameters":{"color":5,"width":2760,"height":716,"content":"# 2. SeekingAlpha News Data Retrieval & Validation\nGet Articles from Seeking Alpha:\nFor each ticker, the workflow sends an HTTP request to retrieve recent Seeking Alpha articles within a defined time window. These articles provide analyst-grade insights and market context.\nTicker Validation Check:\nA conditional node verifies whether valid news results were returned and whether the ticker is recognized.\nIf no articles are found → the ticker may be invalid or have no recent coverage.\nHandle Invalid Tickers:\nIf the ticker is invalid or returns no data, the workflow logs the ticker in the Google Sheet with an \"Invalid Ticker\" status. This enables tracking of symbols that cannot be processed.\nNews Aggregation:\nFor valid tickers, multiple articles are merged into a single structured text block.\nThis prepares consolidated news context for the AI model to analyze in one step"},"typeVersion":1},{"id":"b12ed2e1-7cf3-44a1-8581-8f3df09db54e","name":"Wait","type":"n8n-nodes-base.wait","position":[7936,1792],"webhookId":"597c4bac-8b92-44a2-a400-b51bb547aed5","parameters":{"amount":15},"typeVersion":1.1},{"id":"515a0107-9bec-4b4f-a268-6235ccef31bb","name":"Wait1","type":"n8n-nodes-base.wait","position":[784,416],"webhookId":"dd03763a-0852-42da-8b88-5a4713e68732","parameters":{"amount":15},"typeVersion":1.1},{"id":"46a312ad-baa3-4690-90f3-87306944c36d","name":"Wait2","type":"n8n-nodes-base.wait","position":[784,576],"webhookId":"e5eac015-0dc2-4b35-9cc2-938e03e243c7","parameters":{"amount":20},"typeVersion":1.1},{"id":"0093afdc-5c76-4168-8619-66361a4f88f5","name":"Wait3","type":"n8n-nodes-base.wait","position":[768,736],"webhookId":"b166f73a-0b33-44e6-877d-12ee5e586fa3","parameters":{"amount":30},"typeVersion":1.1},{"id":"e20a243d-c6c9-4ff4-a109-0e7c19432251","name":"Wait4","type":"n8n-nodes-base.wait","position":[768,880],"webhookId":"408c9cc1-89de-4a74-9f41-7dec30315be1","parameters":{"amount":25},"typeVersion":1.1},{"id":"627df88e-50b9-47b5-aa95-7066673faa2a","name":"Wait5","type":"n8n-nodes-base.wait","position":[768,1056],"webhookId":"2c8aa522-02cf-44c6-9396-d0aefb8428b0","parameters":{"amount":20},"typeVersion":1.1},{"id":"ccad2786-3a86-4016-b867-27b9f900d45f","name":"Clean Current Price1","type":"n8n-nodes-base.code","position":[1168,1232],"parameters":{"jsCode":"const inJson = $json || {};\nconst q = inJson[\"Global Quote\"] || {};   // ← this was missing\n\nconst stock =\n  q[\"01. symbol\"] ||\n  inJson.stock ||\n  inJson.Symbol ||\n  inJson.symbol ||\n  \"UNKNOWN\";\n\nreturn [\n  {\n    json: {\n      stock: stock,\n      current_price: Number(q[\"05. price\"]) || null\n    }\n  }\n];"},"typeVersion":2},{"id":"2facabce-7fb9-4134-b876-8b2b3c37b901","name":"alphavantage - Current Price Second","type":"n8n-nodes-base.httpRequest","position":[976,1232],"parameters":{"url":"=https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={{ $('loop_over_tickers').item.json.stock }}&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.4},{"id":"c7338001-85df-4e14-be0d-68224de337e1","name":"Update row in sheet1","type":"n8n-nodes-base.googleSheets","position":[1488,1216],"parameters":{"columns":{"value":{"stock":"={{ $json.stock }}","row_number":0,"last_updated":"={{ new Date().toISOString().split(\"T\")[0] }}","current_price":"={{ $json.current_price }}"},"schema":[{"id":"stock","type":"string","display":true,"removed":false,"required":false,"displayName":"stock","defaultMatch":false,"canBeUsedToMatch":true},{"id":"cache_key","type":"string","display":true,"removed":false,"required":false,"displayName":"cache_key","defaultMatch":false,"canBeUsedToMatch":true},{"id":"data_source","type":"string","display":true,"removed":false,"required":false,"displayName":"data_source","defaultMatch":false,"canBeUsedToMatch":true},{"id":"last_updated","type":"string","display":true,"required":false,"displayName":"last_updated","defaultMatch":false,"canBeUsedToMatch":true},{"id":"eps_current","type":"string","display":true,"removed":true,"required":false,"displayName":"eps_current","defaultMatch":false,"canBeUsedToMatch":true},{"id":"bvps_current","type":"string","display":true,"removed":true,"required":false,"displayName":"bvps_current","defaultMatch":false,"canBeUsedToMatch":true},{"id":"shares_outstanding","type":"string","display":true,"removed":true,"required":false,"displayName":"shares_outstanding","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_ttm","type":"string","display":true,"removed":true,"required":false,"displayName":"revenue_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"total_debt_ttm","type":"string","display":true,"removed":true,"required":false,"displayName":"total_debt_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"gross_margin_ttm","type":"string","display":true,"removed":true,"required":false,"displayName":"gross_margin_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"operating_margin_ttm","type":"string","display":true,"removed":true,"required":false,"displayName":"operating_margin_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"fcf_ttm","type":"string","display":true,"removed":true,"required":false,"displayName":"fcf_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_last_4q","type":"string","display":true,"removed":true,"required":false,"displayName":"revenue_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_income_last_4q","type":"string","display":true,"removed":true,"required":false,"displayName":"net_income_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"gross_margin_last_4q","type":"string","display":true,"removed":true,"required":false,"displayName":"gross_margin_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"total_debt_last_4q","type":"string","display":true,"removed":true,"required":false,"displayName":"total_debt_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"cash_last_4q","type":"string","display":true,"removed":true,"required":false,"displayName":"cash_last_4q","defaultMatch":false,"canBeUsedToMatch":true},{"id":"net_income_ttm","type":"string","display":true,"removed":true,"required":false,"displayName":"net_income_ttm","defaultMatch":false,"canBeUsedToMatch":true},{"id":"current_price","type":"string","display":true,"removed":false,"required":false,"displayName":"current_price","defaultMatch":false,"canBeUsedToMatch":true},{"id":"revenue_growth_yoy","type":"string","display":true,"removed":true,"required":false,"displayName":"revenue_growth_yoy","defaultMatch":false,"canBeUsedToMatch":true},{"id":"f_score","type":"string","display":true,"removed":true,"required":false,"displayName":"f_score","defaultMatch":false,"canBeUsedToMatch":true},{"id":"f_score_data_ok","type":"string","display":true,"removed":true,"required":false,"displayName":"f_score_data_ok","defaultMatch":false,"canBeUsedToMatch":true},{"id":"sector","type":"string","display":true,"removed":true,"required":false,"displayName":"sector","defaultMatch":false,"canBeUsedToMatch":true},{"id":"row_number","type":"number","display":true,"removed":false,"readOnly":true,"required":false,"displayName":"row_number","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":["stock"],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"update","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit#gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1IQeSDT8cqPKmpJgDYIl14fPM_lON_cW1W73KJNn65z8/edit?usp=drivesdk","cachedResultName":"Financial_Data_Cache"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"typeVersion":4.7},{"id":"2ff82136-78e2-41d6-af28-0491362c752d","name":"Send a text message","type":"n8n-nodes-base.telegram","position":[-240,1008],"webhookId":"cc5fb225-de15-428b-90e0-d70866430783","parameters":{"text":"={{ $json.summary_message }}","chatId":"123456789","additionalFields":{}},"credentials":{"telegramApi":{"id":"credential-id","name":"Telegram account"}},"typeVersion":1.2},{"id":"a0145d78-0606-4872-8900-f6b092a3cafb","name":"Merge6","type":"n8n-nodes-base.merge","position":[4448,976],"parameters":{"mode":"combine","options":{},"joinMode":"keepEverything","fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"82d16d36-aa00-4f72-9b86-067b921e47e5","name":"Clean up Gemini","type":"n8n-nodes-base.code","position":[3248,1296],"parameters":{"mode":"runOnceForEachItem","jsCode":"let parsed = $json;\nif ($json.output && typeof $json.output === \"string\") {\n  try {\n    const cleaned = $json.output.replace(/```json\\n?|```/g, \"\").trim();\n    parsed = JSON.parse(cleaned);\n  } catch(e) { parsed = $json; }\n} else if ($json.content?.parts?.[0]?.text) {\n  try {\n    parsed = JSON.parse($json.content.parts[0].text);\n  } catch(e) { parsed = $json; }\n}\nif (parsed && parsed.gemini_result) parsed = parsed.gemini_result;\n\nconst stock = parsed?.stock || $json?.stock || \"UNKNOWN\";\nconst source = $('Merge1').first().json;\n// Carry all financial fields forward from the original input\nconst financials = {\n  current_price:        source?.current_price        ?? null,\n  eps_current:          source?.eps_current          ?? null,\n  bvps_current:         source?.bvps_current         ?? null,\n  revenue_last_4q:      source?.revenue_last_4q      ?? null,\n  net_income_last_4q:   source?.net_income_last_4q   ?? null,\n  gross_margin_last_4q: source?.gross_margin_last_4q ?? null,\n  total_debt_last_4q:   source?.total_debt_last_4q   ?? null,\n  net_debt_latest:      source?.net_debt_latest      ?? null,\n  net_cash_flag:        source?.net_cash_flag        ?? null,\n  operating_margin_ttm: source?.operating_margin_ttm ?? null,\n  fcf_ttm:              source?.fcf_ttm              ?? null,\n  revenue_growth_yoy:   source?.revenue_growth_yoy   ?? null,\n  f_score_data_ok:      source?.f_score_data_ok      ?? null,\n  sector:               source?.sector               ?? null,\n  seekingAlphaNewsText: source?.seekingAlphaNewsText ?? null,\n};\n\nreturn {\n  json: {\n    gemini_result: parsed,\n    stock,\n    ...financials\n  }\n};"},"typeVersion":2},{"id":"168a3bf9-d458-46ac-9659-3c686e784f9e","name":"Code in JavaScript3","type":"n8n-nodes-base.code","position":[4608,976],"parameters":{"jsCode":"const items_in = $input.all();\nconst today = new Date().toISOString().slice(0, 10);\n\nlet bull = null;\nlet bear = null;\n\nfor (const it of items_in) {\n  const j = it.json || {};\n  if (j.chatgpt_result) {\n    const r = j.chatgpt_result;\n    if (r.model === \"CHATGPT_BULL\") bull = {\n      ...r,\n      gap_pct:         j.gap_pct,\n      verdict_chatgpt: j.verdict_chatgpt,\n      verdict_gemini:  j.verdict_gemini,\n      current_price:   j.current_price   // ← FIX: carry current_price\n    };\n  }\n  if (j.gemini_result) {\n    const r = j.gemini_result;\n    if (r.model === \"GEMINI_BEAR\") bear = {\n      ...r,\n      gap_pct:         j.gap_pct,\n      verdict_chatgpt: j.verdict_chatgpt,\n      verdict_gemini:  j.verdict_gemini,\n      current_price:   j.current_price   // ← FIX: carry current_price\n    };\n  }\n}\n\n// ── Conviction label (matches consensus path) ─────────────────────────\nfunction getConviction(gap) {\n  if (gap < 10) return \"HIGH\";\n  if (gap < 20) return \"MEDIUM\";\n  return \"LOW\";\n}\n\n// ── Error fallback — now includes skip_row + enrichment fields ────────\nif (!bull || !bear) {\n  return [{\n    json: {\n      stock:             \"\",\n      date:              today,\n      pt_bear:           0,\n      pt_base:           0,\n      pt_bull:           0,\n      f_score:           null,\n      confidence:        0,\n      rationale:         \"Tiebreaker failed — missing bull or bear result\",\n      model:             \"ERROR\",\n      current_price:     0,\n      row_key:           `ERROR_${today}`,\n      skip_row:          true,             // ← FIX: was missing\n      resolution_method: \"TIEBREAKER\",\n      conviction:        \"NONE\",\n      gap_pct:           0,\n      verdict_chatgpt:   null,\n      verdict_gemini:    null\n    }\n  }];\n}\n\nconst stock           = bull.stock  || bear.stock  || \"UNKNOWN\";\nconst true_base       = parseFloat(((Number(bull.pt_base) + Number(bear.pt_base)) / 2).toFixed(2));\nconst true_bull       = parseFloat(Number(bull.pt_bull).toFixed(2));\nconst true_bear       = parseFloat(Number(bear.pt_bear).toFixed(2));\nconst avg_conf        = Math.round((Number(bull.confidence) + Number(bear.confidence)) / 2);\nconst f_score         = bull.f_score ?? bear.f_score ?? null;\nconst gap_pct         = Number(bull.gap_pct         ?? bear.gap_pct         ?? 0);\nconst verdict_chatgpt = bull.verdict_chatgpt ?? bear.verdict_chatgpt ?? null;\nconst verdict_gemini  = bull.verdict_gemini  ?? bear.verdict_gemini  ?? null;\nconst current_price   = Number(bull.current_price || bear.current_price || 0); // ← FIX\n\n// ── Final verdict (now works because current_price is real) ───────────\nconst upside = current_price > 0 ? (true_base - current_price) / current_price : 0;\nlet final_verdict;\nif (upside >= 0.20)       final_verdict = \"BUY\";\nelse if (upside <= -0.15) final_verdict = \"SELL\";\nelse                      final_verdict = \"HOLD\";\n\nconst rationale = `${final_verdict} (tiebreaker) — `\n  + `Bull: ${bull.rationale} | Bear: ${bear.rationale}`;\n\nreturn [{\n  json: {\n    stock,\n    date:              today,\n    pt_bear:           true_bear,\n    pt_base:           true_base,\n    pt_bull:           true_bull,\n    f_score,\n    confidence:        Math.max(20, Math.min(90, avg_conf)),\n    rationale,\n    model:             \"TIEBREAKER\",\n    current_price,                               // ← FIX: added\n    row_key:           `${stock}_${today}_TIEBREAKER`,\n    skip_row:          false,\n    resolution_method: \"TIEBREAKER\",\n    conviction:        getConviction(gap_pct),   // ← FIX: added\n    gap_pct,                                     // ← FIX: added\n    verdict_chatgpt,                             // ← FIX: added\n    verdict_gemini                               // ← FIX: added\n  }\n}];"},"typeVersion":2},{"id":"8c8bd5fb-b1df-4268-851d-2ad14ed48d9b","name":"Merge7","type":"n8n-nodes-base.merge","position":[4768,1200],"parameters":{"mode":"combine","options":{},"joinMode":"keepEverything","fieldsToMatchString":"stock"},"typeVersion":3.2},{"id":"945ec9a1-e480-4bd7-8d5a-c06882413f99","name":"Tide Breaker - Bull","type":"@n8n/n8n-nodes-langchain.openAi","position":[3968,896],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"GPT-4O"},"options":{"textFormat":{"textOptions":{"type":"json_object"}}},"responses":{"values":[{"content":"=YOUR ROLE: You are a GROWTH-FOCUSED bull analyst.\nThis is a TIEBREAKER — two models disagreed on this stock.\nMake the strongest BUY case the data can justify.\n- Select the higher multiple when between tiers\n- Prefer 15% discount when fundamentals are arguable  \n- Classify ambiguous news as POSITIVE\n- Your pt_base = maximum justifiable OPPORTUNITY value\n- Never fabricate data. Stay within input bounds only.\n----\nYou are a Senior Equity Analyst with 50+ years of institutional market experience.\nYou receive structured financial data, news context, and a Quality F-Score and must produce disciplined valuation targets and a risk-adjusted verdict.\nYou must think like an institutional portfolio manager: conservative, risk-first, evidence-based.\n\nINPUT FIELDS AVAILABLE:\n\nSTRUCTURED DATA INPUT (REAL VALUES — USE THESE ONLY)\n{\n\"stock\": \"{{ $json.stock }}\",\n\"current_price\": {{ $json.current_price }},\n\"eps_current\": {{ $json.eps_current }},\n\"bvps_current\": {{ $json.bvps_current }},\n\"revenue_last_4q\": {{ $json.revenue_last_4q }},\n\"net_income_last_4q\": {{ $json.net_income_last_4q }},\n\"gross_margin_last_4q\": {{ $json.gross_margin_last_4q }},\n\"total_debt_last_4q\": {{ $json.total_debt_last_4q }},\n\"net_debt_latest\": {{ $json.net_debt_latest }},\n\"net_cash_flag\": {{ $json.net_cash_flag }},\n\"operating_margin_ttm\": {{ $json.operating_margin_ttm }},\n\"fcf_ttm\": {{ $json.fcf_ttm }},\n\"revenue_growth_yoy\": {{ $json.revenue_growth_yoy }},\n\"f_score\": {{ $json.f_score }},\n\"f_score_data_ok\": {{ $json.f_score_data_ok }},\n\"sector\":  \"{{ $json.sector }}\",\n\"graham_number\": {{ $json.graham_number }},\n\"dcf_anchor\": {{ $json.dcf_anchor }},\n\"sector_median_pe\": {{ $json.sector_median_pe }}\n}\n\nRules:\n\nUse ONLY these values.\n\nIf any field is null → treat as missing.\n\nNever assume missing data.\n\nNever assume share splits, ADR ratios, or alternative listings.\n\nIMPORTANT — NEWS SOURCE PRIORITY:\n\nSeeking Alpha articles (highest credibility signal)\n\nOther news_items\n\nMarket sentiment\n\nPrefer Seeking Alpha for thesis framing, but verify with financial data.\nIf conflict exists, flag uncertainty.\n\nSEEKING ALPHA ARTICLES (Recent 96h window):\n{{ $json.seekingAlphaNewsText }}\n\nANALYSIS TASKS\n\nFundamental Trends\n\nEvaluate revenue_last_4q and net_income_last_4q.\n\nEvaluate debt using total_debt_last_4q and net_debt_latest.\n\nEvaluate margin stability using gross_margin_last_4q and operating_margin_ttm.\n\nIf fcf_ttm is null → state \"FCF unavailable due to missing data.\"\n\nBusiness & Moat\nClassify moat: Brand, Network, Cost, Switching Costs, or IP.\n\nPricing Power\nJudge using margin stability and operating_margin_ttm.\n\nVALUATION FRAMEWORK (CRITICAL SECTION)\n\nA) IMPLIED P/E SANITY CHECK (MANDATORY)\n\nIf eps_current > 0 AND current_price > 0:\n\nimplied_pe = current_price / eps_current\nElse:\n\nimplied_pe = null\n\nEPS VALIDITY RULE:\nIf implied_pe is not null AND implied_pe > 60:\n\nTreat EPS as NOT economically meaningful (growth/transition phase).\n\nEPS multiple valuation is FORBIDDEN.\n\nCompany must be classified as growth_transition.\n\nB) PHASE CLASSIFICATION (MANDATORY — must pick one)\n\n\"mature_profitable\": eps_current > 0 AND (implied_pe is null OR implied_pe ≤ 60)\n\n\"growth_transition\": eps_current ≤ 0 OR implied_pe > 60\n\nMATURE OVERRIDE RULE:\nIf initial classification = growth_transition due to implied_pe > 60 only\n(i.e., eps_current IS > 0):\n  AND revenue_growth_yoy < 15%\n  AND operating_margin_ttm > 10%\n  → Override to mature_profitable\n  → Add note: \"Phase overridden to mature_profitable: \n    low growth + positive margins suggest temporary P/E spike, \n    not structural transition.\"\n\nC) Graham Number (secondary only)\n\nsqrt(22.5 × eps_current × bvps_current)\n\nUse ONLY if ALL of these are true:\neps_current > 0\nbvps_current > 0\nphase = mature_profitable\nbvps_current >= 5.00\nAND (current_price / bvps_current) <= 15\nAND sector ≠ \"Financials\" \n  \nIf bvps_current < 5.00 OR (current_price / bvps_current) > 15:\n  Graham = null → skip Graham entirely → pt_base = pt_eps only\n  Add to rationale: \"Graham skipped: BVPS distorted by buybacks.\"\n\nDo NOT use Graham for growth_transition stocks.\n\nD) BASE TARGET CONSTRUCTION (pt_base)\n\nYOUR ROLE IS BULL — select the HIGHEST justifiable value at every step.\n\nPrimary valuation logic depends on phase.\n\nIF phase = mature_profitable:\nIf EPS positive:\npt_base = eps_current × multiple.\n\nSelect multiple from these RANGES — as bull, pick the TOP of each range:\n  revenue_growth_yoy > 20%  → use between 25× and 30× (bull: pick 30×)\n  revenue_growth_yoy 5–20%  → use between 18× and 25× (bull: pick 25×)\n  revenue_growth_yoy < 5%   → use between 12× and 18× (bull: pick 18×)\n\nSECTOR MULTIPLE FLOORS (apply AFTER selecting multiple):\nIf sector = \"Consumer Staples\" AND selected multiple < 18: raise to 18×\nIf sector = \"Technology\" AND selected multiple < 18: raise to 20×\nIf sector = \"Utilities\" AND selected multiple > 14: cap at 14×\nIf sector = \"Financials\": skip EPS multiple, use pt_base = bvps_current × 1.5\nIf sector = null or missing: no adjustment.\n\nMARGIN OVERRIDE (apply after sector floors):\nIf operating_margin_ttm > 25%\n  AND selected multiple < 20\n  AND sector ≠ \"Utilities\":\n  override multiple to 20×\n  Add note: \"Bull margin floor applied.\"\n\nIf Graham usable:\npt_base = average(pt_base, Graham)\n\nIF phase = growth_transition:\nGrowth-based anchor — as bull, pick the HIGHER multiplier:\n\nLet g = revenue_growth_yoy\n\nIf g is null  → pt_base = current_price × 1.1\nIf g > 60     → pt_base = current_price × 2.5\nIf g > 30     → pt_base = current_price × 2.0\nIf g > 15     → pt_base = current_price × 1.6\nOtherwise     → pt_base = current_price × 1.2\n\nE) FINAL SAFETY RULES\n\npt_base must never be zero.\n\nIf pt_base cannot be determined → pt_base = current_price.\n\npt_base must not exceed current_price × 5 unless revenue_growth_yoy > 60.\n\nRound pt_base to 2 decimals.\n\nNEWS & THESIS IMPACT ANALYSIS (REQUIRED)\n\nAnalyze Seeking Alpha + news_items.\nExtract:\n\nregulatory risks\n\nlegal risks\n\nearnings/guidance changes\n\ncompetitive pressure\n\nthesis change signals\n\nDetermine if thesis strengthened or weakened.\n\nANTI-THESIS (RISK ADJUSTMENT)\n\nIdentify TWO biggest risks using priority:\n\nSeeking Alpha risks\n\nFinancial deterioration\n\nGeneral news\n\nF-Score Handling:\nIf f_score_data_ok is false → reduce confidence by 10.\nIf f_score ≤ 3 → increase risk discount by +10%.\nIf f_score ≥ 7 → increase confidence by +5 (max 90).\n\nBEAR / BULL BANDS\n\nDiscount selection:\n\n15% → strong fundamentals\n\n25% → moderate risk\n\n35% → weak fundamentals or uncertainty\n\nPremium selection:\n\n15% → slow growth\n\n25% → moderate growth\n\n40% → high growth (>20% revenue growth)\n\nNEWS SENTIMENT ADJUSTMENT (apply after selecting base discount/premium):\n\nAnalyze seekingAlphaNewsText and news context. Classify overall sentiment as:\n  POSITIVE: earnings beat, guidance raised, new contracts, analyst upgrades,\n            strong demand signals, competitive wins\n  NEGATIVE: earnings miss, guidance cut, regulatory action, legal risk,\n            competitive threat, leadership change, demand weakness\n  NEUTRAL:  mixed or no material news\n\nThen adjust:\n\nIf sentiment = POSITIVE:\n  reduce discount by 5% (floor at 10%)\n  increase premium by 5%\n  Add to flags: \"News sentiment: POSITIVE — bands adjusted favorably.\"\n\nIf sentiment = NEGATIVE:\n  increase discount by 5% (cap at 40%)\n  reduce premium by 5% (floor at 10%)\n  Add to flags: \"News sentiment: NEGATIVE — bands adjusted conservatively.\"\n\nIf sentiment = NEUTRAL or news missing:\n  no adjustment\n  Add to flags: \"News sentiment: NEUTRAL — no band adjustment.\"\n\nIf seekingAlphaNewsText is null or empty:\n  no adjustment\n  Add to flags: \"No news data — sentiment adjustment skipped.\"\n\nCompute:\npt_bear = pt_base × (1 − discount)\npt_bull = pt_base × (1 + premium)\n\nRound both to 2 decimals.\nPOST-COMPUTATION CHECK:\nIf pt_bull < current_price:\n  Set overvaluation_flag = true\n  Append to rationale: \"WARNING: All price targets below current market \n  price. Model indicates significant overvaluation vs. formula inputs. \n  Manual review recommended before acting on SELL signal.\"\n\nACTION VERDICT LOGIC\n\nBUY:\n\npt_base ≥ 20% upside\n\nand (f_score ≥ 5 OR fundamentals strong)\n\nSELL:\n\npt_base ≤ -15% downside\n\nor (f_score_data_ok true AND f_score ≤ 2)\n\nOtherwise HOLD.\n\nCONFIDENCE SCORING:\n\nStart at 60.\n\n+10 if revenue_growth_yoy > 20%.\n+10 if strong balance sheet (net_cash_flag true or low debt).\n+5 if f_score ≥ 7.\n\n-15 if declining margins or losses.\n-15 if high leverage.\n-20 if major regulatory/legal risk.\n-10 if F-score unavailable.\n-10 if eps_current < 0 (loss-making company, path to profitability unproven).\n+10 if news sentiment = POSITIVE (strong thesis confirmation from Seeking Alpha)\n-15 if news sentiment = NEGATIVE (material risk identified in news/articles)\nClamp between 20 and 90.\n\nOUTPUT FORMAT — STRICT JSON ONLY\n\nReturn ONLY raw JSON object. No markdown. No commentary.\n\n{\n\"stock\": \"{{ $json.stock }}\",\n\"date\": \"{{ $now.format('yyyy-MM-dd') }}\",\n\"pt_bear\": (number),\n\"pt_base\": (number),\n\"pt_bull\": (number),\n\"f_score\": {{ $json.chatgpt_result.f_score }},\n\"confidence\": (20-90),\n\"rationale\": \"A one-sentence summary starting with BUY/HOLD/SELL, followed by the moat, primary risk, and pricing power level.\"\n}"}]},"builtInTools":{}},"credentials":{"openAiApi":{"id":"credential-id","name":"OpenAi account 2"}},"typeVersion":2.1},{"id":"c8d1a050-dc12-4ef3-89c9-cd5ba907ae44","name":"Tide Breaker - Bear","type":"@n8n/n8n-nodes-langchain.googleGemini","position":[3968,1088],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"models/gemini-2.5-pro","cachedResultName":"models/gemini-2.5-pro"},"options":{},"messages":{"values":[{"content":"=OUR ROLE: You are a RISK-FOCUSED bear analyst.\nThis is a TIEBREAKER — two models disagreed on this stock.\nMake the strongest RISK case the data can justify.\n- Select the lower multiple when between tiers\n- Prefer 25% or 35% discount when there is any uncertainty\n- Classify ambiguous news as NEGATIVE\n- Your pt_base = minimum justifiable RISK-ADJUSTED value\n- Never fabricate data. Stay within input bounds only.\n----\nYou are a Senior Equity Analyst with 50+ years of institutional market experience.\nYou receive structured financial data, news context, and a Quality F-Score and must produce disciplined valuation targets and a risk-adjusted verdict.\nYou must think like an institutional portfolio manager: conservative, risk-first, evidence-based.\n\nINPUT FIELDS AVAILABLE:\n\nSTRUCTURED DATA INPUT (REAL VALUES — USE THESE ONLY)\n{\n\"stock\": \"{{ $json.stock }}\",\n\"current_price\": {{ $json.current_price }},\n\"eps_current\": {{ $json.eps_current }},\n\"bvps_current\": {{ $json.bvps_current }},\n\"revenue_last_4q\": {{ $json.revenue_last_4q }},\n\"net_income_last_4q\": {{ $json.net_income_last_4q }},\n\"gross_margin_last_4q\": {{ $json.gross_margin_last_4q }},\n\"total_debt_last_4q\": {{ $json.total_debt_last_4q }},\n\"net_debt_latest\": {{ $json.net_debt_latest }},\n\"net_cash_flag\": {{ $json.net_cash_flag }},\n\"operating_margin_ttm\": {{ $json.operating_margin_ttm }},\n\"fcf_ttm\": {{ $json.fcf_ttm }},\n\"revenue_growth_yoy\": {{ $json.revenue_growth_yoy }},\n\"f_score\": {{ $json.f_score }},\n\"f_score_data_ok\": {{ $json.f_score_data_ok }},\n\"sector\":  \"{{ $json.sector }}\",\n\"graham_number\": {{ $json.graham_number }},\n\"dcf_anchor\": {{ $json.dcf_anchor }},\n\"sector_median_pe\": {{ $json.sector_median_pe }}\n}\n\nRules:\n\nUse ONLY these values.\n\nIf any field is null → treat as missing.\n\nNever assume missing data.\n\nNever assume share splits, ADR ratios, or alternative listings.\n\nIMPORTANT — NEWS SOURCE PRIORITY:\n\nSeeking Alpha articles (highest credibility signal)\n\nOther news_items\n\nMarket sentiment\n\nPrefer Seeking Alpha for thesis framing, but verify with financial data.\nIf conflict exists, flag uncertainty.\n\nSEEKING ALPHA ARTICLES (Recent 96h window):\n{{ $json.seekingAlphaNewsText }}\n\nANALYSIS TASKS\n\nFundamental Trends\n\nEvaluate revenue_last_4q and net_income_last_4q.\n\nEvaluate debt using total_debt_last_4q and net_debt_latest.\n\nEvaluate margin stability using gross_margin_last_4q and operating_margin_ttm.\n\nIf fcf_ttm is null → state \"FCF unavailable due to missing data.\"\n\nBusiness & Moat\nClassify moat: Brand, Network, Cost, Switching Costs, or IP.\n\nPricing Power\nJudge using margin stability and operating_margin_ttm.\n\nVALUATION FRAMEWORK (CRITICAL SECTION)\n\nA) IMPLIED P/E SANITY CHECK (MANDATORY)\n\nIf eps_current > 0 AND current_price > 0:\n\nimplied_pe = current_price / eps_current\nElse:\n\nimplied_pe = null\n\nEPS VALIDITY RULE:\nIf implied_pe is not null AND implied_pe > 60:\n\nTreat EPS as NOT economically meaningful (growth/transition phase).\n\nEPS multiple valuation is FORBIDDEN.\n\nCompany must be classified as growth_transition.\n\nB) PHASE CLASSIFICATION (MANDATORY — must pick one)\n\n\"mature_profitable\": eps_current > 0 AND (implied_pe is null OR implied_pe ≤ 60)\n\n\"growth_transition\": eps_current ≤ 0 OR implied_pe > 60\n\nMATURE OVERRIDE RULE:\nIf initial classification = growth_transition due to implied_pe > 60 only\n(i.e., eps_current IS > 0):\n  AND revenue_growth_yoy < 15%\n  AND operating_margin_ttm > 10%\n  → Override to mature_profitable\n  → Add note: \"Phase overridden to mature_profitable: \n    low growth + positive margins suggest temporary P/E spike, \n    not structural transition.\"\n\nC) Graham Number (secondary only)\n\nsqrt(22.5 × eps_current × bvps_current)\n\nUse ONLY if ALL of these are true:\neps_current > 0\nbvps_current > 0\nphase = mature_profitable\nbvps_current >= 5.00\nAND (current_price / bvps_current) <= 15\nAND sector ≠ \"Financials\" \n  \nIf bvps_current < 5.00 OR (current_price / bvps_current) > 15:\n  Graham = null → skip Graham entirely → pt_base = pt_eps only\n  Add to rationale: \"Graham skipped: BVPS distorted by buybacks.\"\n\nDo NOT use Graham for growth_transition stocks.\n\nD) BASE TARGET CONSTRUCTION (pt_base)\n\nYOUR ROLE IS BEAR — select the LOWEST justifiable value at every step.\n\nPrimary valuation logic depends on phase.\n\nIF phase = mature_profitable:\nIf EPS positive:\npt_base = eps_current × multiple.\n\nSelect multiple from these RANGES — as bear, pick the BOTTOM of each range:\n  revenue_growth_yoy > 20%  → use between 18× and 25× (bear: pick 18×)\n  revenue_growth_yoy 5–20%  → use between 12× and 18× (bear: pick 12×)\n  revenue_growth_yoy < 5%   → use between 8×  and 12× (bear: pick 8×)\n\nSECTOR MULTIPLE FLOORS (apply AFTER selecting multiple):\nIf sector = \"Consumer Staples\" AND selected multiple < 14: raise to 14×\nIf sector = \"Technology\" AND selected multiple < 14: raise to 14×\nIf sector = \"Utilities\" AND selected multiple > 12: cap at 12×\nIf sector = \"Financials\": skip EPS multiple, use pt_base = bvps_current × 0.9\nIf sector = null or missing: no adjustment.\n\nMARGIN OVERRIDE (apply after sector floors):\nIf operating_margin_ttm > 25%\n  AND selected multiple < 14\n  AND sector ≠ \"Utilities\":\n  override multiple to 14×\n  Add note: \"Bear margin floor applied.\"\n\nIf Graham usable:\npt_base = average(pt_base, Graham)\n\nIF phase = growth_transition:\nGrowth-based anchor — as bear, pick the LOWER multiplier:\n\nLet g = revenue_growth_yoy\n\nIf g is null  → pt_base = current_price × 0.9\nIf g > 60     → pt_base = current_price × 1.5\nIf g > 30     → pt_base = current_price × 1.2\nIf g > 15     → pt_base = current_price × 1.0\nOtherwise     → pt_base = current_price × 0.8\n\nE) FINAL SAFETY RULES\n\npt_base must never be zero.\n\nIf pt_base cannot be determined → pt_base = current_price.\n\npt_base must not exceed current_price × 5 unless revenue_growth_yoy > 60.\n\nRound pt_base to 2 decimals.\n\nNEWS & THESIS IMPACT ANALYSIS (REQUIRED)\n\nAnalyze Seeking Alpha + news_items.\nExtract:\n\nregulatory risks\n\nlegal risks\n\nearnings/guidance changes\n\ncompetitive pressure\n\nthesis change signals\n\nDetermine if thesis strengthened or weakened.\n\nANTI-THESIS (RISK ADJUSTMENT)\n\nIdentify TWO biggest risks using priority:\n\nSeeking Alpha risks\n\nFinancial deterioration\n\nGeneral news\n\nF-Score Handling:\nIf f_score_data_ok is false → reduce confidence by 10.\nIf f_score ≤ 3 → increase risk discount by +10%.\nIf f_score ≥ 7 → increase confidence by +5 (max 90).\n\nBEAR / BULL BANDS\n\nDiscount selection:\n\n15% → strong fundamentals\n\n25% → moderate risk\n\n35% → weak fundamentals or uncertainty\n\nPremium selection:\n\n15% → slow growth\n\n25% → moderate growth\n\n40% → high growth (>20% revenue growth)\n\nNEWS SENTIMENT ADJUSTMENT (apply after selecting base discount/premium):\n\nAnalyze seekingAlphaNewsText and news context. Classify overall sentiment as:\n  POSITIVE: earnings beat, guidance raised, new contracts, analyst upgrades,\n            strong demand signals, competitive wins\n  NEGATIVE: earnings miss, guidance cut, regulatory action, legal risk,\n            competitive threat, leadership change, demand weakness\n  NEUTRAL:  mixed or no material news\n\nThen adjust:\n\nIf sentiment = POSITIVE:\n  reduce discount by 5% (floor at 10%)\n  increase premium by 5%\n  Add to flags: \"News sentiment: POSITIVE — bands adjusted favorably.\"\n\nIf sentiment = NEGATIVE:\n  increase discount by 5% (cap at 40%)\n  reduce premium by 5% (floor at 10%)\n  Add to flags: \"News sentiment: NEGATIVE — bands adjusted conservatively.\"\n\nIf sentiment = NEUTRAL or news missing:\n  no adjustment\n  Add to flags: \"News sentiment: NEUTRAL — no band adjustment.\"\n\nIf seekingAlphaNewsText is null or empty:\n  no adjustment\n  Add to flags: \"No news data — sentiment adjustment skipped.\"\n\nCompute:\npt_bear = pt_base × (1 − discount)\npt_bull = pt_base × (1 + premium)\n\nRound both to 2 decimals.\nPOST-COMPUTATION CHECK:\nIf pt_bull < current_price:\n  Set overvaluation_flag = true\n  Append to rationale: \"WARNING: All price targets below current market \n  price. Model indicates significant overvaluation vs. formula inputs. \n  Manual review recommended before acting on SELL signal.\"\n\nACTION VERDICT LOGIC\n\nBUY:\n\npt_base ≥ 20% upside\n\nand (f_score ≥ 5 OR fundamentals strong)\n\nSELL:\n\npt_base ≤ -15% downside\n\nor (f_score_data_ok true AND f_score ≤ 2)\n\nOtherwise HOLD.\n\nCONFIDENCE SCORING:\n\nStart at 60.\n\n+10 if revenue_growth_yoy > 20%.\n+10 if strong balance sheet (net_cash_flag true or low debt).\n+5 if f_score ≥ 7.\n\n-15 if declining margins or losses.\n-15 if high leverage.\n-20 if major regulatory/legal risk.\n-10 if F-score unavailable.\n-10 if eps_current < 0 (loss-making company, path to profitability unproven).\n+10 if news sentiment = POSITIVE (strong thesis confirmation from Seeking Alpha)\n-15 if news sentiment = NEGATIVE (material risk identified in news/articles)\nClamp between 20 and 90.\n\nOUTPUT FORMAT — STRICT JSON ONLY\n\nReturn ONLY raw JSON object. No markdown. No commentary.\n\n{\n\"stock\": \"{{ $json.stock }}\",\n\"date\": \"{{ $now.format('yyyy-MM-dd') }}\",\n\"pt_bear\": (number),\n\"pt_base\": (number),\n\"pt_bull\": (number),\n\"f_score\": {{ $json.chatgpt_result.f_score }},\n\"confidence\": (20-90),\n\"rationale\": \"A one-sentence summary starting with BUY/HOLD/SELL, followed by the moat, primary risk, and pricing power level.\"\n}"}]},"jsonOutput":true,"builtInTools":{}},"credentials":{"googlePalmApi":{"id":"credential-id","name":"Google Gemini(PaLM) Api account"}},"typeVersion":1.1},{"id":"d0cd397d-0cc1-4e11-9ece-367d4379bdc7","name":"Needs Tiebreaker?","type":"n8n-nodes-base.if","position":[3760,1120],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"ed7c777e-46db-44d0-b09d-468b58fb421f","operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $json.needs_tiebreaker }}","rightValue":true}]}},"typeVersion":2.3},{"id":"694834f4-2c5b-4886-8ee5-f5ac0a668f89","name":"Clean Up Results from First Round","type":"n8n-nodes-base.code","position":[3600,1120],"parameters":{"jsCode":"const items_in = $input.all();\nlet chatgpt = null;\nlet gemini  = null;\n\nfor (const it of items_in) {\n  const j = it.json || {};\n  if (j.chatgpt_result) chatgpt = j.chatgpt_result;\n  if (j.gemini_result)  gemini  = j.gemini_result;\n}\n\nif (!chatgpt || !gemini) {\n  for (const it of items_in) {\n    const j = it.json || {};\n    if (!chatgpt && j.model === \"CHATGPT\") chatgpt = j;\n    if (!gemini  && j.model === \"GEMINI\")  gemini  = j;\n  }\n}\n\n// ── Pull financials from original source ──────────────────────────────\nconst source = $('Merge').first().json;\n\nconst stock         = chatgpt?.stock || gemini?.stock || source?.stock || \"UNKNOWN\";\nconst current_price = Number(source?.current_price || 0);\n\nfunction getVerdict(result) {\n  const r = (result?.rationale || \"\").trim().toUpperCase();\n  if (r.startsWith(\"BUY\"))  return \"BUY\";\n  if (r.startsWith(\"SELL\")) return \"SELL\";\n  return \"HOLD\";\n}\n\nconst v1 = getVerdict(chatgpt);\nconst v2 = getVerdict(gemini);\n\nconst pt1 = Number(chatgpt?.pt_base || 0);\nconst pt2 = Number(gemini?.pt_base  || 0);\n\nconst gap_pct = current_price > 0\n  ? parseFloat((Math.abs(pt1 - pt2) / current_price * 100).toFixed(2))\n  : 0;\n\nconst verdicts_agree   = (v1 === v2);\nconst high_uncertainty = gap_pct > 25;\nconst needs_tiebreaker = !verdicts_agree || high_uncertainty;\n\nreturn [{\n  json: {\n    ...source,             // ← all original financial fields at top level\n    stock,\n    current_price,\n    chatgpt_result:  chatgpt,\n    gemini_result:   gemini,\n    verdict_chatgpt: v1,\n    verdict_gemini:  v2,\n    gap_pct,\n    verdicts_agree,\n    high_uncertainty,\n    needs_tiebreaker\n  }\n}];"},"typeVersion":2},{"id":"fcc757ab-ba75-4c36-b16d-32e7375b8b3a","name":"Clean up Chatgpt 2","type":"n8n-nodes-base.code","position":[4240,896],"parameters":{"mode":"runOnceForEachItem","jsCode":"let parsed = $json;\nconst t = $json?.output?.[0]?.content?.[0]?.text;\nif (t) {\n  if (typeof t === \"string\") {\n    try { parsed = JSON.parse(t); } catch(e) { parsed = $json; }\n  } else if (typeof t === \"object\") {\n    parsed = t;\n  }\n}\n\nconst stock = parsed?.stock || $json?.stock || \"UNKNOWN\";\n\nconst meta = {\n  current_price:    $json?.current_price    ?? null,\n  needs_tiebreaker: $json?.needs_tiebreaker ?? true,\n  verdict_chatgpt:  $json?.verdict_chatgpt  ?? $('Clean Up Results from First Round').first().json?.verdict_chatgpt ?? null,\n  verdict_gemini:   $json?.verdict_gemini   ?? $('Clean Up Results from First Round').first().json?.verdict_gemini  ?? null,\n  gap_pct:          $json?.gap_pct          ?? $('Clean Up Results from First Round').first().json?.gap_pct         ?? 0,\n};\n\nreturn {\n  json: {\n    chatgpt_result: { ...parsed, model: \"CHATGPT_BULL\" },\n    stock,\n    ...meta\n  }\n};"},"typeVersion":2},{"id":"b2e8fb36-c569-4444-8a54-7a2032b469d3","name":"Clean up Gemini 2","type":"n8n-nodes-base.code","position":[4240,1088],"parameters":{"mode":"runOnceForEachItem","jsCode":"let parsed = $json;\nif ($json.output && typeof $json.output === \"string\") {\n  try {\n    const cleaned = $json.output.replace(/```json\\n?|```/g, \"\").trim();\n    parsed = JSON.parse(cleaned);\n  } catch(e) { parsed = $json; }\n} else if ($json.content?.parts?.[0]?.text) {\n  try {\n    parsed = JSON.parse($json.content.parts[0].text);\n  } catch(e) { parsed = $json; }\n}\nif (parsed && parsed.gemini_result) parsed = parsed.gemini_result;\n\nconst stock = parsed?.stock || $json?.stock || \"UNKNOWN\";\n\nconst meta = {\n  current_price:    $json?.current_price    ?? null,\n  needs_tiebreaker: $json?.needs_tiebreaker ?? true,\n  verdict_chatgpt:  $json?.verdict_chatgpt  ?? $('Clean Up Results from First Round').first().json?.verdict_chatgpt ?? null,\n  verdict_gemini:   $json?.verdict_gemini   ?? $('Clean Up Results from First Round').first().json?.verdict_gemini  ?? null,\n  gap_pct:          $json?.gap_pct          ?? $('Clean Up Results from First Round').first().json?.gap_pct         ?? 0,\n};\n\nreturn {\n  json: {\n    gemini_result: { ...parsed, model: \"GEMINI_BEAR\" },\n    stock,\n    ...meta\n  }\n};"},"typeVersion":2},{"id":"f0402aa7-d6ed-44b8-9bde-0f8013211f01","name":"First round Gemini","type":"@n8n/n8n-nodes-langchain.googleGemini","position":[2960,1296],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"models/gemini-2.5-pro","cachedResultName":"models/gemini-2.5-pro"},"options":{},"messages":{"values":[{"content":"=You are a Senior Equity Analyst with 50+ years of institutional market experience.\nYou receive structured financial data, news context, and a Quality F-Score and must produce disciplined valuation targets and a risk-adjusted verdict.\nYou must think like an institutional portfolio manager: conservative, risk-first, evidence-based.\n\nINPUT FIELDS AVAILABLE:\n\nSTRUCTURED DATA INPUT (REAL VALUES — USE THESE ONLY)\n{\n\"stock\": \"{{ $json.stock }}\",\n\"current_price\": {{ $json.current_price }},\n\"eps_current\": {{ $json.eps_current }},\n\"bvps_current\": {{ $json.bvps_current }},\n\"revenue_last_4q\": {{ $json.revenue_last_4q }},\n\"net_income_last_4q\": {{ $json.net_income_last_4q }},\n\"gross_margin_last_4q\": {{ $json.gross_margin_last_4q }},\n\"total_debt_last_4q\": {{ $json.total_debt_last_4q }},\n\"net_debt_latest\": {{ $json.net_debt_latest }},\n\"net_cash_flag\": {{ $json.net_cash_flag }},\n\"operating_margin_ttm\": {{ $json.operating_margin_ttm }},\n\"fcf_ttm\": {{ $json.fcf_ttm }},\n\"revenue_growth_yoy\": {{ $json.revenue_growth_yoy }},\n\"f_score\": {{ $json.f_score }},\n\"f_score_data_ok\": {{ $json.f_score_data_ok }},\n\"sector\":  \"{{ $json.sector }}\",\n\"graham_number\": {{ $json.graham_number }},\n\"dcf_anchor\": {{ $json.dcf_anchor }},\n\"sector_median_pe\": {{ $json.sector_median_pe }}\n}\n\nRules:\n\nUse ONLY these values.\n\nIf any field is null → treat as missing.\n\nNever assume missing data.\n\nNever assume share splits, ADR ratios, or alternative listings.\n\nIMPORTANT — NEWS SOURCE PRIORITY:\n\nSeeking Alpha articles (highest credibility signal)\n\nOther news_items\n\nMarket sentiment\n\nPrefer Seeking Alpha for thesis framing, but verify with financial data.\nIf conflict exists, flag uncertainty.\n\nSEEKING ALPHA ARTICLES (Recent 96h window):\n{{ $json.seekingAlphaNewsText }}\n\nANALYSIS TASKS\n\nFundamental Trends\n\nEvaluate revenue_last_4q and net_income_last_4q.\n\nEvaluate debt using total_debt_last_4q and net_debt_latest.\n\nEvaluate margin stability using gross_margin_last_4q and operating_margin_ttm.\n\nIf fcf_ttm is null → state \"FCF unavailable due to missing data.\"\n\nBusiness & Moat\nClassify moat: Brand, Network, Cost, Switching Costs, or IP.\n\nPricing Power\nJudge using margin stability and operating_margin_ttm.\n\nVALUATION FRAMEWORK (CRITICAL SECTION)\n\nA) IMPLIED P/E SANITY CHECK (MANDATORY)\n\nIf eps_current > 0 AND current_price > 0:\n\nimplied_pe = current_price / eps_current\nElse:\n\nimplied_pe = null\n\nEPS VALIDITY RULE:\nIf implied_pe is not null AND implied_pe > 60:\n\nTreat EPS as NOT economically meaningful (growth/transition phase).\n\nEPS multiple valuation is FORBIDDEN.\n\nCompany must be classified as growth_transition.\n\nB) PHASE CLASSIFICATION (MANDATORY — must pick one)\n\n\"mature_profitable\": eps_current > 0 AND (implied_pe is null OR implied_pe ≤ 60)\n\n\"growth_transition\": eps_current ≤ 0 OR implied_pe > 60\n\nMATURE OVERRIDE RULE:\nIf initial classification = growth_transition due to implied_pe > 60 only\n(i.e., eps_current IS > 0):\n  AND revenue_growth_yoy < 15%\n  AND operating_margin_ttm > 10%\n  → Override to mature_profitable\n  → Add note: \"Phase overridden to mature_profitable: \n    low growth + positive margins suggest temporary P/E spike, \n    not structural transition.\"\n\nC) Graham Number (secondary only)\n\nsqrt(22.5 × eps_current × bvps_current)\n\nUse ONLY if ALL of these are true:\neps_current > 0\nbvps_current > 0\nphase = mature_profitable\nbvps_current >= 5.00\nAND (current_price / bvps_current) <= 15\nAND sector ≠ \"Financials\" \n  \nIf bvps_current < 5.00 OR (current_price / bvps_current) > 15:\n  Graham = null → skip Graham entirely → pt_base = pt_eps only\n  Add to rationale: \"Graham skipped: BVPS distorted by buybacks.\"\n\nDo NOT use Graham for growth_transition stocks.\n\nD) BASE TARGET CONSTRUCTION (pt_base)\n\nPrimary valuation logic depends on phase.\n\nIF phase = mature_profitable:\nIf EPS positive:\npt_base = eps_current × multiple:\n\n25× if revenue_growth_yoy > 20%\n18× if revenue_growth_yoy 5–20%\n12× otherwise\n\nSECTOR MULTIPLE FLOORS (apply AFTER selecting the base multiple above):\n\nIf sector = \"Consumer Staples\" AND selected multiple < 18: raise to 18×\nIf sector = \"Technology\" AND selected multiple < 18: raise to 20×\nIf sector = \"Utilities\" AND selected multiple > 14: cap at 14×\nIf sector = \"Financials\": skip EPS multiple entirely,\n  use pt_base = bvps_current × 1.2 as anchor\nIf sector = null or missing: no adjustment.\n\nMARGIN OVERRIDE (apply after selecting base multiple):\nIf operating_margin_ttm > 25% AND selected multiple < 18:\n  override multiple to 18×\n  AND sector ≠ \"Utilities\":\n  Add note: \"Multiple floor applied: high-margin business.\"\n\nIf Graham usable:\npt_base = average(pt_base, Graham)\n\nIF phase = growth_transition:\nGrowth-based anchor only (EPS multiples forbidden):\n\nLet g = revenue_growth_yoy\n\nIf g is null → pt_base = current_price\n\nIf g > 60 → pt_base = current_price × 2.0\n\nIf g > 30 → pt_base = current_price × 1.6\n\nIf g > 15 → pt_base = current_price × 1.3\n\nOtherwise → pt_base = current_price\n\nE) FINAL SAFETY RULES\n\npt_base must never be zero.\n\nIf pt_base cannot be determined → pt_base = current_price.\n\npt_base must not exceed current_price × 5 unless revenue_growth_yoy > 60.\n\nRound pt_base to 2 decimals.\n\nNEWS & THESIS IMPACT ANALYSIS (REQUIRED)\n\nAnalyze Seeking Alpha + news_items.\nExtract:\n\nregulatory risks\n\nlegal risks\n\nearnings/guidance changes\n\ncompetitive pressure\n\nthesis change signals\n\nDetermine if thesis strengthened or weakened.\n\nANTI-THESIS (RISK ADJUSTMENT)\n\nIdentify TWO biggest risks using priority:\n\nSeeking Alpha risks\n\nFinancial deterioration\n\nGeneral news\n\nF-Score Handling:\nIf f_score_data_ok is false → reduce confidence by 10.\nIf f_score ≤ 3 → increase risk discount by +10%.\nIf f_score ≥ 7 → increase confidence by +5 (max 90).\n\nBEAR / BULL BANDS\n\nDiscount selection:\n\n15% → strong fundamentals\n\n25% → moderate risk\n\n35% → weak fundamentals or uncertainty\n\nPremium selection:\n\n15% → slow growth\n\n25% → moderate growth\n\n40% → high growth (>20% revenue growth)\n\nNEWS SENTIMENT ADJUSTMENT (apply after selecting base discount/premium):\n\nAnalyze seekingAlphaNewsText and news context. Classify overall sentiment as:\n  POSITIVE: earnings beat, guidance raised, new contracts, analyst upgrades,\n            strong demand signals, competitive wins\n  NEGATIVE: earnings miss, guidance cut, regulatory action, legal risk,\n            competitive threat, leadership change, demand weakness\n  NEUTRAL:  mixed or no material news\n\nThen adjust:\n\nIf sentiment = POSITIVE:\n  reduce discount by 5% (floor at 10%)\n  increase premium by 5%\n  Add to flags: \"News sentiment: POSITIVE — bands adjusted favorably.\"\n\nIf sentiment = NEGATIVE:\n  increase discount by 5% (cap at 40%)\n  reduce premium by 5% (floor at 10%)\n  Add to flags: \"News sentiment: NEGATIVE — bands adjusted conservatively.\"\n\nIf sentiment = NEUTRAL or news missing:\n  no adjustment\n  Add to flags: \"News sentiment: NEUTRAL — no band adjustment.\"\n\nIf seekingAlphaNewsText is null or empty:\n  no adjustment\n  Add to flags: \"No news data — sentiment adjustment skipped.\"\n\nCompute:\npt_bear = pt_base × (1 − discount)\npt_bull = pt_base × (1 + premium)\n\nRound both to 2 decimals.\nPOST-COMPUTATION CHECK:\nIf pt_bull < current_price:\n  Set overvaluation_flag = true\n  Append to rationale: \"WARNING: All price targets below current market \n  price. Model indicates significant overvaluation vs. formula inputs. \n  Manual review recommended before acting on SELL signal.\"\n\nACTION VERDICT LOGIC\n\nBUY:\n\npt_base ≥ 20% upside\n\nand (f_score ≥ 5 OR fundamentals strong)\n\nSELL:\n\npt_base ≤ -15% downside\n\nor (f_score_data_ok true AND f_score ≤ 2)\n\nOtherwise HOLD.\n\nCONFIDENCE SCORING:\n\nStart at 60.\n\n+10 if revenue_growth_yoy > 20%.\n+10 if strong balance sheet (net_cash_flag true or low debt).\n+5 if f_score ≥ 7.\n\n-15 if declining margins or losses.\n-15 if high leverage.\n-20 if major regulatory/legal risk.\n-10 if F-score unavailable.\n-10 if eps_current < 0 (loss-making company, path to profitability unproven).\n+10 if news sentiment = POSITIVE (strong thesis confirmation from Seeking Alpha)\n-15 if news sentiment = NEGATIVE (material risk identified in news/articles)\nClamp between 20 and 90.\n\nOUTPUT FORMAT — STRICT JSON ONLY\n\nReturn ONLY raw JSON object. No markdown. No commentary.\n\n{\n\"stock\": \"{{ $json.stock }}\",\n\"date\": \"{{ $now.format('yyyy-MM-dd') }}\",\n\"pt_bear\": (number),\n\"pt_base\": (number),\n\"pt_bull\": (number),\n\"f_score\": {{ $json.f_score }},\n\"confidence\": (20-90),\n\"rationale\": \"A structured summary with the following parts separated by | :\n  1. VERDICT: BUY/HOLD/SELL and one-line reason\n  2. MOAT: moat type and strength (strong/moderate/weak)\n  3. VALUATION: pt_base vs current_price, upside/downside %\n  4. PRIMARY RISK: single biggest threat to the thesis\n  5. PRICING POWER: high/moderate/low with one supporting data point\n  6. NEWS SIGNAL: POSITIVE/NEGATIVE/NEUTRAL and what drove it\"\n}"}]},"jsonOutput":true,"builtInTools":{}},"credentials":{"googlePalmApi":{"id":"credential-id","name":"Google Gemini(PaLM) Api account"}},"typeVersion":1.1},{"id":"69dc0fe8-d09f-446f-8841-894260e3728d","name":"First round ChatGPT","type":"@n8n/n8n-nodes-langchain.openAi","position":[2976,944],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"gpt-4o","cachedResultName":"GPT-4O"},"options":{"textFormat":{"textOptions":{"type":"json_object"}}},"responses":{"values":[{"content":"=You are a Senior Equity Analyst with 50+ years of institutional market experience.\nYou receive structured financial data, news context, and a Quality F-Score and must produce disciplined valuation targets and a risk-adjusted verdict.\nYou must think like an institutional portfolio manager: conservative, risk-first, evidence-based.\n\nINPUT FIELDS AVAILABLE:\n\nSTRUCTURED DATA INPUT (REAL VALUES — USE THESE ONLY)\n{\n\"stock\": \"{{ $json.stock }}\",\n\"current_price\": {{ $json.current_price }},\n\"eps_current\": {{ $json.eps_current }},\n\"bvps_current\": {{ $json.bvps_current }},\n\"revenue_last_4q\": {{ $json.revenue_last_4q }},\n\"net_income_last_4q\": {{ $json.net_income_last_4q }},\n\"gross_margin_last_4q\": {{ $json.gross_margin_last_4q }},\n\"total_debt_last_4q\": {{ $json.total_debt_last_4q }},\n\"net_debt_latest\": {{ $json.net_debt_latest }},\n\"net_cash_flag\": {{ $json.net_cash_flag }},\n\"operating_margin_ttm\": {{ $json.operating_margin_ttm }},\n\"fcf_ttm\": {{ $json.fcf_ttm }},\n\"revenue_growth_yoy\": {{ $json.revenue_growth_yoy }},\n\"f_score\": {{ $json.f_score }},\n\"f_score_data_ok\": {{ $json.f_score_data_ok }},\n\"sector\":  \"{{ $json.sector }}\",\n\"graham_number\": {{ $json.graham_number }},\n\"dcf_anchor\": {{ $json.dcf_anchor }},\n\"sector_median_pe\": {{ $json.sector_median_pe }}\n}\n\nRules:\n\nUse ONLY these values.\n\nIf any field is null → treat as missing.\n\nNever assume missing data.\n\nNever assume share splits, ADR ratios, or alternative listings.\n\nIMPORTANT — NEWS SOURCE PRIORITY:\n\nSeeking Alpha articles (highest credibility signal)\n\nOther news_items\n\nMarket sentiment\n\nPrefer Seeking Alpha for thesis framing, but verify with financial data.\nIf conflict exists, flag uncertainty.\n\nSEEKING ALPHA ARTICLES (Recent 96h window):\n{{ $json.seekingAlphaNewsText }}\n\nANALYSIS TASKS\n\nFundamental Trends\n\nEvaluate revenue_last_4q and net_income_last_4q.\n\nEvaluate debt using total_debt_last_4q and net_debt_latest.\n\nEvaluate margin stability using gross_margin_last_4q and operating_margin_ttm.\n\nIf fcf_ttm is null → state \"FCF unavailable due to missing data.\"\n\nBusiness & Moat\nClassify moat: Brand, Network, Cost, Switching Costs, or IP.\n\nPricing Power\nJudge using margin stability and operating_margin_ttm.\n\nVALUATION FRAMEWORK (CRITICAL SECTION)\n\nA) IMPLIED P/E SANITY CHECK (MANDATORY)\n\nIf eps_current > 0 AND current_price > 0:\n\nimplied_pe = current_price / eps_current\nElse:\n\nimplied_pe = null\n\nEPS VALIDITY RULE:\nIf implied_pe is not null AND implied_pe > 60:\n\nTreat EPS as NOT economically meaningful (growth/transition phase).\n\nEPS multiple valuation is FORBIDDEN.\n\nCompany must be classified as growth_transition.\n\nB) PHASE CLASSIFICATION (MANDATORY — must pick one)\n\n\"mature_profitable\": eps_current > 0 AND (implied_pe is null OR implied_pe ≤ 60)\n\n\"growth_transition\": eps_current ≤ 0 OR implied_pe > 60\n\nMATURE OVERRIDE RULE:\nIf initial classification = growth_transition due to implied_pe > 60 only\n(i.e., eps_current IS > 0):\n  AND revenue_growth_yoy < 15%\n  AND operating_margin_ttm > 10%\n  → Override to mature_profitable\n  → Add note: \"Phase overridden to mature_profitable: \n    low growth + positive margins suggest temporary P/E spike, \n    not structural transition.\"\n\nC) Graham Number (secondary only)\n\nsqrt(22.5 × eps_current × bvps_current)\n\nUse ONLY if ALL of these are true:\neps_current > 0\nbvps_current > 0\nphase = mature_profitable\nbvps_current >= 5.00\nAND (current_price / bvps_current) <= 15\nAND sector ≠ \"Financials\" \n  \nIf bvps_current < 5.00 OR (current_price / bvps_current) > 15:\n  Graham = null → skip Graham entirely → pt_base = pt_eps only\n  Add to rationale: \"Graham skipped: BVPS distorted by buybacks.\"\n\nDo NOT use Graham for growth_transition stocks.\n\nD) BASE TARGET CONSTRUCTION (pt_base)\n\nPrimary valuation logic depends on phase.\n\nIF phase = mature_profitable:\nIf EPS positive:\npt_base = eps_current × multiple:\n\n25× if revenue_growth_yoy > 20%\n18× if revenue_growth_yoy 5–20%\n12× otherwise\n\nSECTOR MULTIPLE FLOORS (apply AFTER selecting the base multiple above):\n\nIf sector = \"Consumer Staples\" AND selected multiple < 18: raise to 18×\nIf sector = \"Technology\" AND selected multiple < 18: raise to 20×\nIf sector = \"Utilities\" AND selected multiple > 14: cap at 14×\nIf sector = \"Financials\": skip EPS multiple entirely,\n  use pt_base = bvps_current × 1.2 as anchor\nIf sector = null or missing: no adjustment.\n\nMARGIN OVERRIDE (apply after selecting base multiple):\nIf operating_margin_ttm > 25% AND selected multiple < 18:\n  override multiple to 18×\n  AND sector ≠ \"Utilities\":\n  Add note: \"Multiple floor applied: high-margin business.\"\n\nIf Graham usable:\npt_base = average(pt_base, Graham)\n\nIF phase = growth_transition:\nGrowth-based anchor only (EPS multiples forbidden):\n\nLet g = revenue_growth_yoy\n\nIf g is null → pt_base = current_price\n\nIf g > 60 → pt_base = current_price × 2.0\n\nIf g > 30 → pt_base = current_price × 1.6\n\nIf g > 15 → pt_base = current_price × 1.3\n\nOtherwise → pt_base = current_price\n\nE) FINAL SAFETY RULES\n\npt_base must never be zero.\n\nIf pt_base cannot be determined → pt_base = current_price.\n\npt_base must not exceed current_price × 5 unless revenue_growth_yoy > 60.\n\nRound pt_base to 2 decimals.\n\nNEWS & THESIS IMPACT ANALYSIS (REQUIRED)\n\nAnalyze Seeking Alpha + news_items.\nExtract:\n\nregulatory risks\n\nlegal risks\n\nearnings/guidance changes\n\ncompetitive pressure\n\nthesis change signals\n\nDetermine if thesis strengthened or weakened.\n\nANTI-THESIS (RISK ADJUSTMENT)\n\nIdentify TWO biggest risks using priority:\n\nSeeking Alpha risks\n\nFinancial deterioration\n\nGeneral news\n\nF-Score Handling:\nIf f_score_data_ok is false → reduce confidence by 10.\nIf f_score ≤ 3 → increase risk discount by +10%.\nIf f_score ≥ 7 → increase confidence by +5 (max 90).\n\nBEAR / BULL BANDS\n\nDiscount selection:\n\n15% → strong fundamentals\n\n25% → moderate risk\n\n35% → weak fundamentals or uncertainty\n\nPremium selection:\n\n15% → slow growth\n\n25% → moderate growth\n\n40% → high growth (>20% revenue growth)\n\nNEWS SENTIMENT ADJUSTMENT (apply after selecting base discount/premium):\n\nAnalyze seekingAlphaNewsText and news context. Classify overall sentiment as:\n  POSITIVE: earnings beat, guidance raised, new contracts, analyst upgrades,\n            strong demand signals, competitive wins\n  NEGATIVE: earnings miss, guidance cut, regulatory action, legal risk,\n            competitive threat, leadership change, demand weakness\n  NEUTRAL:  mixed or no material news\n\nThen adjust:\n\nIf sentiment = POSITIVE:\n  reduce discount by 5% (floor at 10%)\n  increase premium by 5%\n  Add to flags: \"News sentiment: POSITIVE — bands adjusted favorably.\"\n\nIf sentiment = NEGATIVE:\n  increase discount by 5% (cap at 40%)\n  reduce premium by 5% (floor at 10%)\n  Add to flags: \"News sentiment: NEGATIVE — bands adjusted conservatively.\"\n\nIf sentiment = NEUTRAL or news missing:\n  no adjustment\n  Add to flags: \"News sentiment: NEUTRAL — no band adjustment.\"\n\nIf seekingAlphaNewsText is null or empty:\n  no adjustment\n  Add to flags: \"No news data — sentiment adjustment skipped.\"\n\nCompute:\npt_bear = pt_base × (1 − discount)\npt_bull = pt_base × (1 + premium)\n\nRound both to 2 decimals.\nPOST-COMPUTATION CHECK:\nIf pt_bull < current_price:\n  Set overvaluation_flag = true\n  Append to rationale: \"WARNING: All price targets below current market \n  price. Model indicates significant overvaluation vs. formula inputs. \n  Manual review recommended before acting on SELL signal.\"\n\nACTION VERDICT LOGIC\n\nBUY:\n\npt_base ≥ 20% upside\n\nand (f_score ≥ 5 OR fundamentals strong)\n\nSELL:\n\npt_base ≤ -15% downside\n\nor (f_score_data_ok true AND f_score ≤ 2)\n\nOtherwise HOLD.\n\nCONFIDENCE SCORING:\n\nStart at 60.\n\n+10 if revenue_growth_yoy > 20%.\n+10 if strong balance sheet (net_cash_flag true or low debt).\n+5 if f_score ≥ 7.\n\n-15 if declining margins or losses.\n-15 if high leverage.\n-20 if major regulatory/legal risk.\n-10 if F-score unavailable.\n-10 if eps_current < 0 (loss-making company, path to profitability unproven).\n+10 if news sentiment = POSITIVE (strong thesis confirmation from Seeking Alpha)\n-15 if news sentiment = NEGATIVE (material risk identified in news/articles)\nClamp between 20 and 90.\n\nOUTPUT FORMAT — STRICT JSON ONLY\n\nReturn ONLY raw JSON object. No markdown. No commentary.\n\n{\n\"stock\": \"{{ $json.stock }}\",\n\"date\": \"{{ $now.format('yyyy-MM-dd') }}\",\n\"pt_bear\": (number),\n\"pt_base\": (number),\n\"pt_bull\": (number),\n\"f_score\": {{ $json.f_score }},\n\"confidence\": (20-90),\n\"rationale\": \"A structured summary with the following parts separated by | :\n  1. VERDICT: BUY/HOLD/SELL and one-line reason\n  2. MOAT: moat type and strength (strong/moderate/weak)\n  3. VALUATION: pt_base vs current_price, upside/downside %\n  4. PRIMARY RISK: single biggest threat to the thesis\n  5. PRICING POWER: high/moderate/low with one supporting data point\n  6. NEWS SIGNAL: POSITIVE/NEGATIVE/NEUTRAL and what drove it\"\n}"}]},"builtInTools":{}},"credentials":{"openAiApi":{"id":"credential-id","name":"OpenAi account 2"}},"typeVersion":2.1},{"id":"b703c339-afaf-4677-8da3-4aaf7aae27a3","name":"Alert Filter","type":"n8n-nodes-base.code","position":[5552,1040],"parameters":{"jsCode":"const j = $input.first().json;\n\nfunction getVerdict(rationale) {\n  const r = (rationale || '').trim().toUpperCase();\n  if (r.startsWith('BUY'))  return 'BUY';\n  if (r.startsWith('SELL')) return 'SELL';\n  return 'HOLD';\n}\n\nconst verdict      = getVerdict(j.rationale);\nconst confidence   = Number(j.confidence    || 0);\nconst conviction   = j.conviction           || 'LOW';\nconst gap_pct      = Number(j.gap_pct       || 0);\nconst pt_base      = Number(j.pt_base       || 0);\nconst pt_bull      = Number(j.pt_bull       || 0);\nconst pt_bear      = Number(j.pt_bear       || 0);\nconst cur_price    = Number(j.current_price || 0);\nconst f_score      = j.f_score !== null && j.f_score !== undefined ? `F:${j.f_score}/9` : '';\n\nconst upside_pct   = cur_price > 0\n  ? ((pt_base - cur_price) / cur_price * 100).toFixed(1)\n  : null;\n\n// Alert when: clear BUY or SELL signal + minimum confidence threshold\nconst should_alert = (verdict === 'BUY' || verdict === 'SELL') && confidence >= 50;\n\nconst icon = verdict === 'BUY' ? '\\uD83D\\uDFE2' : '\\uD83D\\uDD34';\n\nconst upsideStr = upside_pct !== null\n  ? ` (${Number(upside_pct) >= 0 ? '+' : ''}${upside_pct}%)`\n  : '';\n\nconst alert_message = should_alert\n  ? `${icon} *${verdict}* \\u2014 ${j.stock} [${j.model}]\\n`\n  + `\\uD83D\\uDCC5 ${j.date}\\n`\n  + `\\uD83D\\uDCB0 Price: $${cur_price} \\u2192 Target: $${pt_base}${upsideStr}\\n`\n  + `\\uD83C\\uDFAF Bear: $${pt_bear} | Bull: $${pt_bull}\\n`\n  + `\\uD83D\\uDCCA Confidence: ${confidence}% | Conviction: ${conviction}${f_score ? ` | ${f_score}` : ''}\\n`\n  + (gap_pct ? `\\uD83D\\uDCD0 Model Gap: ${gap_pct}%\\n` : '')\n  + `\\uD83D\\uDCDD ${j.rationale}`\n  : null;\n\nreturn [{\n  json: {\n    ...j,\n    should_alert,\n    alert_verdict:  verdict,\n    alert_message\n  }\n}];"},"typeVersion":2},{"id":"79143262-612a-4140-b63d-28b09544d146","name":"If Alert?","type":"n8n-nodes-base.if","position":[5744,1040],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"af002-cond-should-alert","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{$json.should_alert}}","rightValue":""}]}},"typeVersion":2.3},{"id":"ca61fc9d-6d97-4ba2-9d23-d7359fc09d4a","name":"Telegram Alert","type":"n8n-nodes-base.telegram","position":[6976,800],"webhookId":"587de267-6878-4b18-9075-47c92fa3481d","parameters":{"text":"={{ $json.alert_message }}","chatId":"123456789","additionalFields":{"parse_mode":"Markdown"}},"credentials":{"telegramApi":{"id":"credential-id","name":"Telegram account"}},"typeVersion":1.2},{"id":"fdcddd4e-b9d8-4ad5-ae72-a1245ee4763b","name":"Get 50MA","type":"n8n-nodes-base.httpRequest","position":[5904,928],"parameters":{"url":"=https://www.alphavantage.co/query?function=SMA&symbol={{ $json.stock }}&interval=daily&time_period=50&series_type=close&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.2},{"id":"edd624c4-7a8b-4197-b9b9-0d99856e23b2","name":"MA Check","type":"n8n-nodes-base.code","position":[6224,928],"parameters":{"mode":"runOnceForEachItem","jsCode":"const smaResponse = $input.first().json;\n\n// Recover alert fields\nconst alertData = $('Alert Filter').first().json;\n\n// ─── 50MA ─────────────────────────────────────────────────────────────────\nconst sma50Data = smaResponse['Technical Analysis: SMA'];\nlet ma_50 = null;\nif (sma50Data) {\n  const latestDate50 = Object.keys(sma50Data)[0];\n  ma_50 = parseFloat(sma50Data[latestDate50]['SMA']);\n}\n\n// ─── 200MA (Idea 6) — fetched separately then passed here ────────────────\n// Get 200MA node result is available from previous Get 200MA step\nlet ma_200 = null;\ntry {\n  const sma200Node = $('Get 200MA').first().json;\n  const sma200Data = sma200Node['Technical Analysis: SMA'];\n  if (sma200Data) {\n    const latestDate200 = Object.keys(sma200Data)[0];\n    ma_200 = parseFloat(sma200Data[latestDate200]['SMA']);\n  }\n} catch(e) { /* 200MA unavailable */ }\n\nconst current_price = Number(alertData.current_price || 0);\n\n// 50MA check\nconst ma_50_passed = current_price > 0 && ma_50 !== null && ma_50 > 0 && current_price >= ma_50;\n\n// 200MA trend tier (Idea 6)\nlet trend_tier = 'UNKNOWN';\nif (ma_200 !== null && ma_50 !== null && current_price > 0) {\n  if (current_price > ma_200 && current_price > ma_50) {\n    trend_tier = 'STRONG_UPTREND';   // Price above both MAs — strongest entry\n  } else if (current_price > ma_200 && current_price <= ma_50) {\n    trend_tier = 'PULLBACK';         // Above 200MA but below 50MA — watchlist\n  } else {\n    trend_tier = 'DOWNTREND';        // Below 200MA — structural risk\n  }\n} else if (ma_50 !== null && current_price > 0) {\n  trend_tier = ma_50_passed ? 'ABOVE_50MA' : 'BELOW_50MA';\n}\n\nif (ma_50 === null) {\n  return [{ json: {\n    ...alertData,\n    ma_50: null, ma_200: null,\n    ma_check_passed: true,   // fail open\n    ma_signal: 'MA_DATA_UNAVAILABLE',\n    trend_tier: 'UNKNOWN'\n  }}];\n}\n\nreturn [{ json: {\n  ...alertData,\n  ma_50:           ma_50 !== null ? Number(ma_50.toFixed(2)) : null,\n  ma_200:          ma_200 !== null ? Number(ma_200.toFixed(2)) : null,\n  ma_check_passed: ma_50_passed,\n  ma_signal:       ma_50_passed ? 'ABOVE_50MA' : 'BELOW_50MA',\n  trend_tier\n}}];\n"},"typeVersion":2},{"id":"70b0daec-3b54-4a90-86c1-93e7835c5446","name":"Above 50MA?","type":"n8n-nodes-base.if","position":[6800,928],"parameters":{"options":{},"conditions":{"options":{"version":3,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"af007-cond-above-ma","operator":{"type":"boolean","operation":"true","singleValue":true},"leftValue":"={{$json.ma_check_passed}}","rightValue":""}]}},"typeVersion":2.3},{"id":"48e52eb5-96cb-4ad9-9aa0-4f463a36c22c","name":"Watchlist Telegram","type":"n8n-nodes-base.telegram","position":[6992,1072],"webhookId":"706e2d98-07f3-4135-973b-9e00f92b01c0","parameters":{"text":"=👀 *WATCHLIST* — {{ $json.stock }} [{{ $json.model }}]\n📅 {{ $json.date }}\n⚠️ Below 50-Day MA (${{ $json.ma_50 }}) — Wait for breakout\n💰 Price: ${{ $json.current_price }} → Target: ${{ $json.pt_base }}\n📊 Confidence: {{ $json.confidence }}%\n={{ $json.rsi_14 ? `RSI(14): ${$json.rsi_14} [${$json.rsi_signal}]` : '' }}\n={{ $json.earnings_warning ? '⚠️ ' + $json.earnings_warning : '' }}\n={{ $json.trend_tier ? `Trend: ${$json.trend_tier}` : '' }}","chatId":"123456789","additionalFields":{"parse_mode":"Markdown"}},"credentials":{"telegramApi":{"id":"credential-id","name":"Telegram account"}},"typeVersion":1.2},{"id":"8fe22d1d-f1be-4abc-acfd-23599a2c6e81","name":"Build Summary","type":"n8n-nodes-base.code","position":[-416,1008],"parameters":{"jsCode":"// Receives ALL accumulated done-branch items (one per processed stock)\nconst items = $input.all();\n\nif (!items || items.length === 0) {\n  return [{ json: { summary_message: 'Report is done — no stocks analyzed.' } }];\n}\n\nconst lines = items.map(item => {\n  const j = item.json;\n  const stock   = j.stock         || '?';\n  const verdict = j.alert_verdict || 'HOLD';\n  const conf    = j.confidence    ? ` (${j.confidence}%)` : '';\n   const price   = j.current_price ? ` $${Number(j.current_price).toFixed(2)}` : '';\n  const target  = j.pt_base       ? ` → $${Number(j.pt_base).toFixed(2)}`     : '';\n  const ma      = j.ma_signal && j.ma_signal !== 'MA_DATA_UNAVAILABLE'\n                    ? ` [${j.ma_signal}]` : '';\n  const icon = verdict === 'BUY'  ? '🟢'\n             : verdict === 'SELL' ? '🔴'\n             :                      '🟡';\n   return `${icon} ${stock}: ${verdict}${conf}${ma}${price}${target}`;\n});\n\nconst today = new Date().toISOString().split('T')[0];\nconst summary_message =\n  `📊 *Institutional Report — ${today}*\\n\\n`\n  + lines.join('\\n')\n  + `\\n\\n${items.length} stock${items.length !== 1 ? 's' : ''} analyzed.`;\n\nreturn [{ json: { summary_message } }];"},"typeVersion":2},{"id":"e7080103-92bf-422a-909f-44b01f4a9aa3","name":"Get Previous Verdict","type":"n8n-nodes-base.googleSheets","position":[5152,1040],"parameters":{"options":{"returnFirstMatch":false},"filtersUI":{"values":[{"lookupValue":"={{ $json.stock }}","lookupColumn":"stock"}]},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultName":"Sheet1"},"documentId":{"__rl":true,"mode":"list","value":"1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE","cachedResultUrl":"https://docs.google.com/spreadsheets/d/1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE/edit?usp=drivesdk","cachedResultName":"Sentiments of my stocks"}},"credentials":{"googleSheetsOAuth2Api":{"id":"credential-id","name":"Google Sheets account"}},"typeVersion":4.5,"alwaysOutputData":true},{"id":"e8f834df-8cfd-4da8-b43f-b67f0d4a4bbb","name":"Thesis Reversal Enricher","type":"n8n-nodes-base.code","position":[5360,1040],"parameters":{"jsCode":"// IDEA 2: Thesis Reversal Detection\n// $json here is the analysis item (from If TRUE branch through Get Previous Verdict)\n// We need to recover the analysis data and the prev verdict rows\n\n// The item flowing through is the analysis item (passed as the first input into the lookup)\n// After a Google Sheets lookup, the item might be the lookup result.\n// We need to get the original item from before the lookup.\n\nconst analysisData = $('If').first().json;\nconst stock = analysisData.stock || '';\n\n// Get all previous entries for this stock (from Get Previous Verdict lookup)\nconst prevRows = $input.all().filter(it => it.json.stock === stock && it.json.date);\n\n// Sort by date descending and get the most recent\nprevRows.sort((a, b) => (b.json.date || '').localeCompare(a.json.date || ''));\n\nconst prevEntry = prevRows.length > 0 ? prevRows[0].json : null;\n\nfunction extractVerdict(rationale) {\n  const r = (rationale || '').trim().toUpperCase();\n  if (r.startsWith('BUY'))  return 'BUY';\n  if (r.startsWith('SELL')) return 'SELL';\n  return 'HOLD';\n}\n\nconst prev_verdict = prevEntry ? extractVerdict(prevEntry.rationale) : null;\nconst prev_date    = prevEntry ? prevEntry.date : null;\nconst cur_rationale = analysisData.rationale || (analysisData.chatgpt_result || {}).rationale || '';\nconst cur_verdict   = extractVerdict(cur_rationale);\n\nconst thesis_reversal = prev_verdict !== null && prev_verdict !== cur_verdict;\n\nreturn [{\n  json: {\n    ...analysisData,\n    previous_verdict: prev_verdict,\n    previous_date:    prev_date,\n    thesis_reversal,\n    thesis_reversal_msg: thesis_reversal\n      ? `THESIS REVERSAL: ${stock}\\n${prev_verdict} -> ${cur_verdict} (prev: ${prev_date})\\nF:${analysisData.f_score ?? '?'}/9 | Confidence: ${analysisData.confidence ?? '?'}%\\n${cur_rationale.slice(0, 200)}`\n      : null\n  }\n}];\n"},"typeVersion":2},{"id":"cd0970b0-7f4f-4b70-af29-ee1ccd7e1b43","name":"Is Thesis Reversal?","type":"n8n-nodes-base.if","position":[7168,752],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"reversal-check","operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $json.thesis_reversal }}","rightValue":true}]}},"typeVersion":2.2},{"id":"e2fc109b-7a1e-48a2-80ae-ac72e4b93a5c","name":"Thesis Reversal Alert","type":"n8n-nodes-base.telegram","position":[7408,736],"webhookId":"5186267f-bdeb-4962-aacf-deafd6746e7e","parameters":{"text":"={{ $json.thesis_reversal_msg || '' }}","chatId":"={{ $('Send a text message').params.chatId || '' }}","additionalFields":{"parse_mode":"Markdown"}},"credentials":{"telegramApi":{"id":"credential-id","name":"Telegram account"}},"typeVersion":1.2},{"id":"7cc62935-8322-41cc-ae59-2acdea5aa3f8","name":"Get 200MA","type":"n8n-nodes-base.httpRequest","position":[6064,928],"parameters":{"url":"=https://www.alphavantage.co/query?function=SMA&symbol={{ $json.stock }}&interval=daily&time_period=200&series_type=close&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.2},{"id":"ca885a5a-1cff-48de-8b9f-077d0acb9812","name":"Get RSI","type":"n8n-nodes-base.httpRequest","position":[6352,928],"parameters":{"url":"=https://www.alphavantage.co/query?function=RSI&symbol={{ $json.stock }}&interval=daily&time_period=14&series_type=close&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.2},{"id":"407c2f10-f8fb-471c-9041-c92f752ac386","name":"Get Earnings","type":"n8n-nodes-base.httpRequest","position":[6496,928],"parameters":{"url":"=https://www.alphavantage.co/query?function=EARNINGS&symbol={{ $json.stock }}&apikey=NV2BZ902M2RIVKPC","options":{"response":{"response":{"responseFormat":"json"}}}},"typeVersion":4.2},{"id":"71610cc0-1f5c-4d9d-b190-d24d6081aa46","name":"Entry Signal Enricher","type":"n8n-nodes-base.code","position":[6640,928],"parameters":{"mode":"runOnceForEachItem","jsCode":"// IDEA 3: RSI + Earnings Date enrichment\nconst maData   = $('MA Check').first().json;\nconst rsiResp  = $('Get RSI').first().json;\nconst earningsResp = $('Get Earnings').first().json;\n\n// ─── RSI ────────────────────────────────────────────────────────────────\nlet rsi_14 = null;\nlet rsi_signal = 'UNKNOWN';\ntry {\n  const rsiData = rsiResp['Technical Analysis: RSI'];\n  if (rsiData) {\n    const latestRSI = Object.keys(rsiData)[0];\n    rsi_14 = parseFloat(rsiData[latestRSI]['RSI']);\n    if (rsi_14 <= 35)      rsi_signal = 'OVERSOLD';      // dip opportunity\n    else if (rsi_14 <= 55) rsi_signal = 'NEUTRAL';\n    else if (rsi_14 <= 70) rsi_signal = 'ELEVATED';\n    else                   rsi_signal = 'OVERBOUGHT';     // caution on entry\n  }\n} catch(e) { /* RSI unavailable */ }\n\n// ─── Earnings Date ──────────────────────────────────────────────────────\nlet next_earnings_date = null;\nlet days_to_earnings = null;\nlet earnings_warning = null;\ntry {\n  const annualEarnings = earningsResp.annualEarnings || [];\n  const quarterlyEarnings = earningsResp.quarterlyEarnings || [];\n  // Find the next scheduled earnings (reportedDate in future)\n  const now = Date.now();\n  const upcoming = quarterlyEarnings\n    .filter(e => e.reportedDate)\n    .map(e => ({ date: e.reportedDate, ms: new Date(e.reportedDate).getTime() }))\n    .filter(e => Number.isFinite(e.ms) && e.ms > now)\n    .sort((a, b) => a.ms - b.ms);\n  if (upcoming.length > 0) {\n    next_earnings_date = upcoming[0].date;\n    days_to_earnings = Math.ceil((upcoming[0].ms - now) / (1000 * 60 * 60 * 24));\n    if (days_to_earnings <= 14) {\n      earnings_warning = `EARNINGS IN ${days_to_earnings} DAYS (${next_earnings_date}) — binary risk`;\n    }\n  }\n} catch(e) { /* earnings unavailable */ }\n\nreturn [{\n  json: {\n    ...maData,\n    rsi_14:             rsi_14 !== null ? Number(rsi_14.toFixed(1)) : null,\n    rsi_signal,\n    next_earnings_date,\n    days_to_earnings,\n    earnings_warning\n  }\n}];\n"},"typeVersion":2},{"id":"dece9466-9493-46bb-bc8e-fdb4378eb1bc","name":"Sticky Note — Accuracy Tracker","type":"n8n-nodes-base.stickyNote","position":[-512,1712],"parameters":{"width":600,"height":400,"content":"## IDEA 7: Prediction Accuracy Tracker\n\n**Create a separate scheduled workflow (monthly) that:**\n\n1. Read the Sentiments sheet for all predictions older than 30 days\n2. For each stock+date+model row:\n   - Fetch `alphavantage Current Price` for the stock\n   - Compute: `return_pct = (actual_price_30d - current_price) / current_price * 100`\n   - Determine `was_correct`: BUY verdict + return > 5% = correct\n3. Append to a new **Accuracy_Tracking** Google Sheet with columns:\n   `stock | predicted_on | verdict | pt_base | current_price_at_prediction | actual_price_30d | return_pct | was_correct | model | resolution_method`\n4. Send weekly Telegram summary: *Model accuracy last 90 days: ChatGPT 68%, Gemini 71%, Tiebreaker 74%*\n\n**This turns the system into a learning engine.**\nSheet ID for Sentiments: `1fbptcVE0mBjaIZJHkJzFBTdoVJVLgQOWmJXpCx40YyE`"},"typeVersion":1},{"id":"b8252532-a54e-439b-9e09-cce81d30a2c2","name":"Sticky Note7","type":"n8n-nodes-base.stickyNote","position":[5520,576],"parameters":{"color":2,"width":2064,"height":1564,"content":"# 4.Strong Buy Alerts\n"},"typeVersion":1},{"id":"ab3ec24d-13db-427c-ad28-9c2d33f62d35","name":"Wait6","type":"n8n-nodes-base.wait","position":[768,1232],"webhookId":"2c8aa522-02cf-44c6-9396-d0aefb8428b0","parameters":{"amount":20},"typeVersion":1.1}],"active":true,"pinData":{},"settings":{"binaryMode":"separate","availableInMCP":false,"executionOrder":"v1"},"versionId":"6d19e3fa-4904-4a78-a519-b3f88c6a331f","connections":{"If":{"main":[[{"node":"Get Previous Verdict","type":"main","index":0}],[{"node":"loop_over_tickers","type":"main","index":0}]]},"If1":{"main":[[{"node":"XML","type":"main","index":0}],[{"node":"return the news","type":"main","index":0}]]},"If2":{"main":[[{"node":"Update row in sheet","type":"main","index":0}],[{"node":"Insert Row","type":"main","index":0}]]},"XML":{"main":[[{"node":"Edit Fields2","type":"main","index":0}]]},"Wait":{"main":[[{"node":"loop_over_tickers","type":"main","index":0}]]},"Merge":{"main":[[{"node":"Clean Up Results from First Round","type":"main","index":0}]]},"Wait1":{"main":[[{"node":"alphavantage - Balance Sheet","type":"main","index":0}]]},"Wait2":{"main":[[{"node":"alphavantage - Profile","type":"main","index":0}]]},"Wait3":{"main":[[{"node":"alphavantage - Income Statement","type":"main","index":0}]]},"Wait4":{"main":[[{"node":"alphavantage - CashFlow","type":"main","index":0}]]},"Wait5":{"main":[[{"node":"alphavantage - Current Price","type":"main","index":0}]]},"Wait6":{"main":[[{"node":"alphavantage - Current Price Second","type":"main","index":0}]]},"Merge1":{"main":[[{"node":"First round ChatGPT","type":"main","index":0},{"node":"First round Gemini","type":"main","index":0}]]},"Merge2":{"main":[[{"node":"Merge4","type":"main","index":0}]]},"Merge3":{"main":[[{"node":"Merge2","type":"main","index":1}]]},"Merge4":{"main":[[{"node":"Clean Read Financial","type":"main","index":0}]]},"Merge5":{"main":[[{"node":"Merge4","type":"main","index":1}]]},"Merge6":{"main":[[{"node":"Code in JavaScript3","type":"main","index":0}]]},"Merge7":{"main":[[{"node":"If","type":"main","index":0}]]},"Get RSI":{"main":[[{"node":"Get Earnings","type":"main","index":0}]]},"Get 50MA":{"main":[[{"node":"Get 200MA","type":"main","index":0}]]},"MA Check":{"main":[[{"node":"Get RSI","type":"main","index":0}]]},"Get 200MA":{"main":[[{"node":"MA Check","type":"main","index":0}]]},"If Alert?":{"main":[[{"node":"Get 50MA","type":"main","index":0}],[{"node":"write_sentiment_to_sheets","type":"main","index":0}]]},"Insert Row":{"main":[[{"node":"Merge New Financial","type":"main","index":0}]]},"Above 50MA?":{"main":[[{"node":"Telegram Alert","type":"main","index":0}],[{"node":"Watchlist Telegram","type":"main","index":0}]]},"Alert Filter":{"main":[[{"node":"If Alert?","type":"main","index":0}]]},"Cache Lookup":{"main":[[{"node":"Check the cache","type":"main","index":0}]]},"Edit Fields2":{"main":[[{"node":"Final version of news","type":"main","index":0}]]},"Get Earnings":{"main":[[{"node":"Entry Signal Enricher","type":"main","index":0}]]},"Build Summary":{"main":[[{"node":"Send a text message","type":"main","index":0}]]},"Clean Profile":{"main":[[{"node":"Merge3","type":"main","index":0}]]},"Clean the news":{"main":[[{"node":"If1","type":"main","index":0}]]},"Telegram Alert":{"main":[[{"node":"Is Thesis Reversal?","type":"main","index":0}]]},"Check the cache":{"main":[[{"node":"Is Cache Valid?","type":"main","index":0}]]},"Clean Ccashflow":{"main":[[{"node":"Merge5","type":"main","index":0}]]},"Clean up Gemini":{"main":[[{"node":"Merge","type":"main","index":1}]]},"Is Cache Valid?":{"main":[[{"node":"Wait1","type":"main","index":0},{"node":"Wait2","type":"main","index":0},{"node":"Wait3","type":"main","index":0},{"node":"Wait4","type":"main","index":0},{"node":"Wait5","type":"main","index":0},{"node":"Wait6","type":"main","index":0}],[]]},"return the news":{"main":[[{"node":"Final version of news","type":"main","index":0}]]},"Clean Up ChatGPT":{"main":[[{"node":"Merge","type":"main","index":0}]]},"Schedule Trigger":{"main":[[{"node":"Read_tickers_from_Sheet","type":"main","index":0}]]},"Clean up Gemini 2":{"main":[[{"node":"Merge6","type":"main","index":1}]]},"Needs Tiebreaker?":{"main":[[{"node":"Tide Breaker - Bull","type":"main","index":0},{"node":"Tide Breaker - Bear","type":"main","index":0}],[{"node":"Code in JavaScript","type":"main","index":0}]]},"loop_over_tickers":{"main":[[{"node":"Build Summary","type":"main","index":0}],[{"node":"Cache Lookup","type":"main","index":0},{"node":"Seekingalpha Articles","type":"main","index":0}]]},"Clean up Chatgpt 2":{"main":[[{"node":"Merge6","type":"main","index":0}]]},"Code in JavaScript":{"main":[[{"node":"Merge7","type":"main","index":1}]]},"First round Gemini":{"main":[[{"node":"Clean up Gemini","type":"main","index":0}]]},"Watchlist Telegram":{"main":[[{"node":"write_sentiment_to_sheets","type":"main","index":0}]]},"Clean Current Price":{"main":[[{"node":"Merge5","type":"main","index":1}]]},"Clean Old Financial":{"main":[[{"node":"Merge New Financial","type":"main","index":0}]]},"Clean balance sheet":{"main":[[{"node":"Merge2","type":"main","index":0}]]},"Code in JavaScript3":{"main":[[{"node":"Merge7","type":"main","index":0}]]},"First round ChatGPT":{"main":[[{"node":"Clean Up ChatGPT","type":"main","index":0}]]},"Get row(s) in sheet":{"main":[[{"node":"Clean Old Financial","type":"main","index":0}]]},"Is Thesis Reversal?":{"main":[[{"node":"Thesis Reversal Alert","type":"main","index":0}],[{"node":"write_sentiment_to_sheets","type":"main","index":0}]]},"Merge New Financial":{"main":[[{"node":"Merge1","type":"main","index":0}]]},"Tide Breaker - Bear":{"main":[[{"node":"Clean up Gemini 2","type":"main","index":0}]]},"Tide Breaker - Bull":{"main":[[{"node":"Clean up Chatgpt 2","type":"main","index":0}]]},"Update row in sheet":{"main":[[{"node":"Merge New Financial","type":"main","index":0}]]},"Clean Current Price1":{"main":[[{"node":"Update row in sheet1","type":"main","index":0}]]},"Clean Read Financial":{"main":[[{"node":"If2","type":"main","index":0}]]},"Get Previous Verdict":{"main":[[{"node":"Thesis Reversal Enricher","type":"main","index":0}]]},"Update row in sheet1":{"main":[[{"node":"Get row(s) in sheet","type":"main","index":0}]]},"Entry Signal Enricher":{"main":[[{"node":"Above 50MA?","type":"main","index":0}]]},"Final version of news":{"main":[[{"node":"Merge1","type":"main","index":1}]]},"Seekingalpha Articles":{"main":[[{"node":"Clean the news","type":"main","index":0}]]},"Thesis Reversal Alert":{"main":[[{"node":"write_sentiment_to_sheets","type":"main","index":0}]]},"Clean Income statement":{"main":[[{"node":"Merge3","type":"main","index":1}]]},"alphavantage - Profile":{"main":[[{"node":"Clean Profile","type":"main","index":0}]]},"Read_tickers_from_Sheet":{"main":[[{"node":"loop_over_tickers","type":"main","index":0}]]},"alphavantage - CashFlow":{"main":[[{"node":"Clean Ccashflow","type":"main","index":0}]]},"Thesis Reversal Enricher":{"main":[[{"node":"Alert Filter","type":"main","index":0}]]},"write_sentiment_to_sheets":{"main":[[{"node":"Wait","type":"main","index":0}]]},"alphavantage - Balance Sheet":{"main":[[{"node":"Clean balance sheet","type":"main","index":0}]]},"alphavantage - Current Price":{"main":[[{"node":"Clean Current Price","type":"main","index":0}]]},"alphavantage - Income Statement":{"main":[[{"node":"Clean Income statement","type":"main","index":0}]]},"Clean Up Results from First Round":{"main":[[{"node":"Needs Tiebreaker?","type":"main","index":0}]]},"alphavantage - Current Price Second":{"main":[[{"node":"Clean Current Price1","type":"main","index":0}]]}}},"lastUpdatedBy":1,"workflowInfo":{"nodeCount":88,"nodeTypes":{"n8n-nodes-base.if":{"count":8},"n8n-nodes-base.set":{"count":1},"n8n-nodes-base.xml":{"count":1},"n8n-nodes-base.code":{"count":25},"n8n-nodes-base.wait":{"count":7},"n8n-nodes-base.merge":{"count":8},"n8n-nodes-base.telegram":{"count":4},"n8n-nodes-base.stickyNote":{"count":9},"n8n-nodes-base.httpRequest":{"count":11},"n8n-nodes-base.googleSheets":{"count":8},"n8n-nodes-base.splitInBatches":{"count":1},"n8n-nodes-base.scheduleTrigger":{"count":1},"@n8n/n8n-nodes-langchain.openAi":{"count":2},"@n8n/n8n-nodes-langchain.googleGemini":{"count":2}}},"status":"published","readyToDemo":null,"user":{"name":"Mo AlBarrak","username":"abomone","bio":"Automation specialist and equity investor focused on building structured, scalable decision systems. I design intelligent workflows that transform financial data, news, and risk metrics into disciplined valuation models and scenario-based price targets. My work combines institutional-style investment logic with advanced automation architecture to eliminate emotional bias and improve consistency in market decisions.","verified":false,"links":["https://www.linkedin.com/in/mohammed-albarrak-6aaba0a/?skipRedirect=true"],"avatar":"https://gravatar.com/avatar/1f0f247ef89fe9f14afa579a1ff0e131fa60697d6a1c18f8282ea0c41ed7066d?r=pg&d=retro&size=200"},"nodes":[{"id":18,"icon":"file:googleSheets.svg","name":"n8n-nodes-base.googleSheets","codex":{"data":{"alias":["CSV","Sheet","Spreadsheet","GS"],"resources":{"generic":[{"url":"https://n8n.io/blog/love-at-first-sight-ricardos-n8n-journey/","icon":"❤️","label":"Love at first sight: Ricardo’s n8n journey"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/creating-triggers-for-n8n-workflows-using-polling/","icon":"⏲","label":"Creating triggers for n8n workflows using polling"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/","icon":"📈","label":"Migrating Community Metrics to Orbit using n8n"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/your-business-doesnt-need-you-to-operate/","icon":" 🖥️","label":"Hey founders! Your business doesn't need you to operate"},{"url":"https://n8n.io/blog/how-honest-burgers-use-automation-to-save-100k-per-year/","icon":"🍔","label":"How Honest Burgers Use Automation to Save $100k per year"},{"url":"https://n8n.io/blog/how-a-digital-strategist-uses-n8n-for-online-marketing/","icon":"💻","label":"How a digital strategist uses n8n for online marketing"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"}]},"categories":["Data & Storage","Productivity"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"input\",\"output\"]","defaults":{"name":"Google Sheets"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGZpbGw9IiMyOEI0NDYiIGQ9Ik0zNS42OSAxIDUyIDE3LjIyNXYzOS4wODdhMy42NyAzLjY3IDAgMCAxLTEuMDg0IDIuNjFBMy43IDMuNyAwIDAgMSA0OC4yOTMgNjBIMTIuNzA3YTMuNyAzLjcgMCAwIDEtMi42MjMtMS4wNzhBMy42NyAzLjY3IDAgMCAxIDkgNTYuMzEyVjQuNjg4YTMuNjcgMy42NyAwIDAgMSAxLjA4NC0yLjYxQTMuNyAzLjcgMCAwIDEgMTIuNzA3IDF6Ii8+PHBhdGggZmlsbD0iIzZBQ0U3QyIgZD0iTTM1LjY5IDEgNTIgMTcuMjI1SDM5LjM5N2MtMi4wNTQgMC0zLjcwNy0xLjgyOS0zLjcwNy0zLjg3MnoiLz48cGF0aCBmaWxsPSIjMjE5QjM4IiBkPSJNMzkuMjExIDE3LjIyNSA1MiAyMi40OHYtNS4yNTV6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZD0iTTIwLjEyIDMxLjk3NWMwLS44MTcuNjYyLTEuNDc1IDEuNDgzLTEuNDc1aDE3Ljc5NGMuODIxIDAgMS40ODIuNjU4IDEuNDgyIDEuNDc1djE1LjQ4N2MwIC44MTgtLjY2MSAxLjQ3NS0xLjQ4MiAxLjQ3NUgyMS42MDNhMS40NzYgMS40NzYgMCAwIDEtMS40ODItMS40NzRWMzEuOTc0em0yLjIyNSAxLjQ3NWg2LjY3MnYyLjIxMmgtNi42NzJ6bTAgNS4xNjJoNi42NzJ2Mi4yMTNoLTYuNjcyem0wIDUuMTYzaDYuNjcydjIuMjEyaC02LjY3MnptOS42MzgtMTAuMzI1aDYuNjcydjIuMjEyaC02LjY3MnptMCA1LjE2Mmg2LjY3MnYyLjIxM2gtNi42NzJ6bTAgNS4xNjNoNi42NzJ2Mi4yMTJoLTYuNjcyeiIvPjxwYXRoIGZpbGw9IiMyOEI0NDYiIGQ9Ik0zNC42OSAwIDUxIDE2LjIyNXYzOS4wODdhMy42NyAzLjY3IDAgMCAxLTEuMDg0IDIuNjFBMy43IDMuNyAwIDAgMSA0Ny4yOTMgNTlIMTEuNzA3YTMuNyAzLjcgMCAwIDEtMi42MjMtMS4wNzhBMy42NyAzLjY3IDAgMCAxIDggNTUuMzEyVjMuNjg4YTMuNjcgMy42NyAwIDAgMSAxLjA4NC0yLjYxQTMuNyAzLjcgMCAwIDEgMTEuNzA3IDB6Ii8+PHBhdGggZmlsbD0iIzZBQ0U3QyIgZD0iTTM0LjY5IDAgNTEgMTYuMjI1SDM4LjM5N2MtMi4wNTQgMC0zLjcwNy0xLjgyOS0zLjcwNy0zLjg3MnoiLz48cGF0aCBmaWxsPSIjMjE5QjM4IiBkPSJNMzguMjExIDE2LjIyNSA1MSAyMS40OHYtNS4yNTV6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZD0iTTE5LjEyIDMwLjk3NWMwLS44MTcuNjYyLTEuNDc1IDEuNDgzLTEuNDc1aDE3Ljc5NGMuODIxIDAgMS40ODIuNjU4IDEuNDgyIDEuNDc1djE1LjQ4N2MwIC44MTgtLjY2MSAxLjQ3NS0xLjQ4MiAxLjQ3NUgyMC42MDNhMS40NzYgMS40NzYgMCAwIDEtMS40ODItMS40NzRWMzAuOTc0em0yLjIyNSAxLjQ3NWg2LjY3MnYyLjIxMmgtNi42NzJ6bTAgNS4xNjJoNi42NzJ2Mi4yMTNoLTYuNjcyem0wIDUuMTYzaDYuNjcydjIuMjEyaC02LjY3MnptOS42MzgtMTAuMzI1aDYuNjcydjIuMjEyaC02LjY3MnptMCA1LjE2Mmg2LjY3MnYyLjIxM2gtNi42NzJ6bTAgNS4xNjNoNi42NzJ2Mi4yMTJoLTYuNjcyeiIvPjwvZz48L3N2Zz4="},"displayName":"Google Sheets","typeVersion":5,"nodeCategories":[{"id":3,"name":"Data & Storage"},{"id":4,"name":"Productivity"}]},{"id":19,"icon":"file:httprequest.svg","name":"n8n-nodes-base.httpRequest","codex":{"data":{"alias":["API","Request","URL","Build","cURL"],"resources":{"generic":[{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/","icon":"📈","label":"Automatically pulling and visualizing data with n8n"},{"url":"https://n8n.io/blog/learn-how-to-automatically-cross-post-your-content-with-n8n/","icon":"✍️","label":"Learn how to automatically cross-post your content with n8n"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/","icon":"🛳","label":"Running n8n on ships: An interview with Maranics"},{"url":"https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/","icon":" 🪢","label":"What are APIs and how to use them with no code"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/world-poetry-day-workflow/","icon":"📜","label":"Celebrating World Poetry Day with a daily poem in Telegram"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automate-designs-with-bannerbear-and-n8n/","icon":"🎨","label":"Automate Designs with Bannerbear and n8n"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/","icon":"📱","label":"Building an expense tracking app in 10 minutes"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/how-to-use-the-http-request-node-the-swiss-army-knife-for-workflow-automation/","icon":"🧰","label":"How to use the HTTP Request Node - The Swiss Army Knife for Workflow Automation"},{"url":"https://n8n.io/blog/learn-how-to-use-webhooks-with-mattermost-slash-commands/","icon":"🦄","label":"Learn how to use webhooks with Mattermost slash commands"},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/","icon":"📈","label":"A low-code bitcoin ticker built with QuestDB and n8n.io"},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/automations-for-activists/","icon":"✨","label":"How Common Knowledge use workflow automation for activism"},{"url":"https://n8n.io/blog/creating-scheduled-text-affirmations-with-n8n/","icon":"🤟","label":"Creating scheduled text affirmations with n8n"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"output\"]","defaults":{"name":"HTTP Request","color":"#0004F5"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MCAyMEM0MCA4Ljk1MzE0IDMxLjA0NjkgMCAyMCAwQzguOTUzMTQgMCAwIDguOTUzMTQgMCAyMEMwIDMxLjA0NjkgOC45NTMxNCA0MCAyMCA0MEMzMS4wNDY5IDQwIDQwIDMxLjA0NjkgNDAgMjBaTTIwIDM2Ljk0NThDMTguODg1MiAzNi45NDU4IDE3LjEzNzggMzUuOTY3IDE1LjQ5OTggMzIuNjk4NUMxNC43OTY0IDMxLjI5MTggMTQuMTk2MSAyOS41NDMxIDEzLjc1MjYgMjcuNjg0N0gyNi4xODk4QzI1LjgwNDUgMjkuNTQwMyAyNS4yMDQ0IDMxLjI5MDEgMjQuNTAwMiAzMi42OTg1QzIyLjg2MjIgMzUuOTY3IDIxLjExNDggMzYuOTQ1OCAyMCAzNi45NDU4Wk0xMi45MDY0IDIwQzEyLjkwNjQgMjEuNjA5NyAxMy4wMDg3IDIzLjE2NCAxMy4yMDAzIDI0LjYzMDVIMjYuNzk5N0MyNi45OTEzIDIzLjE2NCAyNy4wOTM2IDIxLjYwOTcgMjcuMDkzNiAyMEMyNy4wOTM2IDE4LjM5MDMgMjYuOTkxMyAxNi44MzYgMjYuNzk5NyAxNS4zNjk1SDEzLjIwMDNDMTMuMDA4NyAxNi44MzYgMTIuOTA2NCAxOC4zOTAzIDEyLjkwNjQgMjBaTTIwIDMuMDU0MTlDMjEuMTE0OSAzLjA1NDE5IDIyLjg2MjIgNC4wMzA3OCAyNC41MDAxIDcuMzAwMzlDMjUuMjA2NiA4LjcxNDA4IDI1LjgwNzIgMTAuNDA2NyAyNi4xOTIgMTIuMzE1M0gxMy43NTAxQzE0LjE5MzMgMTAuNDA0NyAxNC43OTQyIDguNzEyNTQgMTUuNDk5OCA3LjMwMDY0QzE3LjEzNzcgNC4wMzA4MyAxOC44ODUxIDMuMDU0MTkgMjAgMy4wNTQxOVpNMzAuMTQ3OCAyMEMzMC4xNDc4IDE4LjQwOTkgMzAuMDU0MyAxNi44NjE3IDI5LjgyMjcgMTUuMzY5NUgzNi4zMDQyQzM2LjcyNTIgMTYuODQyIDM2Ljk0NTggMTguMzk2NCAzNi45NDU4IDIwQzM2Ljk0NTggMjEuNjAzNiAzNi43MjUyIDIzLjE1OCAzNi4zMDQyIDI0LjYzMDVIMjkuODIyN0MzMC4wNTQzIDIzLjEzODMgMzAuMTQ3OCAyMS41OTAxIDMwLjE0NzggMjBaTTI2LjI3NjcgNC4yNTUxMkMyNy42MzY1IDYuMzYwMTkgMjguNzExIDkuMTMyIDI5LjM3NzQgMTIuMzE1M0gzNS4xMDQ2QzMzLjI1MTEgOC42NjggMzAuMTA3IDUuNzgzNDYgMjYuMjc2NyA0LjI1NTEyWk0xMC42MjI2IDEyLjMxNTNINC44OTI5M0M2Ljc1MTQ3IDguNjY3ODQgOS44OTM1MSA1Ljc4MzQxIDEzLjcyMzIgNC4yNTUxM0MxMi4zNjM1IDYuMzYwMjEgMTEuMjg5IDkuMTMyMDEgMTAuNjIyNiAxMi4zMTUzWk0zLjA1NDE5IDIwQzMuMDU0MTkgMjEuNjAzIDMuMjc3NDMgMjMuMTU3NSAzLjY5NDg0IDI0LjYzMDVIMTAuMTIxN0M5Ljk0NjE5IDIzLjE0MiA5Ljg1MjIyIDIxLjU5NDMgOS44NTIyMiAyMEM5Ljg1MjIyIDE4LjQwNTcgOS45NDYxOSAxNi44NTggMTAuMTIxNyAxNS4zNjk1SDMuNjk0ODRDMy4yNzc0MyAxNi44NDI1IDMuMDU0MTkgMTguMzk3IDMuMDU0MTkgMjBaTTI2LjI3NjYgMzUuNzQyN0MyNy42MzY1IDMzLjYzOTMgMjguNzExIDMwLjg2OCAyOS4zNzc0IDI3LjY4NDdIMzUuMTA0NkMzMy4yNTEgMzEuMzMyMiAzMC4xMDY4IDM0LjIxNzkgMjYuMjc2NiAzNS43NDI3Wk0xMy43MjM0IDM1Ljc0MjdDOS44OTM2OSAzNC4yMTc5IDYuNzUxNTUgMzEuMzMyNCA0Ljg5MjkzIDI3LjY4NDdIMTAuNjIyNkMxMS4yODkgMzAuODY4IDEyLjM2MzUgMzMuNjM5MyAxMy43MjM0IDM1Ljc0MjdaIiBmaWxsPSIjM0E0MkU5Ii8+Cjwvc3ZnPgo="},"displayName":"HTTP Request","typeVersion":4,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","codex":{"data":{"alias":["Router","Filter","Condition","Logic","Boolean","Branch"],"details":"The IF node can be used to implement binary conditional logic in your workflow. You can set up one-to-many conditions to evaluate each item of data being inputted into the node. That data will either evaluate to TRUE or FALSE and route out of the node accordingly.\n\nThis node has multiple types of conditions: Bool, String, Number, and Date & Time.","resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/create-a-toxic-language-detector-for-telegram/","icon":"🤬","label":"Create a toxic language detector for Telegram in 4 step"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automation-for-maintainers-of-open-source-projects/","icon":"🏷️","label":"How to automatically manage contributions to open-source projects"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.if/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"If","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"displayName":"If","typeVersion":2,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":24,"icon":"file:merge.svg","name":"n8n-nodes-base.merge","codex":{"data":{"alias":["Join","Concatenate","Wait"],"resources":{"generic":[{"url":"https://n8n.io/blog/how-to-sync-data-between-two-systems/","icon":"🏬","label":"How to synchronize data between two systems (one-way vs. two-way sync"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/","icon":"📈","label":"Migrating Community Metrics to Orbit using n8n"},{"url":"https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/","icon":"👦","label":"Build your own virtual assistant with n8n: A step by step guide"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.merge/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Merge"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTc3XzUxOCkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgNDhDMCAyMS40OTAzIDIxLjQ5MDMgMCA0OCAwSDExMkMxMzguNTEgMCAxNjAgMjEuNDkwMyAxNjAgNDhWNTZIMTk2LjI1MkMyNDAuNDM1IDU2IDI3Ni4yNTIgOTEuODE3MiAyNzYuMjUyIDEzNlYxOTJDMjc2LjI1MiAyMTQuMDkxIDI5NC4xNjEgMjMyIDMxNi4yNTIgMjMySDM1MlYyMjRDMzUyIDE5Ny40OSAzNzMuNDkgMTc2IDQwMCAxNzZINDY0QzQ5MC41MSAxNzYgNTEyIDE5Ny40OSA1MTIgMjI0VjI4OEM1MTIgMzE0LjUxIDQ5MC41MSAzMzYgNDY0IDMzNkg0MDBDMzczLjQ5IDMzNiAzNTIgMzE0LjUxIDM1MiAyODhWMjgwSDMxNi4yNTJDMjk0LjE2MSAyODAgMjc2LjI1MiAyOTcuOTA5IDI3Ni4yNTIgMzIwVjM3NkMyNzYuMjUyIDQyMC4xODMgMjQwLjQzNSA0NTYgMTk2LjI1MiA0NTZIMTYwVjQ2NEMxNjAgNDkwLjUxIDEzOC41MSA1MTIgMTEyIDUxMkg0OEMyMS40OTAzIDUxMiAwIDQ5MC41MSAwIDQ2NFY0MDBDMCAzNzMuNDkgMjEuNDkwMyAzNTIgNDggMzUySDExMkMxMzguNTEgMzUyIDE2MCAzNzMuNDkgMTYwIDQwMFY0MDhIMTk2LjI1MkMyMTMuOTI1IDQwOCAyMjguMjUyIDM5My42NzMgMjI4LjI1MiAzNzZWMzIwQzIyOC4yNTIgMjk0Ljc4NCAyMzguODU5IDI3Mi4wNDQgMjU1Ljg1MyAyNTZDMjM4Ljg1OSAyMzkuOTU2IDIyOC4yNTIgMjE3LjIxNiAyMjguMjUyIDE5MlYxMzZDMjI4LjI1MiAxMTguMzI3IDIxMy45MjUgMTA0IDE5Ni4yNTIgMTA0SDE2MFYxMTJDMTYwIDEzOC41MSAxMzguNTEgMTYwIDExMiAxNjBINDhDMjEuNDkwMyAxNjAgMCAxMzguNTEgMCAxMTJWNDhaTTEwNCA0OEMxMDguNDE4IDQ4IDExMiA1MS41ODE3IDExMiA1NlYxMDRDMTEyIDEwOC40MTggMTA4LjQxOCAxMTIgMTA0IDExMkg1NkM1MS41ODE3IDExMiA0OCAxMDguNDE4IDQ4IDEwNFY1NkM0OCA1MS41ODE3IDUxLjU4MTcgNDggNTYgNDhIMTA0Wk00NTYgMjI0QzQ2MC40MTggMjI0IDQ2NCAyMjcuNTgyIDQ2NCAyMzJWMjgwQzQ2NCAyODQuNDE4IDQ2MC40MTggMjg4IDQ1NiAyODhINDA4QzQwMy41ODIgMjg4IDQwMCAyODQuNDE4IDQwMCAyODBWMjMyQzQwMCAyMjcuNTgyIDQwMy41ODIgMjI0IDQwOCAyMjRINDU2Wk0xMTIgNDA4QzExMiA0MDMuNTgyIDEwOC40MTggNDAwIDEwNCA0MDBINTZDNTEuNTgxNyA0MDAgNDggNDAzLjU4MiA0OCA0MDhWNDU2QzQ4IDQ2MC40MTggNTEuNTgxNyA0NjQgNTYgNDY0SDEwNEMxMDguNDE4IDQ2NCAxMTIgNDYwLjQxOCAxMTIgNDU2VjQwOFoiIGZpbGw9IiM1NEI4QzkiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTc3XzUxOCI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Merge","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":38,"icon":"fa:pen","name":"n8n-nodes-base.set","codex":{"data":{"alias":["Set","JS","JSON","Filter","Transform","Map"],"resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/","icon":"📈","label":"Automatically pulling and visualizing data with n8n"},{"url":"https://n8n.io/blog/database-monitoring-and-alerting-with-n8n/","icon":"📡","label":"Database Monitoring and Alerting with n8n"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/","icon":"📱","label":"Building an expense tracking app in 10 minutes"},{"url":"https://n8n.io/blog/the-ultimate-guide-to-automate-your-video-collaboration-with-whereby-mattermost-and-n8n/","icon":"📹","label":"The ultimate guide to automate your video collaboration with Whereby, Mattermost, and n8n"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/","icon":"🧰","label":"Learn to Build Powerful API Endpoints Using Webhooks"},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/","icon":"📈","label":"A low-code bitcoin ticker built with QuestDB and n8n.io"},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.set/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Data Transformation"]}}},"group":"[\"input\"]","defaults":{"name":"Edit Fields"},"iconData":{"icon":"pen","type":"icon"},"displayName":"Edit Fields (Set)","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":39,"icon":"fa:sync","name":"n8n-nodes-base.splitInBatches","codex":{"data":{"alias":["Loop","Concatenate","Batch","Split","Split In Batches"],"resources":{"generic":[{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.splitinbatches/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"organization\"]","defaults":{"name":"Loop Over Items","color":"#007755"},"iconData":{"icon":"sync","type":"icon"},"displayName":"Loop Over Items (Split in Batches)","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":48,"icon":"fa:file-code","name":"n8n-nodes-base.xml","codex":{"data":{"alias":["Parse"],"resources":{"generic":[{"url":"https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/","icon":" 🪢","label":"What are APIs and how to use them with no code"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.xml/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"XML","color":"#333377"},"iconData":{"icon":"file-code","type":"icon"},"displayName":"XML","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":49,"icon":"file:telegram.svg","name":"n8n-nodes-base.telegram","codex":{"data":{"alias":["human","form","wait","hitl","approval"],"resources":{"generic":[{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/create-a-toxic-language-detector-for-telegram/","icon":"🤬","label":"Create a toxic language detector for Telegram in 4 step"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/world-poetry-day-workflow/","icon":"📜","label":"Celebrating World Poetry Day with a daily poem in Telegram"},{"url":"https://n8n.io/blog/using-automation-to-boost-productivity-in-the-workplace/","icon":"💪","label":"Using Automation to Boost Productivity in the Workplace"},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/creating-scheduled-text-affirmations-with-n8n/","icon":"🤟","label":"Creating scheduled text affirmations with n8n"},{"url":"https://n8n.io/blog/creating-telegram-bots-with-n8n-a-no-code-platform/","icon":"💬","label":"Creating Telegram Bots with n8n, a No-Code Platform"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.telegram/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/telegram/"}]},"categories":["Communication","HITL"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"HITL":["Human in the Loop"]}}},"group":"[\"output\"]","defaults":{"name":"Telegram"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiB2aWV3Qm94PSIwIDAgNjYgNjYiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSI+PHBhdGggZmlsbD0iIzM3YWVlMiIgZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiLz48cGF0aCBmaWxsPSIjYzhkYWVhIiBkPSJtMjEuNjYxIDM0LjMzOCAzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIvPjxwYXRoIGZpbGw9IiNhOWM2ZDgiIGQ9Im0yNi42OTUgMzcuMDM0LS43MjkgNy43NDZzLS4zMDUgMi4zNzMgMi4wNjggMGw0LjY0NC00LjIwMyIvPjxwYXRoIGQ9Im0yMS43MyAzNC43MTItNy44MDktMi41NDVzLS45MzItLjM3OC0uNjMzLTEuMjM3Yy4wNjItLjE3Ny4xODYtLjMyOC41NTktLjU4OCAxLjczMS0xLjIwNiAzMi4wMjgtMTIuMDk2IDMyLjAyOC0xMi4wOTZzLjg1Ni0uMjg4IDEuMzYxLS4wOTdjLjIzMS4wODguMzc4LjE4Ny41MDMuNTQ4LjA0NS4xMzIuMDcxLjQxMS4wNjguNjg5LS4wMDMuMjAxLS4wMjcuMzg2LS4wNDUuNjc4LS4xODQgMi45NzgtNS43MDYgMjUuMTk4LTUuNzA2IDI1LjE5OHMtLjMzIDEuMy0xLjUxNCAxLjM0NWMtLjQzMi4wMTYtLjk1Ni0uMDcxLTEuNTgyLS42MS0yLjMyMy0xLjk5OC0xMC4zNTItNy4zOTQtMTIuMTI2LTguNThhLjM0LjM0IDAgMCAxLS4xNDYtLjIzOWMtLjAyNS0uMTI1LjEwOC0uMjguMTA4LS4yOHMxMy45OC0xMi40MjcgMTQuMzUyLTEzLjczMWMuMDI5LS4xMDEtLjA3OS0uMTUxLS4yMjYtLjEwNy0uOTI5LjM0Mi0xNy4wMjUgMTAuNTA2LTE4LjgwMSAxMS42MjktLjEwNC4wNjYtLjM5NS4wMjMtLjM5NS4wMjMiLz48L2c+PC9zeW1ib2w+PC9zdmc+"},"displayName":"Telegram","typeVersion":1,"nodeCategories":[{"id":6,"name":"Communication"},{"id":28,"name":"HITL"}]},{"id":514,"icon":"fa:pause-circle","name":"n8n-nodes-base.wait","codex":{"data":{"alias":["pause","sleep","delay","timeout"],"resources":{"generic":[{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Flow"]}}},"group":"[\"organization\"]","defaults":{"name":"Wait","color":"#804050"},"iconData":{"icon":"pause-circle","type":"icon"},"displayName":"Wait","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":565,"icon":"fa:sticky-note","name":"n8n-nodes-base.stickyNote","codex":{"data":{"alias":["Comments","Notes","Sticky"],"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"input\"]","defaults":{"name":"Sticky Note","color":"#FFD233"},"iconData":{"icon":"sticky-note","type":"icon"},"displayName":"Sticky Note","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":834,"icon":"file:code.svg","name":"n8n-nodes-base.code","codex":{"data":{"alias":["cpde","Javascript","JS","Python","Script","Custom Code","Function"],"details":"The Code node allows you to execute JavaScript in your workflow.","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Code"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Code","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":839,"icon":"fa:clock","name":"n8n-nodes-base.scheduleTrigger","codex":{"data":{"alias":["Time","Scheduler","Polling","Cron","Interval"],"resources":{"generic":[],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.scheduletrigger/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"trigger\",\"schedule\"]","defaults":{"name":"Schedule Trigger","color":"#31C49F"},"iconData":{"icon":"clock","type":"icon"},"displayName":"Schedule Trigger","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":1250,"icon":"file:openAi.svg","name":"@n8n/n8n-nodes-langchain.openAi","codex":{"data":{"alias":["LangChain","ChatGPT","Sora","DallE","whisper","audio","transcribe","tts","assistant"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.openai/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Agents","Miscellaneous","Root Nodes"]}}},"group":"[\"transform\"]","defaults":{"name":"OpenAI"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTM2Ljg2NzEgMTYuMzcxOEMzNy43NzQ2IDEzLjY0OCAzNy40NjIxIDEwLjY2NDIgMzYuMDEwOCA4LjE4NjYxQzMzLjgyODIgNC4zODY1MyAyOS40NDA3IDIuNDMxNDkgMjUuMTU1NiAzLjM1MTUxQzIzLjI0OTMgMS4yMDM5NiAyMC41MTA1IC0wLjAxNzMxNDggMTcuNjM5MiAwLjAwMDE4NTUzM0MxMy4yNTkxIC0wLjAwOTgxNDY4IDkuMzcyNzMgMi44MTAyNSA4LjAyNTIgNi45Nzc4M0M1LjIxMTM5IDcuNTU0MSAyLjc4MjU4IDkuMzE1MzggMS4zNjEzIDExLjgxMTdDLTAuODM3NDkzIDE1LjYwMTggLTAuMzM2MjMyIDIwLjM3OTQgMi42MDEzMyAyMy42Mjk0QzEuNjkzODEgMjYuMzUzMiAyLjAwNjMyIDI5LjMzNzEgMy40NTc2IDMxLjgxNDZDNS42NDAxNSAzNS42MTQ3IDEwLjAyNzcgMzcuNTY5NyAxNC4zMTI4IDM2LjY0OTdDMTYuMjE3OSAzOC43OTczIDE4Ljk1NzkgNDAuMDE4NSAyMS44MjkyIDM5Ljk5OThDMjYuMjExOCA0MC4wMTEgMzAuMDk5NCAzNy4xODg1IDMxLjQ0NjkgMzMuMDE3MUMzNC4yNjA4IDMyLjQ0MDkgMzYuNjg5NiAzMC42Nzk2IDM4LjExMDggMjguMTgzM0M0MC4zMDcxIDI0LjM5MzIgMzkuODA0NiAxOS42MTk0IDM2Ljg2ODMgMTYuMzY5M0wzNi44NjcxIDE2LjM3MThaTTIxLjgzMTcgMzcuMzg2QzIwLjA3OCAzNy4zODg1IDE4LjM3OTIgMzYuNzc0NyAxNy4wMzI5IDM1LjY1MDlDMTcuMDk0MSAzNS42MTg1IDE3LjIwMDQgMzUuNTU5NyAxNy4yNjkxIDM1LjUxNzJMMjUuMjM0MyAzMC45MTcxQzI1LjY0MTggMzAuNjg1OCAyNS44OTE4IDMwLjI1MjEgMjUuODg5MyAyOS43ODMzVjE4LjU1NDNMMjkuMjU1NiAyMC40OTgxQzI5LjI5MTkgMjAuNTE1NiAyOS4zMTU3IDIwLjU1MDYgMjkuMzIwNyAyMC41OTA2VjI5Ljg4OTZDMjkuMzE1NyAzNC4wMjQ3IDI1Ljk2NjggMzcuMzc3MiAyMS44MzE3IDM3LjM4NlpNNS43MjY0IDMwLjUwNzFDNC44NDc2MyAyOC45ODk2IDQuNTMxMzcgMjcuMjEwOCA0LjgzMjYzIDI1LjQ4NDVDNC44OTEzOCAyNS41MTk1IDQuOTk1MTMgMjUuNTgzMiA1LjA2ODg4IDI1LjYyNTdMMTMuMDM0MSAzMC4yMjU4QzEzLjQzNzggMzAuNDYyMSAxMy45Mzc4IDMwLjQ2MjEgMTQuMzQyOCAzMC4yMjU4TDI0LjA2NjggMjQuNjEwN1YyOC40OTgzQzI0LjA2OTMgMjguNTM4MyAyNC4wNTA1IDI4LjU3NyAyNC4wMTkzIDI4LjYwMkwxNS45Njc5IDMzLjI1MDlDMTIuMzgxNSAzNS4zMTU5IDcuODAxNDQgMzQuMDg4NCA1LjcyNzY1IDMwLjUwNzFINS43MjY0Wk0zLjYzMDEgMTMuMTIwNUM0LjUwNTEyIDExLjYwMDQgNS44ODY0IDEwLjQzNzkgNy41MzE0NCA5LjgzNDE1QzcuNTMxNDQgOS45MDI5IDcuNTI3NjkgMTAuMDI0MSA3LjUyNzY5IDEwLjEwOTJWMTkuMzEwNkM3LjUyNTE5IDE5Ljc3ODEgNy43NzUxOSAyMC4yMTE5IDguMTgxNDUgMjAuNDQzMUwxNy45MDU0IDI2LjA1N0wxNC41MzkxIDI4LjAwMDhDMTQuNTA1MyAyOC4wMjMzIDE0LjQ2MjggMjguMDI3IDE0LjQyNTMgMjguMDEwOEw2LjM3MjY2IDIzLjM1ODJDMi43OTM4MyAyMS4yODU2IDEuNTY2MzEgMTYuNzA2OCAzLjYyODg1IDEzLjEyMTdMMy42MzAxIDEzLjEyMDVaTTMxLjI4ODIgMTkuNTU2OUwyMS41NjQyIDEzLjk0MTdMMjQuOTMwNiAxMS45OTkyQzI0Ljk2NDMgMTEuOTc2NyAyNS4wMDY4IDExLjk3MjkgMjUuMDQ0MyAxMS45ODkyTDMzLjA5NyAxNi42MzhDMzYuNjgyMSAxOC43MDkzIDM3LjkxMDggMjMuMjk1NyAzNS44Mzk1IDI2Ljg4MDhDMzQuOTYzMyAyOC4zOTgzIDMzLjU4MzIgMjkuNTYwOCAzMS45Mzk1IDMwLjE2NThWMjAuNjg5NEMzMS45NDMyIDIwLjIyMTkgMzEuNjk0NSAxOS43ODk0IDMxLjI4OTQgMTkuNTU2OUgzMS4yODgyWk0zNC42MzgzIDE0LjUxNDJDMzQuNTc5NSAxNC40NzggMzQuNDc1OCAxNC40MTU1IDM0LjQwMiAxNC4zNzNMMjYuNDM2OCA5Ljc3Mjg5QzI2LjAzMzEgOS41MzY2NCAyNS41MzMxIDkuNTM2NjQgMjUuMTI4MSA5Ljc3Mjg5TDE1LjQwNDEgMTUuMzg4VjExLjUwMDRDMTUuNDAxNiAxMS40NjA0IDE1LjQyMDQgMTEuNDIxNyAxNS40NTE2IDExLjM5NjdMMjMuNTAzIDYuNzUxNThDMjcuMDg5NCA0LjY4Mjc5IDMxLjY3NDUgNS45MTQwNiAzMy43NDIgOS41MDE2NEMzNC42MTU4IDExLjAxNjcgMzQuOTMyIDEyLjc5MDUgMzQuNjM1OCAxNC41MTQySDM0LjYzODNaTTEzLjU3NDEgMjEuNDQzMUwxMC4yMDY1IDE5LjQ5OTRDMTAuMTcwMiAxOS40ODE5IDEwLjE0NjUgMTkuNDQ2OCAxMC4xNDE1IDE5LjQwNjhWMTAuMTA3OUMxMC4xNDQgNS45Njc4MSAxMy41MDI4IDIuNjEyNzQgMTcuNjQyOSAyLjYxNTI0QzE5LjM5NDIgMi42MTUyNCAyMS4wODkyIDMuMjMwMjUgMjIuNDM1NSA0LjM1MDI4QzIyLjM3NDMgNC4zODI3OCAyMi4yNjkzIDQuNDQxNTMgMjIuMTk5MiA0LjQ4NDAzTDE0LjIzNDEgOS4wODQxM0MxMy44MjY2IDkuMzE1MzggMTMuNTc2NiA5Ljc0Nzg5IDEzLjU3OTEgMTAuMjE2N0wxMy41NzQxIDIxLjQ0MDZWMjEuNDQzMVpNMTUuNDAyOSAxNy41MDA2TDE5LjczNDIgMTQuOTk5M0wyNC4wNjU1IDE3LjQ5OTNWMjIuNTAwN0wxOS43MzQyIDI1LjAwMDdMMTUuNDAyOSAyMi41MDA3VjE3LjUwMDZaIiBmaWxsPSJibGFjayIvPgo8L3N2Zz4K"},"displayName":"OpenAI","typeVersion":2,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]},{"id":1309,"icon":"file:gemini.svg","name":"@n8n/n8n-nodes-langchain.googleGemini","codex":{"data":{"alias":["LangChain","video","document","audio","transcribe","assistant"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.googlegemini/"}]},"categories":["AI","Langchain"],"subcategories":{"AI":["Agents","Miscellaneous","Root Nodes"]}}},"group":"[\"transform\"]","defaults":{"name":"Google Gemini"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iX2Zvb3RlclNwYXJrXzk4dWR0XzE1MSIgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiB2aWV3Qm94PSIwIDAgNjQgNjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTU3LjA2NjcgMjguNjEwM0M1Mi4xMzU5IDI2LjQ4NzggNDcuODIxNyAyMy41NzYgNDQuMTIyMyAxOS44Nzg0QzQwLjQyNDcgMTYuMTgwOCAzNy41MTI4IDExLjg2NDkgMzUuMzkwMiA2LjkzNDJDMzQuNTc1NCA1LjA0NDQ5IDMzLjkyMDYgMy4xMDIwNCAzMy40MTg2IDEuMTEwNDlDMzMuMjU0OSAwLjQ1OTM2OCAzMi42NzExIDAuMDAxMDM3NiAzMiAwLjAwMTAzNzZDMzEuMzI4OCAwLjAwMTAzNzYgMzAuNzQ1IDAuNDU5MzY4IDMwLjU4MTMgMS4xMTA0OUMzMC4wNzkzIDMuMTAyMDQgMjkuNDI0NiA1LjA0MjY3IDI4LjYwOTcgNi45MzQyQzI2LjQ4NzIgMTEuODY0OSAyMy41NzUzIDE2LjE4MDggMTkuODc3NyAxOS44Nzg0QzE2LjE4IDIzLjU3NiAxMS44NjQgMjYuNDg3OCA2LjkzMzI3IDI4LjYxMDNDNS4wNDM1MyAyOS40MjUxIDMuMTAxMDUgMzAuMDc5OSAxLjEwOTQ3IDMwLjU4MTlDMC40NTgzMzggMzAuNzQ1NiAwIDMxLjMyOTQgMCAzMi4wMDA1QzAgMzIuNjcxNiAwLjQ1ODMzOCAzMy4yNTU1IDEuMTA5NDcgMzMuNDE5MUMzLjEwMTA1IDMzLjkyMTEgNS4wNDE3MiAzNC41NzU5IDYuOTMzMjcgMzUuMzkwN0MxMS44NjQgMzcuNTEzMiAxNi4xNzgyIDQwLjQyNTEgMTkuODc3NyA0NC4xMjI2QzIzLjU3NzEgNDcuODIwMiAyNi40ODcyIDUyLjEzNjEgMjguNjA5NyA1Ny4wNjY4QzI5LjQyNDYgNTguOTU2NSAzMC4wNzkzIDYwLjg5OSAzMC41ODEzIDYyLjg5MDVDMzAuNzQ1IDYzLjU0MTYgMzEuMzI4OCA2NCAzMiA2NEMzMi42NzExIDY0IDMzLjI1NDkgNjMuNTQxNiAzMy40MTg2IDYyLjg5MDVDMzMuOTIwNiA2MC44OTkgMzQuNTc1NCA1OC45NTgzIDM1LjM5MDIgNTcuMDY2OEMzNy41MTI4IDUyLjEzNjEgNDAuNDI0NyA0Ny44MjIgNDQuMTIyMyA0NC4xMjI2QzQ3LjgxOTkgNDAuNDI1MSA1Mi4xMzU5IDM3LjUxMzIgNTcuMDY2NyAzNS4zOTA3QzU4Ljk1NjQgMzQuNTc1OSA2MC44OTg5IDMzLjkyMTEgNjIuODkwNSAzMy40MTkxQzYzLjU0MTYgMzMuMjU1NSA2NCAzMi42NzE2IDY0IDMyLjAwMDVDNjQgMzEuMzI5NCA2My41NDE2IDMwLjc0NTYgNjIuODkwNSAzMC41ODE5QzYwLjg5ODkgMzAuMDc5OSA1OC45NTgyIDI5LjQyNTEgNTcuMDY2NyAyOC42MTAzWiIgZmlsbD0id2hpdGUiPjwvcGF0aD48bWFzayBpZD0ibWFzazBfMTA4NTlfNDg5NCIgc3R5bGU9Im1hc2stdHlwZTphbHBoYSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0Ij48cGF0aCBkPSJNMzIgMEMzMi42NzExIDEuMTQ0ZS0wNSAzMy4yNTUzIDAuNDU4MjYzIDMzLjQxODkgMS4xMDkzOEMzMy45MjA5IDMuMTAwOTMgMzQuNTc1OCA1LjA0Mzg5IDM1LjM5MDYgNi45MzM1OUMzNy41MTMxIDExLjg2MzkgNDAuNDI0NyAxNi4xNzk2IDQ0LjEyMjEgMTkuODc3QzQ3LjgyMTUgMjMuNTc0NSA1Mi4xMzU3IDI2LjQ4NjkgNTcuMDY2NCAyOC42MDk0QzU4Ljk1OCAyOS40MjQyIDYwLjg5OSAzMC4wNzkxIDYyLjg5MDYgMzAuNTgxMUM2My41NDE1IDMwLjc0NDggNjMuOTk5OCAzMS4zMjgxIDY0IDMxLjk5OUM2NCAzMi42NzAxIDYzLjU0MTcgMzMuMjU0MiA2Mi44OTA2IDMzLjQxOEM2MC44OTkgMzMuOTE5OSA1OC45NTYxIDM0LjU3NDggNTcuMDY2NCAzNS4zODk2QzUyLjEzNTggMzcuNTEyMSA0Ny44MTk2IDQwLjQyMzcgNDQuMTIyMSA0NC4xMjExQzQwLjQyNDYgNDcuODIwNCAzNy41MTMxIDUyLjEzNDkgMzUuMzkwNiA1Ny4wNjU0QzM0LjU3NTggNTguOTU3IDMzLjkyMDkgNjAuODk4MSAzMy40MTg5IDYyLjg4OTZDMzMuMjU1MiA2My41NDA3IDMyLjY3MTEgNjMuOTk5IDMyIDYzLjk5OUMzMS4zMjg5IDYzLjk5OSAzMC43NDQ4IDYzLjU0MDcgMzAuNTgxMSA2Mi44ODk2QzMwLjA3OTEgNjAuODk4MSAyOS40MjQyIDU4Ljk1NTEgMjguNjA5NCA1Ny4wNjU0QzI2LjQ4NjkgNTIuMTM0OSAyMy41NzczIDQ3LjgxODYgMTkuODc3OSA0NC4xMjExQzE2LjE3ODYgNDAuNDIzNyAxMS44NjQyIDM3LjUxMjEgNi45MzM1OSAzNS4zODk2QzUuMDQyMDQgMzQuNTc0OCAzLjEwMDk2IDMzLjkxOTkgMS4xMDkzOCAzMy40MThDMC40NTgzMDkgMzMuMjU0MiAwIDMyLjY3MDEgMCAzMS45OTlDMC4wMDAyMDE1NDggMzEuMzI4MSAwLjQ1ODQ2MyAzMC43NDQ4IDEuMTA5MzggMzAuNTgxMUMzLjEwMDk2IDMwLjA3OTEgNS4wNDM4NiAyOS40MjQyIDYuOTMzNTkgMjguNjA5NEMxMS44NjQzIDI2LjQ4NjkgMTYuMTgwNCAyMy41NzQ1IDE5Ljg3NzkgMTkuODc3QzIzLjU3NTMgMTYuMTc5NiAyNi40ODY5IDExLjg2MzkgMjguNjA5NCA2LjkzMzU5QzI5LjQyNDIgNS4wNDIwNyAzMC4wNzkxIDMuMTAwOTMgMzAuNTgxMSAxLjEwOTM4QzMwLjc0NDggMC40NTgyNiAzMS4zMjg5IDAgMzIgMFoiIGZpbGw9ImJsYWNrIj48L3BhdGg+PHBhdGggZD0iTTMyIDBDMzIuNjcxMSAxLjE0NGUtMDUgMzMuMjU1MyAwLjQ1ODI2MyAzMy40MTg5IDEuMTA5MzhDMzMuOTIwOSAzLjEwMDkzIDM0LjU3NTggNS4wNDM4OSAzNS4zOTA2IDYuOTMzNTlDMzcuNTEzMSAxMS44NjM5IDQwLjQyNDcgMTYuMTc5NiA0NC4xMjIxIDE5Ljg3N0M0Ny44MjE1IDIzLjU3NDUgNTIuMTM1NyAyNi40ODY5IDU3LjA2NjQgMjguNjA5NEM1OC45NTggMjkuNDI0MiA2MC44OTkgMzAuMDc5MSA2Mi44OTA2IDMwLjU4MTFDNjMuNTQxNSAzMC43NDQ4IDYzLjk5OTggMzEuMzI4MSA2NCAzMS45OTlDNjQgMzIuNjcwMSA2My41NDE3IDMzLjI1NDIgNjIuODkwNiAzMy40MThDNjAuODk5IDMzLjkxOTkgNTguOTU2MSAzNC41NzQ4IDU3LjA2NjQgMzUuMzg5NkM1Mi4xMzU4IDM3LjUxMjEgNDcuODE5NiA0MC40MjM3IDQ0LjEyMjEgNDQuMTIxMUM0MC40MjQ2IDQ3LjgyMDQgMzcuNTEzMSA1Mi4xMzQ5IDM1LjM5MDYgNTcuMDY1NEMzNC41NzU4IDU4Ljk1NyAzMy45MjA5IDYwLjg5ODEgMzMuNDE4OSA2Mi44ODk2QzMzLjI1NTIgNjMuNTQwNyAzMi42NzExIDYzLjk5OSAzMiA2My45OTlDMzEuMzI4OSA2My45OTkgMzAuNzQ0OCA2My41NDA3IDMwLjU4MTEgNjIuODg5NkMzMC4wNzkxIDYwLjg5ODEgMjkuNDI0MiA1OC45NTUxIDI4LjYwOTQgNTcuMDY1NEMyNi40ODY5IDUyLjEzNDkgMjMuNTc3MyA0Ny44MTg2IDE5Ljg3NzkgNDQuMTIxMUMxNi4xNzg2IDQwLjQyMzcgMTEuODY0MiAzNy41MTIxIDYuOTMzNTkgMzUuMzg5NkM1LjA0MjA0IDM0LjU3NDggMy4xMDA5NiAzMy45MTk5IDEuMTA5MzggMzMuNDE4QzAuNDU4MzA5IDMzLjI1NDIgMCAzMi42NzAxIDAgMzEuOTk5QzAuMDAwMjAxNTQ4IDMxLjMyODEgMC40NTg0NjMgMzAuNzQ0OCAxLjEwOTM4IDMwLjU4MTFDMy4xMDA5NiAzMC4wNzkxIDUuMDQzODYgMjkuNDI0MiA2LjkzMzU5IDI4LjYwOTRDMTEuODY0MyAyNi40ODY5IDE2LjE4MDQgMjMuNTc0NSAxOS44Nzc5IDE5Ljg3N0MyMy41NzUzIDE2LjE3OTYgMjYuNDg2OSAxMS44NjM5IDI4LjYwOTQgNi45MzM1OUMyOS40MjQyIDUuMDQyMDcgMzAuMDc5MSAzLjEwMDkzIDMwLjU4MTEgMS4xMDkzOEMzMC43NDQ4IDAuNDU4MjYgMzEuMzI4OSAwIDMyIDBaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMTA4NTlfNDg5NCkiPjwvcGF0aD48L21hc2s+PGcgbWFzaz0idXJsKCNtYXNrMF8xMDg1OV80ODk0KSI+PGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfZl8xMDg1OV80ODk0KSI+PGVsbGlwc2UgY3g9IjE0LjIwODQiIGN5PSIxNi43MTY0IiByeD0iMTQuMjA4NCIgcnk9IjE2LjcxNjQiIHRyYW5zZm9ybT0ibWF0cml4KDAuOTQyMzQzIDAuMzM0NjQ5IC0wLjMzNDY1NiAwLjk0MjM0IC03Ljk3OSAxMy43NzM1KSIgZmlsbD0iI0ZGRTQzMiI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyMV9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMjcuMDU0MyIgY3k9IjIuNTUxMTQiIHJ4PSIxOC4zOTQ0IiByeT0iMTguNzk4NSIgZmlsbD0iI0ZDNDEzRCI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyMl9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMTkuMjI0NSIgY3k9IjI0LjkwNDIiIHJ4PSIxOS4yMjQ1IiByeT0iMjQuOTA0MiIgdHJhbnNmb3JtPSJtYXRyaXgoMC45OTg4MDcgLTAuMDQ4ODI1NCAwLjA0ODgyNjYgMC45OTg4MDcgLTEuNzI3NzggMzIuNjU3MykiIGZpbGw9IiMwMEI5NUMiPjwvZWxsaXBzZT48L2c+PGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjNfZl8xMDg1OV80ODk0KSI+PGVsbGlwc2UgY3g9IjE5LjIyNDUiIGN5PSIyNC45MDQyIiByeD0iMTkuMjI0NSIgcnk9IjI0LjkwNDIiIHRyYW5zZm9ybT0ibWF0cml4KDAuOTk4ODA3IC0wLjA0ODgyNTQgMC4wNDg4MjY2IDAuOTk4ODA3IC0xLjcyNzc4IDMyLjY1NzMpIiBmaWxsPSIjMDBCOTVDIj48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXI0X2ZfMTA4NTlfNDg5NCkiPjxlbGxpcHNlIGN4PSIxOC44NDI5IiBjeT0iMjAuNzQ0MSIgcng9IjE4Ljg0MjkiIHJ5PSIyMC43NDQxIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjg1NDMwMSAtMC41MTk3NzkgMC41MTk3OSAwLjg1NDI5NCAtNy4xMzU3NCA0Ny41MDc4KSIgZmlsbD0iIzAwQjk1QyI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyNV9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iNjYuNDYxNyIgY3k9IjI0Ljk3NyIgcng9IjE4LjA5MzMiIHJ5PSIxNy40MjI5IiBmaWxsPSIjMzE4NkZGIj48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXI2X2ZfMTA4NTlfNDg5NCkiPjxlbGxpcHNlIGN4PSIyMC45MjkyIiBjeT0iMjIuMDc1MiIgcng9IjIwLjkyOTIiIHJ5PSIyMi4wNzUyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjc5NTk5IDAuNjA1MzEgLTAuNjA1MzIgMC43OTU5ODIgLTIuODE4ODUgLTcuNDMzMjMpIiBmaWxsPSIjRkJCQzA0Ij48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXI3X2ZfMTA4NTlfNDg5NCkiPjxlbGxpcHNlIGN4PSIyNC4xMzExIiBjeT0iMjIuMjkxOSIgcng9IjI0LjEzMTEiIHJ5PSIyMi4yOTE5IiB0cmFuc2Zvcm09Im1hdHJpeCgwLjgyNDAzNyAwLjU2NjUzNiAtMC41NjY1NDYgMC44MjQwMyAzOS42MzM4IDAuMzEwNjA4KSIgZmlsbD0iIzMxODZGRiI+PC9lbGxpcHNlPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyOF9mXzEwODU5XzQ4OTQpIj48cGF0aCBkPSJNNTQuMjI1NSAtMi4zMDQwM0M1Ny4wMTk1IDEuNDk0NjIgNTMuNDI5NCA4Ljg4MDQgNDYuMjA2OCAxNC4xOTI2QzM4Ljk4NDIgMTkuNTA0OCAzMC44NjQyIDIwLjczMTggMjguMDcwMiAxNi45MzMxQzI1LjI3NjIgMTMuMTM0NSAyOC44NjYzIDUuNzQ4NjcgMzYuMDg4OSAwLjQzNjQ4NkM0My4zMTE1IC00Ljg3NTcgNTEuNDMxNSAtNi4xMDI2NyA1NC4yMjU1IC0yLjMwNDAzWiIgZmlsbD0iIzc0OUJGRiI+PC9wYXRoPjwvZz48ZyBmaWx0ZXI9InVybCgjZmlsdGVyOV9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMjcuNTg1MyIgY3k9IjE3LjE0NzgiIHJ4PSIyNy41ODUzIiByeT0iMTcuMTQ3OCIgdHJhbnNmb3JtPSJtYXRyaXgoMC43MzMxNjYgLTAuNjgwMDQ5IDAuNjgwMDYxIDAuNzMzMTU1IC0xMi4yNTgzIDkuNDk2OTUpIiBmaWxsPSIjRkM0MTNEIj48L2VsbGlwc2U+PC9nPjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIxMF9mXzEwODU5XzQ4OTQpIj48ZWxsaXBzZSBjeD0iMTQuNzgxOSIgY3k9IjguNTk2MzciIHJ4PSIxNC43ODE5IiByeT0iOC41OTYzNyIgdHJhbnNmb3JtPSJtYXRyaXgoMC44MTMxODYgMC41ODIwMDQgLTAuNTgyMDE2IDAuODEzMTc3IDYuMzc4NDIgMzAuNTExKSIgZmlsbD0iI0ZGRUU0OCI+PC9lbGxpcHNlPjwvZz48L2c+PGRlZnM+PGZpbHRlciBpZD0iZmlsdGVyMF9mXzEwODU5XzQ4OTQiIHg9Ii0xOS42MTgiIHk9IjEyLjkwMjciIHdpZHRoPSIzOC44NjgxIiBoZWlnaHQ9IjQyLjc1NjIiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMi40NTk2NSIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyMV9mXzEwODU5XzQ4OTQiIHg9Ii0xNS4xMjIzIiB5PSItNDAuMDI5NiIgd2lkdGg9Ijg0LjM1MzMiIGhlaWdodD0iODUuMTYxNSIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ij48L2ZlRmxvb2Q+PGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0ic2hhcGUiPjwvZmVCbGVuZD48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxMS44OTExIiByZXN1bHQ9ImVmZmVjdDFfZm9yZWdyb3VuZEJsdXJfMTA4NTlfNDg5NCI+PC9mZUdhdXNzaWFuQmx1cj48L2ZpbHRlcj48ZmlsdGVyIGlkPSJmaWx0ZXIyX2ZfMTA4NTlfNDg5NCIgeD0iLTIwLjc2ODIiIHk9IjExLjQ4MzUiIHdpZHRoPSI3OC45MTYxIiBoZWlnaHQ9IjkwLjIxOTYiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMTAuMTA4NiIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyM19mXzEwODU5XzQ4OTQiIHg9Ii0yMC43NjgyIiB5PSIxMS40ODM1IiB3aWR0aD0iNzguOTE2MSIgaGVpZ2h0PSI5MC4yMTk2IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjEwLjEwODYiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjRfZl8xMDg1OV80ODk0IiB4PSItMTkuODUiIHk9IjE0Ljk2NjQiIHdpZHRoPSI3OS4xODg2IiBoZWlnaHQ9IjgwLjkzNzgiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMTAuMTA4NiIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyNV9mXzEwODU5XzQ4OTQiIHg9IjI5LjE1NjEiIHk9Ii0xMS42NTgyIiB3aWR0aD0iNzQuNjExMSIgaGVpZ2h0PSI3My4yNzAzIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjkuNjA2MTMiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjZfZl8xMDg1OV80ODk0IiB4PSItMzguMjkxIiB5PSItMTYuMjY4NyIgd2lkdGg9Ijc3LjUzOCIgaGVpZ2h0PSI3OC4xNTEzIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjguNzA1OTEiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjdfZl8xMDg1OV80ODk0IiB4PSI3Ljc4MDM4IiB5PSItNi4wOTgxIiB3aWR0aD0iNzguMjE4MSIgaGVpZ2h0PSI3Ni44OTgyIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjcuNzc0NzMiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjhfZl8xMDg1OV80ODk0IiB4PSIxMy4yMDgyIiB5PSItMTguNDI1IiB3aWR0aD0iNTUuODc5MyIgaGVpZ2h0PSI1MS40NzkxIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiPjwvZmVGbG9vZD48ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJzaGFwZSI+PC9mZUJsZW5kPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjYuOTU2OTQiIHJlc3VsdD0iZWZmZWN0MV9mb3JlZ3JvdW5kQmx1cl8xMDg1OV80ODk0Ij48L2ZlR2F1c3NpYW5CbHVyPjwvZmlsdGVyPjxmaWx0ZXIgaWQ9ImZpbHRlcjlfZl8xMDg1OV80ODk0IiB4PSItMTUuNDczOSIgeT0iLTMxLjAyNzIiIHdpZHRoPSI3MC4yMDM0IiBoZWlnaHQ9IjY4LjY3MzUiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCI+PC9mZUZsb29kPjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIj48L2ZlQmxlbmQ+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNS44NzU5OCIgcmVzdWx0PSJlZmZlY3QxX2ZvcmVncm91bmRCbHVyXzEwODU5XzQ4OTQiPjwvZmVHYXVzc2lhbkJsdXI+PC9maWx0ZXI+PGZpbHRlciBpZD0iZmlsdGVyMTBfZl8xMDg1OV80ODk0IiB4PSItMTQuMTczIiB5PSIyMC40NzQiIHdpZHRoPSI1NS4xMzczIiBoZWlnaHQ9IjUxLjI2MSIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ij48L2ZlRmxvb2Q+PGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0ic2hhcGUiPjwvZmVCbGVuZD48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI3LjI3MjUzIiByZXN1bHQ9ImVmZmVjdDFfZm9yZWdyb3VuZEJsdXJfMTA4NTlfNDg5NCI+PC9mZUdhdXNzaWFuQmx1cj48L2ZpbHRlcj48bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMTA4NTlfNDg5NCIgeDE9IjE4LjE5MzEiIHkxPSI0Mi44MjEiIHgyPSI1MS40MzM1IiB5Mj0iMTQuNzk1OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiM0ODkzRkMiPjwvc3RvcD48c3RvcCBvZmZzZXQ9IjAuMjciIHN0b3AtY29sb3I9IiM0ODkzRkMiPjwvc3RvcD48c3RvcCBvZmZzZXQ9IjAuNzc2OTgxIiBzdG9wLWNvbG9yPSIjOTY5REZGIj48L3N0b3A+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjQkQ5OUZFIj48L3N0b3A+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PC9zdmc+Cg=="},"displayName":"Google Gemini","typeVersion":1,"nodeCategories":[{"id":25,"name":"AI"},{"id":26,"name":"Langchain"}]}],"categories":[{"id":44,"name":"Crypto Trading"},{"id":49,"name":"AI Summarization"}],"image":[]}}