Skip to content

AI Trading Assistant — Telegram + TradingView Charts + Technical Analysis

Haris Khan

🧠 AI Trading Assistant — Telegram + TradingView Charts + Technical Analysis

📌 Summary

This workflow turns your Telegram bot into a complete AI-powered Trading Assistant.
Users can send any ticker symbol (e.g., AAPL, TSLA, XAUUSD, EURUSD 1h) and instantly receive:

  • A real-time TradingView-style chart
  • A professional AI-generated technical analysis
  • Key metrics: RSI, MACD, SMA20/50, ATR, volatility %, support & resistance
  • Clean Telegram-friendly Markdown output

Perfect for traders, analysts, crypto/forex groups, community bots, or personal automation.

📊 Overview

This workflow automates the entire lifecycle of market analysis:

  1. Receives a ticker symbol from Telegram
  2. Fetches real market data from TwelveData
  3. Computes technical indicators
  4. Converts the symbol to TradingView format
  5. Generates a full chart using Chart-IMG
  6. Produces an AI-written analysis using OpenAI
  7. Sends both back to Telegram

The result is a self-contained AI trading system built entirely inside n8n — no coding needed.

🚀 Key Features

🔹 Telegram Interface

Users message your bot directly with a ticker like:

AAPL
TSLA 1h
XAUUSD 1day
BTCUSD 4h

🔹 Intelligent Symbol Parsing

Automatically extracts:
Ticker
Interval
Removes command prefixes (/chart, /stock, etc.)

Defaults safely when no interval is provided

🔹 Market Data (via TwelveData)

Pulls:

100-candle OHLC time series

RSI(14)

MACD (macd, signal, hist)

🔹 Technical Indicator Engine (Custom JS)

Calculates:

Price change %

RSI interpretation

MACD momentum

SMA20 & SMA50 averages

SMA crossover direction

ATR14

Volatility %

Support / resistance from recent highs/lows

Bullish / Bearish / Neutral stance

🔹 TradingView Symbol Converter

Converts any instrument into proper format:

AAPL → NASDAQ:AAPL

TSLA → NASDAQ:TSLA

AMD → NASDAQ:AMD

XAUUSD → OANDA:XAUUSD

EURUSD → OANDA:EURUSD

BTCUSD → CRYPTO:BTCUSD

🔹 Interval Mapper

Converts intervals:

1h → 60

4h → 240

1day → 1D

1week → 1W

🔹 Chart Rendering (via Chart-IMG)

Generates a TradingView-style chart with:

Candlesticks

Volume

RSI

Stochastic RSI

Returns a shareable chart URL.

🔹 AI Market Summary (OpenAI)

GPT-4 produces:

Trend explanation

Momentum view

Overbought/oversold signals

MACD interpretation

Volatility context

IF/ELSE level map

Final stance summary

Formatted in perfect Telegram-friendly Markdown.

🔹 Telegram Output

The bot replies with:

Chart image

AI-generated analysis

Clear & clean formatting

🔧 How it Works (Node-by-Node)

  1. Telegram Trigger

Captures every message sent to the bot.

  1. Parse User Input

Extracts:

Symbol

Interval

Chat ID

Normalizes text and handles errors gracefully.

  1. Credential Loader

Imports all API keys from n8n Credentials Manager:

TwelveData

OpenAI

Chart-IMG

Telegram Bot Token

  1. Symbol Search

Matches user ticker with the correct asset.

  1. Market Data Retrieval

Fetches:

OHLC candles

RSI

MACD

  1. Merge Indicators

Combines all data into a single unified object.

  1. Technical Analysis Engine

Calculates every indicator, level, and stance.

  1. TradingView Format Conversion

Converts ticker + interval into proper TradingView format.

  1. Chart Generator

Requests chart rendering from Chart-IMG.

  1. OpenAI Summary Generator

Creates a structured market summary.

  1. Telegram Output

Sends:

The chart

AI analysis

Market stance

🧩 Requirements

Before activating the workflow, configure:

Telegram Bot Credentials

TwelveData API Key

Chart-IMG API Key

OpenAI API Key

All API keys must be stored in the n8n Credentials Manager.
No secrets exist in the workflow (Marketplace compliant).

💡 Usage Examples

Send to Telegram bot:

AAPL
TSLA 1h
GOLD 4h
EURUSD 15min
BTCUSD 1day

Bot replies with:

✔ Live chart
✔ Technical summary
✔ RSI / MACD
✔ SMA crossovers
✔ Support & resistance
✔ Bullish/Bearish/Neutral stance

🎯 Use Cases

Traders wanting instant analysis

Telegram financial group automation

Private or public trading bot

Forex analysis bot

Crypto signal bot

Portfolio and watchlist monitoring

AI-based charting tool

🛡 Compliance Notes

No hardcoded API keys

Uses Sticky Notes for clarity

All logic is original

Works on both n8n Cloud & Self-Hosted

Meets Marketplace requirements

🎉 Final Output

Every time a user sends a ticker:

➡ Chart-IMG generates a TradingView chart
➡ GPT analyzes market structure
➡ Telegram bot replies instantly

This workflow turns n8n into a full AI Trading Intelligence System—all automated, all real-time.

There’s nothing you
can’t automate with n8n

Our customer’s words, not ours.
Skeptical? Try it out, and see for yourself.

Use template

{ "id": "oy1OZVEMHmSYqGp5", "meta": { "instanceId": "feb34099c0c94eef2e6a7f4a0a877e34cd1f2361d9f835c09b80edd031586efb", "templateCredsSetupCompleted": true }, "name": "With Chart Analyze", "tags": [ "telegram", "trading", "stocks", "forex", "charts", "openai", "analysis" ], "nodes": [ { "id": "f51e1688-8b0f-4d87-95b6-94f8ee3c4a46", "name": "Telegram Trigger", "type": "n8n-nodes-base.telegramTrigger", "position": [ -1824, 176 ], "webhookId": "auto-generated", "parameters": { "updates": [ "message" ], "additionalFields": {} }, "credentials": { "telegramApi": { "id": "", "name": "Telegram Bot" } }, "typeVersion": 1.2 }, { "id": "14c684a6-78cf-4efb-9cb0-496a2818a765", "name": "Parse User Input", "type": "n8n-nodes-base.code", "position": [ -1312, 176 ], "parameters": { "jsCode": "// Parse user input -> { query, interval?, chat_id }\nconst update = $input.first().json;\nconst text = (update.message && update.message.text) ? update.message.text.trim() : '';\nconst chat_id = update.message?.chat?.id || update.message?.from?.id;\n// Strip a command prefix like /stock TSLA 1h\nlet cleaned = text.replace(/^\\/[a-zA-Z_]+\\s*/, '').trim();\nlet parts = cleaned.split(/\\s+/).filter(Boolean);\nlet query = parts[0] || 'AAPL';\nlet rawInterval = (parts[1] || '').toLowerCase();\nconst allowed = new Set(['1min','5min','15min','30min','45min','1h','2h','4h','1day','1week','1month']);\nlet interval = allowed.has(rawInterval) ? rawInterval : '1day';\nreturn [{ json: { raw_text: text, query, interval, chat_id } }];" }, "typeVersion": 2 }, { "id": "e11ea408-bcd3-489b-bd5a-9da75ec26a25", "name": "Edit Fields", "type": "n8n-nodes-base.set", "position": [ -816, 176 ], "parameters": { "options": {}, "assignments": { "assignments": [ { "id": "TWKEY", "name": "TWELVEDATA_KEY", "type": "string", "value": "" }, { "id": "OAKEY", "name": "OPENAI_API_KEY", "type": "string", "value": "" }, { "id": "TGKEY", "name": "TELEGRAM_BOT_TOKEN", "type": "string", "value": "" }, { "id": "CIKEY", "name": "CHARTIMG_API_KEY", "type": "string", "value": "" } ] } }, "typeVersion": 3.4 }, { "id": "20e4481f-1926-4881-935a-d292163a5d12", "name": "TwelveData Symbol Search", "type": "n8n-nodes-base.httpRequest", "position": [ -432, 176 ], "parameters": { "url": "https://api.twelvedata.com/symbol_search", "options": { "response": { "response": { "responseFormat": "json" } }, "allowUnauthorizedCerts": true }, "sendQuery": true, "queryParameters": { "parameters": [ { "name": "symbol", "value": "={{ $('Parse User Input').item.json.query }}" }, { "name": "apikey", "value": "={{ $('Edit Fields').item.json.TWELVEDATA_KEY }}" }, { "name": "interval", "value": "={{ $('Parse User Input').item.json.interval }}" }, { "name": "outputsize", "value": "1" } ] } }, "typeVersion": 4.2 }, { "id": "4a585370-6fbb-4eb5-8ebb-f210ec9c3303", "name": "Pick Top Match", "type": "n8n-nodes-base.code", "position": [ -112, 176 ], "parameters": { "jsCode": "// Pick a solid US/major-exchange match; fallback to first\nconst resp = $input.first().json;\nconst base = $('Parse User Input').first().json;\nconst list = Array.isArray(resp.data) ? resp.data : [];\nlet best = list.find(x => /NASDAQ|NYSE/i.test(x.exchange || ''))\n || list.find(x => (x.country === 'United States' || x.currency === 'USD'))\n || list[0];\nif (!best) return [{ json: { error: 'No symbol match', query: base.query, chat_id: base.chat_id } }];\nreturn [{ json: { symbol: best.symbol, name: best.name, exchange: best.exchange, interval: base.interval, chat_id: base.chat_id } }];" }, "typeVersion": 2 }, { "id": "8bb463ad-1780-4769-9659-367003ba05a9", "name": "Time Series (100)", "type": "n8n-nodes-base.httpRequest", "position": [ 416, 192 ], "parameters": { "url": "={{ \"https://api.twelvedata.com/time_series?symbol=\" + $json.symbol + \"&interval=\" + $json.interval + \"&outputsize=100&order=desc&apikey=\" + $('Edit Fields').item.json.TWELVEDATA_KEY }}", "options": { "response": { "response": { "responseFormat": "json" } } } }, "typeVersion": 4.2 }, { "id": "9940ea4f-605f-4d78-b5b0-0c86f32c17f3", "name": "RSI", "type": "n8n-nodes-base.httpRequest", "position": [ 400, 672 ], "parameters": { "url": "={{ \"https://api.twelvedata.com/rsi?symbol=\" + $json.symbol + \"&interval=\" + $json.interval + \"&time_period=14&outputsize=1&apikey=\" + $('Edit Fields').item.json.TWELVEDATA_KEY }}", "options": { "response": { "response": { "responseFormat": "json" } } } }, "typeVersion": 4.2 }, { "id": "08aad177-29f6-4dbb-bb31-3c6a8ec47d9a", "name": "MACD", "type": "n8n-nodes-base.httpRequest", "position": [ 416, -240 ], "parameters": { "url": "={{ \"https://api.twelvedata.com/macd?symbol=\" + $json.symbol + \"&interval=\" + $json.interval + \"&outputsize=1&apikey=\" + $('Edit Fields').item.json.TWELVEDATA_KEY }}", "options": { "response": { "response": { "responseFormat": "json" } } } }, "typeVersion": 4.2 }, { "id": "7cd82a5d-28ce-4d96-99d1-8b2f43a2d66c", "name": "Merge", "type": "n8n-nodes-base.merge", "position": [ 1136, 192 ], "parameters": { "numberInputs": 3 }, "typeVersion": 3.2 }, { "id": "67765d58-d898-4d0d-a9ee-8c52ae7f5f1d", "name": "Compute TA + Merge", "type": "n8n-nodes-base.code", "position": [ 1472, 192 ], "parameters": { "jsCode": "// --- read from merged inputs (MACD, TS, RSI) ---\nconst items = $input.all();\nconst mergedArray = Array.isArray($json) ? $json : items.map(i => i.json);\nconst isTS = o => Array.isArray(o?.values) && o.values[0] && ('open' in o.values[0] || 'close' in o.values[0]);\nconst isMACD= o => o?.meta?.indicator?.name?.toLowerCase().includes('macd');\nconst isRSI = o => o?.meta?.indicator?.name?.toLowerCase().includes('rsi');\nconst ts = mergedArray.find(isTS) || {};\nconst macd = mergedArray.find(isMACD) || {};\nconst rsi = mergedArray.find(isRSI) || {};\nconst ctx = $('Pick Top Match')?.first()?.json || {};\nconst values = Array.isArray(ts.values) ? ts.values : [];\nconst latest = values[0] || {};\nconst prev = values[1] || {};\nconst toNum = (v) => Number.parseFloat(v ?? 0);\nconst p = toNum(latest.close ?? latest.price);\nconst pp = toNum(prev.close ?? prev.price ?? p);\nconst changePct = pp ? (((p - pp) / pp) * 100).toFixed(2) : '0.00';\nconst rsiVal = toNum(rsi?.values?.[0]?.rsi ?? rsi?.value).toFixed(2);\nconst m0 = macd?.values?.[0] ?? {};\nconst macdVal = toNum(m0.macd).toFixed(3);\nconst macdSignal = toNum(m0.macd_signal).toFixed(3);\nconst macdHist = toNum(m0.macd_hist).toFixed(3);\n// ---- extras for detail ----\nconst highs = values.slice(0,50).map(b => toNum(b.high ?? b.close));\nconst lows = values.slice(0,50).map(b => toNum(b.low ?? b.close));\nconst closes= values.slice(0,50).map(b => toNum(b.close ?? b.price));\nconst sma = (arr,n)=> arr.slice(0,n).reduce((a,b)=>a+b,0)/Math.max(1,Math.min(n,arr.length));\nconst sma20 = Number.isFinite(sma(closes,20)) ? sma(closes,20) : 0;\nconst sma50 = Number.isFinite(sma(closes,50)) ? sma(closes,50) : 0;\nconst smaCross = (sma20 && sma50) ? (sma20 > sma50 ? 'bullish' : (sma20 < sma50 ? 'bearish' : 'flat')) : 'n/a';\nfunction calcATR(bars, period=14){\n const trs=[];\n for (let i=0;ia+b,0)/Math.min(period,trs.length);\n}\nconst atr14 = calcATR(values,14);\nconst volPct = p ? ((atr14/p)*100).toFixed(2) : '0.00';\nconst sup = Math.min(...lows.slice(0,20).filter(n=>Number.isFinite(n)));\nconst res = Math.max(...highs.slice(0,20).filter(n=>Number.isFinite(n)));\nlet stance = 'Neutral';\nif (+rsiVal && +macdVal && +macdSignal) {\n if (+rsiVal +macdSignal) stance = 'Bullish';\n else if (+rsiVal > 65 && +macdVal < +macdSignal) stance = 'Bearish';\n}\nreturn [{\n json: {\n symbol: ctx.symbol ?? ts?.meta?.symbol,\n name: ctx.name ?? ts?.meta?.type,\n exchange: ctx.exchange ?? ts?.meta?.type,\n interval: ctx.interval ?? ts?.meta?.interval,\n chat_id: ctx.chat_id,\n price: +p,\n change_pct: changePct,\n rsi: rsiVal,\n macd: { macd: macdVal, signal: macdSignal, hist: macdHist },\n sma: { sma20: +sma20.toFixed(2), sma50: +sma50.toFixed(2), cross: smaCross },\n atr14: +atr14.toFixed(2),\n volatility_pct: volPct,\n levels: { support: +sup.toFixed(2), resistance: +res.toFixed(2) },\n stance,\n last_time: latest.datetime ?? latest.time ?? ts?.meta?.last_update ?? ''\n }\n}];" }, "typeVersion": 2, "alwaysOutputData": true }, { "id": "c4b3c357-ce31-4626-9693-d9dfeacb3c73", "name": "Prep Chart Params", "type": "n8n-nodes-base.code", "position": [ 1360, 1296 ], "parameters": { "jsCode": "const s = $json.symbol || '';\nconst ex = ($json.exchange || '').toUpperCase();\n\n// Build TradingView symbol\nlet tv_symbol;\n\nif (s.includes('/')) {\n const cleaned = s.replace('/', '').toUpperCase();\n tv_symbol = cleaned === 'XAUUSD' ? 'OANDA:XAUUSD' : 'OANDA:' + cleaned;\n} else {\n if (ex.includes('NASDAQ')) tv_symbol = `NASDAQ:${s}`;\n else if (ex.includes('NYSE')) tv_symbol = `NYSE:${s}`;\n else if (ex.includes('AMEX')) tv_symbol = `AMEX:${s}`;\n else tv_symbol = `${ex}:${s}`;\n}\n\n// Interval mapping\nconst mapInt = {\n '1min': '1',\n '5min': '5',\n '15min': '15',\n '30min': '30',\n '45min': '45',\n '1h': '60',\n '2h': '120',\n '4h': '240',\n '1day': '1D',\n '1week': '1W',\n '1month': '1M'\n};\n\nconst tv_interval = mapInt[$json.interval] || '1D';\n\nreturn [{\n json: {\n ...$json,\n tv_symbol,\n tv_interval\n }\n}];\n" }, "typeVersion": 2 }, { "id": "963af5ce-777a-4d37-83b4-60ab57b7c5fd", "name": "Get Chart URL", "type": "n8n-nodes-base.httpRequest", "position": [ 2288, 1312 ], "parameters": { "url": "https://api.chart-img.com/v2/tradingview/advanced-chart/storage", "method": "POST", "options": { "response": { "response": { "responseFormat": "json" } } }, "jsonBody": "={\n \"symbol\": \"{{ $json.tv_symbol }}\",\n \"interval\": \"{{ $json.tv_interval }}\",\n \"style\": \"candle\",\n \"theme\": \"light\",\n \"override\": {\n \"showStudyLastValue\": false\n },\n \"studies\": [\n { \"name\": \"Volume\", \"forceOverlay\": true },\n { \"name\": \"Relative Strength Index\" },\n { \"name\": \"Stochastic RSI\" }\n ]\n}\n", "sendBody": true, "sendHeaders": true, "specifyBody": "json", "authentication": "predefinedCredentialType", "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" } ] }, "nodeCredentialType": "httpBearerAuth" }, "credentials": { "httpBearerAuth": { "id": "", "name": "Bearer YOUR_TOKEN_HERE account" } }, "typeVersion": 4.2 }, { "id": "5362abc9-ae7b-4573-afde-6578c349cfe9", "name": "Send Chart", "type": "n8n-nodes-base.telegram", "position": [ 3104, 352 ], "webhookId": "5ad1260a-2b2a-441f-b4e1-2a27c7030bcf", "parameters": { "file": "={{ $('Get Chart URL').item.json.url }}", "chatId": "={{ $json.chat_id }}", "operation": "sendPhoto", "additionalFields": {} }, "credentials": { "telegramApi": { "id": "", "name": "Telegram Bot" } }, "typeVersion": 1.2 }, { "id": "f4ac614c-d16f-483e-81ba-49402a61db39", "name": "Message a model", "type": "@n8n/n8n-nodes-langchain.openAi", "position": [ 2144, 144 ], "parameters": { "modelId": { "__rl": true, "mode": "list", "value": "gpt-4-turbo", "cachedResultName": "GPT-4-TURBO" }, "options": {}, "responses": { "values": [ { "role": "system", "content": "You are a concise stock/FX analyst. Use only the supplied metrics: price, change %, RSI(14), MACD, SMA20/50, ATR14, volatility %, support/resistance, stance. Write Telegram-friendly Markdown: 5–7 short bullets with emojis, then a one-line stance. Be specific about levels. No financial advice." }, { "content": "=**{{$json.name || $json.symbol}}** ({{$json.symbol}})\n_Exchange:_ {{$json.exchange}} | _Interval:_ {{$json.interval}}\nPrice: ${{ (Number($json.price)||0).toFixed(2) }} ({{$json.change_pct}}%)\nRSI(14): {{$json.rsi}} | MACD: {{$json.macd.macd}} / {{$json.macd.signal}} / {{$json.macd.hist}}\nSMA20/50: {{$json.sma.sma20}} / {{$json.sma.sma50}} ({{$json.sma.cross}}) | ATR14: {{$json.atr14}} (~{{$json.volatility_pct}}%)\nKey levels → Support: {{$json.levels.support}} • Resistance: {{$json.levels.resistance}}\nStance: {{$json.stance}}\n\nPlease deliver:\n- Trend summary and momentum 📈/📉\n- What RSI & MACD imply (overbought/oversold, crossovers) 🔎\n- Volatility context using ATR% ⚠️\n- Trade map with IF/ELSE levels (above R / below S) 📍\n- A quick risk note 🧯\n\nEnd with a single **stance line**." } ] }, "builtInTools": {} }, "credentials": { "openAiApi": { "id": "fqca6f9191wYr2PP", "name": "OpenAi account" } }, "typeVersion": 2 }, { "id": "9a52e7ca-43b9-478b-b924-c3d63ec83a37", "name": "Send a text message", "type": "n8n-nodes-base.telegram", "position": [ 3104, 80 ], "webhookId": "4e67fe68-c0e0-4ef9-a2e2-1dc1008c429c", "parameters": { "text": "={{ $json.output[0].content[0].text }}", "chatId": "={{ $json.chat_id }}", "additionalFields": {} }, "credentials": { "telegramApi": { "id": "", "name": "Telegram Bot" } }, "typeVersion": 1.2 }, { "id": "6fa78865-60bb-4e13-81b2-0fb29c053c32", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [ -1920, -160 ], "parameters": { "width": 320, "height": 560, "content": "## Entry Point — Telegram Message Listener\n\nThis section listens for all incoming Telegram messages.\nEvery user command or ticker symbol starts here.\nThe workflow extracts the raw message and passes it forward for parsing.\n\nThis is the main entry gate into the entire automation." }, "typeVersion": 1 }, { "id": "93856df0-4d4c-44ae-a418-4a407f850f0f", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [ -1504, -160 ], "parameters": { "width": 432, "height": 544, "content": "## User Input Parsing & Normalization\n\nThis block cleans and structures the user’s message.\nIt detects:\n\nThe ticker symbol (e.g., TSLA, XAUUSD, BTCUSD)\n\nThe timeframe (e.g., 1h, 1day, 4h, 15min)\n\nIt removes command prefixes and ensures invalid inputs fall back to defaults.\nThis guarantees the rest of the workflow receives clean, valid parameters." }, "typeVersion": 1 }, { "id": "5cdd5eac-712d-4642-95ef-a7d21d9de442", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [ -1024, -304 ], "parameters": { "width": 432, "height": 704, "content": "## Environment Setup: API Keys Loader\n\nThis is where we load all external API keys from n8n Credentials Manager.\nNo secrets are stored in the workflow — fully compliant with Marketplace rules.\n\n\n\n### This block injects:\n\nTwelveData API Key\n\nOpenAI Key\n\nChart-IMG Key\n\nTelegram Bot Token\n\nThe rest of the workflow depends on these dynamic credentials." }, "typeVersion": 1 }, { "id": "60a37db6-d575-4a34-b3f0-c957fcef1ac1", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [ -512, -192 ], "parameters": { "width": 528, "height": 624, "content": "## Symbol Resolution & Data Discovery\n\nThis section finds the correct financial instrument based on user input.\n\nSteps performed:\n\nSearches for symbol matches using TwelveData\n\nIdentifies the best match (NASDAQ/NYSE preferred)\n\nNormalizes exchange + symbol formatting\n\nEnsures accuracy for ambiguous tickers\n(e.g., “META”, “GOLD”, “XAUUSD”, “ADAUSD”)\n\nThis guarantees we chart the correct asset every time." }, "typeVersion": 1 }, { "id": "36a84abf-1b0a-43c1-9a3b-11a0a0e5139f", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [ 48, -592 ], "parameters": { "width": 784, "height": 1536, "content": "## Market Data Retrieval (Price + Indicators)\n\nHere we fetch real-time market data from TwelveData:\n\n100-candle time series\n\nRSI(14)\n\nMACD values (macd, signal, histogram)\n\nThese indicators form the foundation for all analysis and chart-building.\n\nThe node outputs are merged to create one unified TA dataset." }, "typeVersion": 1 }, { "id": "3777b877-3c57-4724-bfe8-eb752f734638", "name": "Sticky Note6", "type": "n8n-nodes-base.stickyNote", "position": [ 896, -592 ], "parameters": { "width": 784, "height": 1536, "content": "## Technical Analysis Engine (Core Logic)\n\nThis custom JavaScript block computes all technical signals:\n\nPrice change %\n\nRSI interpretation\n\nMACD trend & momentum\n\nSMA20/50 averages & crossovers\n\nATR(14)\n\nVolatility zones\n\nSupport & Resistance levels\n\nBullish / Bearish / Neutral stance\n\nThis is the brain of the workflow — the place where raw data becomes actionable insight." }, "typeVersion": 1 }, { "id": "be693cdd-d062-4e56-84d0-7b8f586b202c", "name": "Sticky Note7", "type": "n8n-nodes-base.stickyNote", "position": [ 848, 1024 ], "parameters": { "color": 5, "width": 784, "height": 608, "content": "## Convert to TradingView-Compatible Format\n\nChart-IMG requires TradingView-style symbols and intervals.\n\nThis block converts:\n\nAAPL → NASDAQ:AAPL\n\nTSLA → NASDAQ:TSLA\n\nXAUUSD → OANDA:XAUUSD\n\n1h → 60\n\n1day → 1D\n\nThis ensures chart rendering never fails due to invalid formats." }, "typeVersion": 1 }, { "id": "9d71fe3f-84f3-43c0-9794-cc61d0587583", "name": "Sticky Note8", "type": "n8n-nodes-base.stickyNote", "position": [ 1680, 1008 ], "parameters": { "color": 5, "width": 784, "height": 608, "content": "## Chart Generation (TradingView-Style Rendering)\n\nThis section sends the fully prepared request to Chart-IMG’s\nTradingView API to generate a candle chart with:\n\nVolume\n\nRSI\n\nStochastic RSI\n\nReturns a direct URL to the chart image, ready to send to Telegram." }, "typeVersion": 1 }, { "id": "5dafb762-f933-46f7-b89c-0bc47bbea5fa", "name": "Sticky Note9", "type": "n8n-nodes-base.stickyNote", "position": [ 1792, -192 ], "parameters": { "color": 3, "width": 688, "height": 608, "content": "## AI Market Summary (GPT-4o-mini)\n\nUses OpenAI to produce a compact but powerful market analysis:\n\nMomentum summary\n\nIndicator meaning\n\nVolatility context\n\nKey levels interpretation\n\nSuggested directional bias\n\nEverything is formatted specifically for Telegram Markdown." }, "typeVersion": 1 }, { "id": "5269a12e-0d25-49f7-8e71-42392e1fb202", "name": "Sticky Note10", "type": "n8n-nodes-base.stickyNote", "position": [ 2848, -368 ], "parameters": { "width": 592, "height": 1136, "content": "## Final Telegram Output\n\nThe workflow sends:\n\nA TradingView-style chart image\n\nA clean, structured AI-generated analysis message\n\nThis completes the full trading assistant cycle from input → analysis → output." }, "typeVersion": 1 } ], "active": true, "pinData": {}, "settings": { "executionOrder": "v1" }, "versionId": "b04b920d-a783-4a61-b8a4-08a78099f8bc", "connections": { "RSI": { "main": [ [ { "node": "Merge", "type": "main", "index": 2 } ] ] }, "MACD": { "main": [ [ { "node": "Merge", "type": "main", "index": 0 } ] ] }, "Merge": { "main": [ [ { "node": "Compute TA + Merge", "type": "main", "index": 0 } ] ] }, "Edit Fields": { "main": [ [ { "node": "TwelveData Symbol Search", "type": "main", "index": 0 } ] ] }, "Get Chart URL": { "main": [ [ { "node": "Send Chart", "type": "main", "index": 0 } ] ] }, "Pick Top Match": { "main": [ [ { "node": "MACD", "type": "main", "index": 0 }, { "node": "Time Series (100)", "type": "main", "index": 0 }, { "node": "RSI", "type": "main", "index": 0 } ] ] }, "Message a model": { "main": [ [ { "node": "Send a text message", "type": "main", "index": 0 } ] ] }, "Parse User Input": { "main": [ [ { "node": "Edit Fields", "type": "main", "index": 0 } ] ] }, "Telegram Trigger": { "main": [ [ { "node": "Parse User Input", "type": "main", "index": 0 } ] ] }, "Prep Chart Params": { "main": [ [ { "node": "Get Chart URL", "type": "main", "index": 0 } ] ] }, "Time Series (100)": { "main": [ [ { "node": "Merge", "type": "main", "index": 1 } ] ] }, "Compute TA + Merge": { "main": [ [ { "node": "Prep Chart Params", "type": "main", "index": 0 }, { "node": "Message a model", "type": "main", "index": 0 } ] ] }, "TwelveData Symbol Search": { "main": [ [ { "node": "Pick Top Match", "type": "main", "index": 0 } ] ] } } }

				
					{
  "id": "oy1OZVEMHmSYqGp5",
  "meta": {
    "instanceId": "feb34099c0c94eef2e6a7f4a0a877e34cd1f2361d9f835c09b80edd031586efb",
    "templateCredsSetupCompleted": true
  },
  "name": "With Chart Analyze",
  "tags": [
    "telegram",
    "trading",
    "stocks",
    "forex",
    "charts",
    "openai",
    "analysis"
  ],
  "nodes": [
    {
      "id": "f51e1688-8b0f-4d87-95b6-94f8ee3c4a46",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -1824,
        176
      ],
      "webhookId": "auto-generated",
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "",
          "name": "Telegram Bot"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "14c684a6-78cf-4efb-9cb0-496a2818a765",
      "name": "Parse User Input",
      "type": "n8n-nodes-base.code",
      "position": [
        -1312,
        176
      ],
      "parameters": {
        "jsCode": "// Parse user input -&gt; { query, interval?, chat_id }\nconst update = $input.first().json;\nconst text = (update.message &amp;&amp; update.message.text) ? update.message.text.trim() : '';\nconst chat_id = update.message?.chat?.id || update.message?.from?.id;\n// Strip a command prefix like /stock TSLA 1h\nlet cleaned = text.replace(/^\\/[a-zA-Z_]+\\s*/, '').trim();\nlet parts = cleaned.split(/\\s+/).filter(Boolean);\nlet query = parts[0] || 'AAPL';\nlet rawInterval = (parts[1] || '').toLowerCase();\nconst allowed = new Set(['1min','5min','15min','30min','45min','1h','2h','4h','1day','1week','1month']);\nlet interval = allowed.has(rawInterval) ? rawInterval : '1day';\nreturn [{ json: { raw_text: text, query, interval, chat_id } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "e11ea408-bcd3-489b-bd5a-9da75ec26a25",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -816,
        176
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "TWKEY",
              "name": "TWELVEDATA_KEY",
              "type": "string",
              "value": ""
            },
            {
              "id": "OAKEY",
              "name": "OPENAI_API_KEY",
              "type": "string",
              "value": ""
            },
            {
              "id": "TGKEY",
              "name": "TELEGRAM_BOT_TOKEN",
              "type": "string",
              "value": ""
            },
            {
              "id": "CIKEY",
              "name": "CHARTIMG_API_KEY",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "20e4481f-1926-4881-935a-d292163a5d12",
      "name": "TwelveData Symbol Search",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -432,
        176
      ],
      "parameters": {
        "url": "https://api.twelvedata.com/symbol_search",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "allowUnauthorizedCerts": true
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{ $('Parse User Input').item.json.query }}"
            },
            {
              "name": "apikey",
              "value": "={{ $('Edit Fields').item.json.TWELVEDATA_KEY }}"
            },
            {
              "name": "interval",
              "value": "={{ $('Parse User Input').item.json.interval }}"
            },
            {
              "name": "outputsize",
              "value": "1"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4a585370-6fbb-4eb5-8ebb-f210ec9c3303",
      "name": "Pick Top Match",
      "type": "n8n-nodes-base.code",
      "position": [
        -112,
        176
      ],
      "parameters": {
        "jsCode": "// Pick a solid US/major-exchange match; fallback to first\nconst resp = $input.first().json;\nconst base = $('Parse User Input').first().json;\nconst list = Array.isArray(resp.data) ? resp.data : [];\nlet best = list.find(x =&gt; /NASDAQ|NYSE/i.test(x.exchange || ''))\n         || list.find(x =&gt; (x.country === 'United States' || x.currency === 'USD'))\n         || list[0];\nif (!best) return [{ json: { error: 'No symbol match', query: base.query, chat_id: base.chat_id } }];\nreturn [{ json: { symbol: best.symbol, name: best.name, exchange: best.exchange, interval: base.interval, chat_id: base.chat_id } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "8bb463ad-1780-4769-9659-367003ba05a9",
      "name": "Time Series (100)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        416,
        192
      ],
      "parameters": {
        "url": "={{ \"https://api.twelvedata.com/time_series?symbol=\" + $json.symbol + \"&amp;interval=\" + $json.interval + \"&amp;outputsize=100&amp;order=desc&amp;apikey=\" + $('Edit Fields').item.json.TWELVEDATA_KEY }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "9940ea4f-605f-4d78-b5b0-0c86f32c17f3",
      "name": "RSI",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        672
      ],
      "parameters": {
        "url": "={{ \"https://api.twelvedata.com/rsi?symbol=\" + $json.symbol + \"&amp;interval=\" + $json.interval + \"&amp;time_period=14&amp;outputsize=1&amp;apikey=\" + $('Edit Fields').item.json.TWELVEDATA_KEY }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "08aad177-29f6-4dbb-bb31-3c6a8ec47d9a",
      "name": "MACD",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        416,
        -240
      ],
      "parameters": {
        "url": "={{ \"https://api.twelvedata.com/macd?symbol=\" + $json.symbol + \"&amp;interval=\" + $json.interval + \"&amp;outputsize=1&amp;apikey=\" + $('Edit Fields').item.json.TWELVEDATA_KEY }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7cd82a5d-28ce-4d96-99d1-8b2f43a2d66c",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        1136,
        192
      ],
      "parameters": {
        "numberInputs": 3
      },
      "typeVersion": 3.2
    },
    {
      "id": "67765d58-d898-4d0d-a9ee-8c52ae7f5f1d",
      "name": "Compute TA + Merge",
      "type": "n8n-nodes-base.code",
      "position": [
        1472,
        192
      ],
      "parameters": {
        "jsCode": "// --- read from merged inputs (MACD, TS, RSI) ---\nconst items = $input.all();\nconst mergedArray = Array.isArray($json) ? $json : items.map(i =&gt; i.json);\nconst isTS  = o =&gt; Array.isArray(o?.values) &amp;&amp; o.values[0] &amp;&amp; ('open' in o.values[0] || 'close' in o.values[0]);\nconst isMACD= o =&gt; o?.meta?.indicator?.name?.toLowerCase().includes('macd');\nconst isRSI = o =&gt; o?.meta?.indicator?.name?.toLowerCase().includes('rsi');\nconst ts   = mergedArray.find(isTS)   || {};\nconst macd = mergedArray.find(isMACD) || {};\nconst rsi  = mergedArray.find(isRSI)  || {};\nconst ctx  = $('Pick Top Match')?.first()?.json || {};\nconst values = Array.isArray(ts.values) ? ts.values : [];\nconst latest = values[0] || {};\nconst prev   = values[1] || {};\nconst toNum = (v) =&gt; Number.parseFloat(v ?? 0);\nconst p  = toNum(latest.close ?? latest.price);\nconst pp = toNum(prev.close   ?? prev.price   ?? p);\nconst changePct = pp ? (((p - pp) / pp) * 100).toFixed(2) : '0.00';\nconst rsiVal = toNum(rsi?.values?.[0]?.rsi ?? rsi?.value).toFixed(2);\nconst m0 = macd?.values?.[0] ?? {};\nconst macdVal    = toNum(m0.macd).toFixed(3);\nconst macdSignal = toNum(m0.macd_signal).toFixed(3);\nconst macdHist   = toNum(m0.macd_hist).toFixed(3);\n// ---- extras for detail ----\nconst highs = values.slice(0,50).map(b =&gt; toNum(b.high ?? b.close));\nconst lows  = values.slice(0,50).map(b =&gt; toNum(b.low  ?? b.close));\nconst closes= values.slice(0,50).map(b =&gt; toNum(b.close ?? b.price));\nconst sma = (arr,n)=&gt; arr.slice(0,n).reduce((a,b)=&gt;a+b,0)/Math.max(1,Math.min(n,arr.length));\nconst sma20 = Number.isFinite(sma(closes,20)) ? sma(closes,20) : 0;\nconst sma50 = Number.isFinite(sma(closes,50)) ? sma(closes,50) : 0;\nconst smaCross = (sma20 &amp;&amp; sma50) ? (sma20 &gt; sma50 ? 'bullish' : (sma20 &lt; sma50 ? &#039;bearish&#039; : &#039;flat&#039;)) : &#039;n/a&#039;;\nfunction calcATR(bars, period=14){\n  const trs=[];\n  for (let i=0;i<Math>a+b,0)/Math.min(period,trs.length);\n}\nconst atr14 = calcATR(values,14);\nconst volPct = p ? ((atr14/p)*100).toFixed(2) : '0.00';\nconst sup = Math.min(...lows.slice(0,20).filter(n=&gt;Number.isFinite(n)));\nconst res = Math.max(...highs.slice(0,20).filter(n=&gt;Number.isFinite(n)));\nlet stance = 'Neutral';\nif (+rsiVal &amp;&amp; +macdVal &amp;&amp; +macdSignal) {\n  if (+rsiVal  +macdSignal) stance = 'Bullish';\n  else if (+rsiVal &gt; 65 &amp;&amp; +macdVal &lt; +macdSignal) stance = &#039;Bearish&#039;;\n}\nreturn [{\n  json: {\n    symbol:   ctx.symbol   ?? ts?.meta?.symbol,\n    name:     ctx.name     ?? ts?.meta?.type,\n    exchange: ctx.exchange ?? ts?.meta?.type,\n    interval: ctx.interval ?? ts?.meta?.interval,\n    chat_id:  ctx.chat_id,\n    price: +p,\n    change_pct: changePct,\n    rsi: rsiVal,\n    macd: { macd: macdVal, signal: macdSignal, hist: macdHist },\n    sma: { sma20: +sma20.toFixed(2), sma50: +sma50.toFixed(2), cross: smaCross },\n    atr14: +atr14.toFixed(2),\n    volatility_pct: volPct,\n    levels: { support: +sup.toFixed(2), resistance: +res.toFixed(2) },\n    stance,\n    last_time: latest.datetime ?? latest.time ?? ts?.meta?.last_update ?? &#039;&#039;\n  }\n}];&quot;
      },
      &quot;typeVersion&quot;: 2,
      &quot;alwaysOutputData&quot;: true
    },
    {
      &quot;id&quot;: &quot;c4b3c357-ce31-4626-9693-d9dfeacb3c73&quot;,
      &quot;name&quot;: &quot;Prep Chart Params&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.code&quot;,
      &quot;position&quot;: [
        1360,
        1296
      ],
      &quot;parameters&quot;: {
        &quot;jsCode&quot;: &quot;const s = $json.symbol || &#039;&#039;;\nconst ex = ($json.exchange || &#039;&#039;).toUpperCase();\n\n// Build TradingView symbol\nlet tv_symbol;\n\nif (s.includes(&#039;/&#039;)) {\n    const cleaned = s.replace(&#039;/&#039;, &#039;&#039;).toUpperCase();\n    tv_symbol = cleaned === &#039;XAUUSD&#039; ? &#039;OANDA:XAUUSD&#039; : &#039;OANDA:&#039; + cleaned;\n} else {\n    if (ex.includes(&#039;NASDAQ&#039;)) tv_symbol = `NASDAQ:${s}`;\n    else if (ex.includes(&#039;NYSE&#039;)) tv_symbol = `NYSE:${s}`;\n    else if (ex.includes(&#039;AMEX&#039;)) tv_symbol = `AMEX:${s}`;\n    else tv_symbol = `${ex}:${s}`;\n}\n\n// Interval mapping\nconst mapInt = {\n    &#039;1min&#039;: &#039;1&#039;,\n    &#039;5min&#039;: &#039;5&#039;,\n    &#039;15min&#039;: &#039;15&#039;,\n    &#039;30min&#039;: &#039;30&#039;,\n    &#039;45min&#039;: &#039;45&#039;,\n    &#039;1h&#039;: &#039;60&#039;,\n    &#039;2h&#039;: &#039;120&#039;,\n    &#039;4h&#039;: &#039;240&#039;,\n    &#039;1day&#039;: &#039;1D&#039;,\n    &#039;1week&#039;: &#039;1W&#039;,\n    &#039;1month&#039;: &#039;1M&#039;\n};\n\nconst tv_interval = mapInt[$json.interval] || &#039;1D&#039;;\n\nreturn [{\n    json: {\n        ...$json,\n        tv_symbol,\n        tv_interval\n    }\n}];\n&quot;
      },
      &quot;typeVersion&quot;: 2
    },
    {
      &quot;id&quot;: &quot;963af5ce-777a-4d37-83b4-60ab57b7c5fd&quot;,
      &quot;name&quot;: &quot;Get Chart URL&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.httpRequest&quot;,
      &quot;position&quot;: [
        2288,
        1312
      ],
      &quot;parameters&quot;: {
        &quot;url&quot;: &quot;https://api.chart-img.com/v2/tradingview/advanced-chart/storage&quot;,
        &quot;method&quot;: &quot;POST&quot;,
        &quot;options&quot;: {
          &quot;response&quot;: {
            &quot;response&quot;: {
              &quot;responseFormat&quot;: &quot;json&quot;
            }
          }
        },
        &quot;jsonBody&quot;: &quot;={\n  \&quot;symbol\&quot;: \&quot;{{ $json.tv_symbol }}\&quot;,\n  \&quot;interval\&quot;: \&quot;{{ $json.tv_interval }}\&quot;,\n  \&quot;style\&quot;: \&quot;candle\&quot;,\n  \&quot;theme\&quot;: \&quot;light\&quot;,\n  \&quot;override\&quot;: {\n    \&quot;showStudyLastValue\&quot;: false\n  },\n  \&quot;studies\&quot;: [\n    { \&quot;name\&quot;: \&quot;Volume\&quot;, \&quot;forceOverlay\&quot;: true },\n    { \&quot;name\&quot;: \&quot;Relative Strength Index\&quot; },\n    { \&quot;name\&quot;: \&quot;Stochastic RSI\&quot; }\n  ]\n}\n&quot;,
        &quot;sendBody&quot;: true,
        &quot;sendHeaders&quot;: true,
        &quot;specifyBody&quot;: &quot;json&quot;,
        &quot;authentication&quot;: &quot;predefinedCredentialType&quot;,
        &quot;headerParameters&quot;: {
          &quot;parameters&quot;: [
            {
              &quot;name&quot;: &quot;Content-Type&quot;,
              &quot;value&quot;: &quot;application/json&quot;
            }
          ]
        },
        &quot;nodeCredentialType&quot;: &quot;httpBearerAuth&quot;
      },
      &quot;credentials&quot;: {
        &quot;httpBearerAuth&quot;: {
          &quot;id&quot;: &quot;&quot;,
          &quot;name&quot;: &quot;Bearer YOUR_TOKEN_HERE account&quot;
        }
      },
      &quot;typeVersion&quot;: 4.2
    },
    {
      &quot;id&quot;: &quot;5362abc9-ae7b-4573-afde-6578c349cfe9&quot;,
      &quot;name&quot;: &quot;Send Chart&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.telegram&quot;,
      &quot;position&quot;: [
        3104,
        352
      ],
      &quot;webhookId&quot;: &quot;5ad1260a-2b2a-441f-b4e1-2a27c7030bcf&quot;,
      &quot;parameters&quot;: {
        &quot;file&quot;: &quot;={{ $(&#039;Get Chart URL&#039;).item.json.url }}&quot;,
        &quot;chatId&quot;: &quot;={{ $json.chat_id }}&quot;,
        &quot;operation&quot;: &quot;sendPhoto&quot;,
        &quot;additionalFields&quot;: {}
      },
      &quot;credentials&quot;: {
        &quot;telegramApi&quot;: {
          &quot;id&quot;: &quot;&quot;,
          &quot;name&quot;: &quot;Telegram Bot&quot;
        }
      },
      &quot;typeVersion&quot;: 1.2
    },
    {
      &quot;id&quot;: &quot;f4ac614c-d16f-483e-81ba-49402a61db39&quot;,
      &quot;name&quot;: &quot;Message a model&quot;,
      &quot;type&quot;: &quot;@n8n/n8n-nodes-langchain.openAi&quot;,
      &quot;position&quot;: [
        2144,
        144
      ],
      &quot;parameters&quot;: {
        &quot;modelId&quot;: {
          &quot;__rl&quot;: true,
          &quot;mode&quot;: &quot;list&quot;,
          &quot;value&quot;: &quot;gpt-4-turbo&quot;,
          &quot;cachedResultName&quot;: &quot;GPT-4-TURBO&quot;
        },
        &quot;options&quot;: {},
        &quot;responses&quot;: {
          &quot;values&quot;: [
            {
              &quot;role&quot;: &quot;system&quot;,
              &quot;content&quot;: &quot;You are a concise stock/FX analyst. Use only the supplied metrics: price, change %, RSI(14), MACD, SMA20/50, ATR14, volatility %, support/resistance, stance. Write Telegram-friendly Markdown: 5–7 short bullets with emojis, then a one-line stance. Be specific about levels. No financial advice.&quot;
            },
            {
              &quot;content&quot;: &quot;=**{{$json.name || $json.symbol}}** ({{$json.symbol}})\n_Exchange:_ {{$json.exchange}}  |  _Interval:_ {{$json.interval}}\nPrice: ${{ (Number($json.price)||0).toFixed(2) }} ({{$json.change_pct}}%)\nRSI(14): {{$json.rsi}}  |  MACD: {{$json.macd.macd}} / {{$json.macd.signal}} / {{$json.macd.hist}}\nSMA20/50: {{$json.sma.sma20}} / {{$json.sma.sma50}} ({{$json.sma.cross}})  |  ATR14: {{$json.atr14}} (~{{$json.volatility_pct}}%)\nKey levels → Support: {{$json.levels.support}}  •  Resistance: {{$json.levels.resistance}}\nStance: {{$json.stance}}\n\nPlease deliver:\n- Trend summary and momentum 📈/📉\n- What RSI &amp; MACD imply (overbought/oversold, crossovers) 🔎\n- Volatility context using ATR% ⚠️\n- Trade map with IF/ELSE levels (above R / below S) 📍\n- A quick risk note 🧯\n\nEnd with a single **stance line**.&quot;
            }
          ]
        },
        &quot;builtInTools&quot;: {}
      },
      &quot;credentials&quot;: {
        &quot;openAiApi&quot;: {
          &quot;id&quot;: &quot;fqca6f9191wYr2PP&quot;,
          &quot;name&quot;: &quot;OpenAi account&quot;
        }
      },
      &quot;typeVersion&quot;: 2
    },
    {
      &quot;id&quot;: &quot;9a52e7ca-43b9-478b-b924-c3d63ec83a37&quot;,
      &quot;name&quot;: &quot;Send a text message&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.telegram&quot;,
      &quot;position&quot;: [
        3104,
        80
      ],
      &quot;webhookId&quot;: &quot;4e67fe68-c0e0-4ef9-a2e2-1dc1008c429c&quot;,
      &quot;parameters&quot;: {
        &quot;text&quot;: &quot;={{ $json.output[0].content[0].text }}&quot;,
        &quot;chatId&quot;: &quot;={{ $json.chat_id }}&quot;,
        &quot;additionalFields&quot;: {}
      },
      &quot;credentials&quot;: {
        &quot;telegramApi&quot;: {
          &quot;id&quot;: &quot;&quot;,
          &quot;name&quot;: &quot;Telegram Bot&quot;
        }
      },
      &quot;typeVersion&quot;: 1.2
    },
    {
      &quot;id&quot;: &quot;6fa78865-60bb-4e13-81b2-0fb29c053c32&quot;,
      &quot;name&quot;: &quot;Sticky Note2&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        -1920,
        -160
      ],
      &quot;parameters&quot;: {
        &quot;width&quot;: 320,
        &quot;height&quot;: 560,
        &quot;content&quot;: &quot;## Entry Point — Telegram Message Listener\n\nThis section listens for all incoming Telegram messages.\nEvery user command or ticker symbol starts here.\nThe workflow extracts the raw message and passes it forward for parsing.\n\nThis is the main entry gate into the entire automation.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;93856df0-4d4c-44ae-a418-4a407f850f0f&quot;,
      &quot;name&quot;: &quot;Sticky Note&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        -1504,
        -160
      ],
      &quot;parameters&quot;: {
        &quot;width&quot;: 432,
        &quot;height&quot;: 544,
        &quot;content&quot;: &quot;## User Input Parsing &amp; Normalization\n\nThis block cleans and structures the user’s message.\nIt detects:\n\nThe ticker symbol (e.g., TSLA, XAUUSD, BTCUSD)\n\nThe timeframe (e.g., 1h, 1day, 4h, 15min)\n\nIt removes command prefixes and ensures invalid inputs fall back to defaults.\nThis guarantees the rest of the workflow receives clean, valid parameters.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;5cdd5eac-712d-4642-95ef-a7d21d9de442&quot;,
      &quot;name&quot;: &quot;Sticky Note1&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        -1024,
        -304
      ],
      &quot;parameters&quot;: {
        &quot;width&quot;: 432,
        &quot;height&quot;: 704,
        &quot;content&quot;: &quot;## Environment Setup: API Keys Loader\n\nThis is where we load all external API keys from n8n Credentials Manager.\nNo secrets are stored in the workflow — fully compliant with Marketplace rules.\n\n\n\n### This block injects:\n\nTwelveData API Key\n\nOpenAI Key\n\nChart-IMG Key\n\nTelegram Bot Token\n\nThe rest of the workflow depends on these dynamic credentials.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;60a37db6-d575-4a34-b3f0-c957fcef1ac1&quot;,
      &quot;name&quot;: &quot;Sticky Note4&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        -512,
        -192
      ],
      &quot;parameters&quot;: {
        &quot;width&quot;: 528,
        &quot;height&quot;: 624,
        &quot;content&quot;: &quot;## Symbol Resolution &amp; Data Discovery\n\nThis section finds the correct financial instrument based on user input.\n\nSteps performed:\n\nSearches for symbol matches using TwelveData\n\nIdentifies the best match (NASDAQ/NYSE preferred)\n\nNormalizes exchange + symbol formatting\n\nEnsures accuracy for ambiguous tickers\n(e.g., “META”, “GOLD”, “XAUUSD”, “ADAUSD”)\n\nThis guarantees we chart the correct asset every time.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;36a84abf-1b0a-43c1-9a3b-11a0a0e5139f&quot;,
      &quot;name&quot;: &quot;Sticky Note5&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        48,
        -592
      ],
      &quot;parameters&quot;: {
        &quot;width&quot;: 784,
        &quot;height&quot;: 1536,
        &quot;content&quot;: &quot;## Market Data Retrieval (Price + Indicators)\n\nHere we fetch real-time market data from TwelveData:\n\n100-candle time series\n\nRSI(14)\n\nMACD values (macd, signal, histogram)\n\nThese indicators form the foundation for all analysis and chart-building.\n\nThe node outputs are merged to create one unified TA dataset.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;3777b877-3c57-4724-bfe8-eb752f734638&quot;,
      &quot;name&quot;: &quot;Sticky Note6&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        896,
        -592
      ],
      &quot;parameters&quot;: {
        &quot;width&quot;: 784,
        &quot;height&quot;: 1536,
        &quot;content&quot;: &quot;## Technical Analysis Engine (Core Logic)\n\nThis custom JavaScript block computes all technical signals:\n\nPrice change %\n\nRSI interpretation\n\nMACD trend &amp; momentum\n\nSMA20/50 averages &amp; crossovers\n\nATR(14)\n\nVolatility zones\n\nSupport &amp; Resistance levels\n\nBullish / Bearish / Neutral stance\n\nThis is the brain of the workflow — the place where raw data becomes actionable insight.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;be693cdd-d062-4e56-84d0-7b8f586b202c&quot;,
      &quot;name&quot;: &quot;Sticky Note7&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        848,
        1024
      ],
      &quot;parameters&quot;: {
        &quot;color&quot;: 5,
        &quot;width&quot;: 784,
        &quot;height&quot;: 608,
        &quot;content&quot;: &quot;## Convert to TradingView-Compatible Format\n\nChart-IMG requires TradingView-style symbols and intervals.\n\nThis block converts:\n\nAAPL → NASDAQ:AAPL\n\nTSLA → NASDAQ:TSLA\n\nXAUUSD → OANDA:XAUUSD\n\n1h → 60\n\n1day → 1D\n\nThis ensures chart rendering never fails due to invalid formats.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;9d71fe3f-84f3-43c0-9794-cc61d0587583&quot;,
      &quot;name&quot;: &quot;Sticky Note8&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        1680,
        1008
      ],
      &quot;parameters&quot;: {
        &quot;color&quot;: 5,
        &quot;width&quot;: 784,
        &quot;height&quot;: 608,
        &quot;content&quot;: &quot;## Chart Generation (TradingView-Style Rendering)\n\nThis section sends the fully prepared request to Chart-IMG’s\nTradingView API to generate a candle chart with:\n\nVolume\n\nRSI\n\nStochastic RSI\n\nReturns a direct URL to the chart image, ready to send to Telegram.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;5dafb762-f933-46f7-b89c-0bc47bbea5fa&quot;,
      &quot;name&quot;: &quot;Sticky Note9&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        1792,
        -192
      ],
      &quot;parameters&quot;: {
        &quot;color&quot;: 3,
        &quot;width&quot;: 688,
        &quot;height&quot;: 608,
        &quot;content&quot;: &quot;## AI Market Summary (GPT-4o-mini)\n\nUses OpenAI to produce a compact but powerful market analysis:\n\nMomentum summary\n\nIndicator meaning\n\nVolatility context\n\nKey levels interpretation\n\nSuggested directional bias\n\nEverything is formatted specifically for Telegram Markdown.&quot;
      },
      &quot;typeVersion&quot;: 1
    },
    {
      &quot;id&quot;: &quot;5269a12e-0d25-49f7-8e71-42392e1fb202&quot;,
      &quot;name&quot;: &quot;Sticky Note10&quot;,
      &quot;type&quot;: &quot;n8n-nodes-base.stickyNote&quot;,
      &quot;position&quot;: [
        2848,
        -368
      ],
      &quot;parameters&quot;: {
        &quot;width&quot;: 592,
        &quot;height&quot;: 1136,
        &quot;content&quot;: &quot;## Final Telegram Output\n\nThe workflow sends:\n\nA TradingView-style chart image\n\nA clean, structured AI-generated analysis message\n\nThis completes the full trading assistant cycle from input → analysis → output.&quot;
      },
      &quot;typeVersion&quot;: 1
    }
  ],
  &quot;active&quot;: true,
  &quot;pinData&quot;: {},
  &quot;settings&quot;: {
    &quot;executionOrder&quot;: &quot;v1&quot;
  },
  &quot;versionId&quot;: &quot;b04b920d-a783-4a61-b8a4-08a78099f8bc&quot;,
  &quot;connections&quot;: {
    &quot;RSI&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Merge&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 2
          }
        ]
      ]
    },
    &quot;MACD&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Merge&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Merge&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Compute TA + Merge&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Edit Fields&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;TwelveData Symbol Search&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Get Chart URL&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Send Chart&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Pick Top Match&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;MACD&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          },
          {
            &quot;node&quot;: &quot;Time Series (100)&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          },
          {
            &quot;node&quot;: &quot;RSI&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Message a model&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Send a text message&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Parse User Input&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Edit Fields&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Telegram Trigger&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Parse User Input&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Prep Chart Params&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Get Chart URL&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;Time Series (100)&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Merge&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 1
          }
        ]
      ]
    },
    &quot;Compute TA + Merge&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Prep Chart Params&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          },
          {
            &quot;node&quot;: &quot;Message a model&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    },
    &quot;TwelveData Symbol Search&quot;: {
      &quot;main&quot;: [
        [
          {
            &quot;node&quot;: &quot;Pick Top Match&quot;,
            &quot;type&quot;: &quot;main&quot;,
            &quot;index&quot;: 0
          }
        ]
      ]
    }
  }
}