加密通貨 RSI 경고 시스템 및 EODHD, Telegram, TradingView 차트
중급
이것은Crypto Trading, Multimodal AI분야의자동화 워크플로우로, 15개의 노드를 포함합니다.주로 If, Set, Code, SplitOut, Telegram 등의 노드를 사용하며. EODHD, Telegram 및 TradingView 차트와 통합된 암호화폐 RSI 경고 시스템
사전 요구사항
- •Telegram Bot Token
- •대상 API의 인증 정보가 필요할 수 있음
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"meta": {
"instanceId": "c2b1f8c1d4a74a0cb8a1f1d3b7b0e7c1b9d2f5a8f3e44c9c93a1e2f4a6b7c8d9",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "m1",
"name": "'워크플로 실행' 클릭 시",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-520,
-160
],
"parameters": {},
"typeVersion": 1
},
{
"id": "note_overview",
"name": "Sticky Note — 개요",
"type": "n8n-nodes-base.stickyNote",
"position": [
-540,
-360
],
"parameters": {
"color": 6,
"width": 860,
"height": 260,
"content": "## Crypto RSI Alert Bot (overview)\n- Runs on a schedule or manual trigger.\n- Iterates a **watchlist** (BTC/ETH/SOL).\n- Fetches **intraday 1h** OHLCV from **EODHD** for each symbol.\n- Code node computes **Wilder's RSI(14)** and detects **30/70** crossings.\n- On signal, sends a **Telegram** alert (HTML) + **View chart** button (TradingView BINANCE/USD).\n\nEnv vars required:\n- `EODHD_TOKEN`\n- `TELEGRAM_CHAT_ID`"
},
"typeVersion": 1
},
{
"id": "set1",
"name": "필드 편집 (워치리스트)",
"type": "n8n-nodes-base.set",
"position": [
-300,
-160
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "sym_arr",
"name": "symbol",
"type": "array",
"value": "[\"BTC-USD.CC\",\"ETH-USD.CC\",\"SOL-USD.CC\"]"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "note_watchlist",
"name": "Sticky Note — 워치리스트",
"type": "n8n-nodes-base.stickyNote",
"position": [
-320,
-300
],
"parameters": {
"color": 6,
"width": 560,
"height": 160,
"content": "### Edit Fields (watchlist)\n- Defines the **symbol array**.\n- Make sure the field type is **Array** (String[]), not a single String.\n- Example output: `{ symbol: [\"BTC-USD.CC\",\"ETH-USD.CC\",\"SOL-USD.CC\"] }`"
},
"typeVersion": 1
},
{
"id": "split_out",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
-80,
-160
],
"parameters": {
"options": {},
"fieldToSplitOut": "symbol"
},
"typeVersion": 1
},
{
"id": "note_split",
"name": "Sticky Note — Split Out",
"type": "n8n-nodes-base.stickyNote",
"position": [
-100,
-300
],
"parameters": {
"color": 6,
"width": 520,
"height": 150,
"content": "### Split Out\n- Explodes the array into **one item per symbol**.\n- Input: 1 item with array → Output: N items like `{ symbol: \"BTC-USD.CC\" }`."
},
"typeVersion": 1
},
{
"id": "loop",
"name": "항목 루프",
"type": "n8n-nodes-base.splitInBatches",
"position": [
140,
-160
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "note_loop",
"name": "Sticky Note — 루프",
"type": "n8n-nodes-base.stickyNote",
"position": [
120,
-300
],
"parameters": {
"color": 6,
"width": 560,
"height": 120,
"content": "### Loop Over Items\n- Processes **one symbol per pass** to avoid mixing BTC/ETH/SOL candles.\n- Wiring: **Loop → HTTP → Code → back to Loop**. **Done → IF**."
},
"typeVersion": 1
},
{
"id": "http",
"name": "HTTP 요청 (EODHD 1시간 내 데이터)",
"type": "n8n-nodes-base.httpRequest",
"position": [
360,
-260
],
"parameters": {
"url": "=https://eodhd.com/api/intraday/{{ $json.symbol }}",
"method": "GET",
"options": {
"redirect": {
"redirect": {}
},
"response": {
"response": {}
},
"splitIntoItems": true
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "interval",
"value": "1h"
},
{
"name": "fmt",
"value": "json"
},
{
"name": "api_token",
"value": "={{ $env.EODHD_TOKEN }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "note_http",
"name": "Sticky Note — HTTP",
"type": "n8n-nodes-base.stickyNote",
"position": [
340,
-420
],
"parameters": {
"color": 6,
"width": 560,
"height": 160,
"content": "### HTTP (EODHD)\n- Fetches **intraday 1h OHLCV** for current symbol.\n- Token via env var `EODHD_TOKEN` → no secret in JSON.\n- **Split Into Items** enabled: 1 candle = 1 item (~2–3k items)."
},
"typeVersion": 1
},
{
"id": "code",
"name": "코드 (RSI + 메시지)",
"type": "n8n-nodes-base.code",
"position": [
580,
-260
],
"parameters": {
"jsCode": "// n8n Code node — Run Once for All Items\\n// Computes RSI(14) (Wilder) on 1h candles and raises 30/70 cross alerts.\\n\\nconst PERIOD = 14;\\nconst OVERBOUGHT = 70;\\nconst OVERSOLD = 30;\\n\\n// Collect ALL candles from the HTTP node (1 item = 1 candle)\\nconst inputItems = $input.all();\\nlet candles = [];\\nfor (const it of inputItems) {\\n if (Array.isArray(it.json)) candles.push(...it.json);\\n else candles.push(it.json);\\n}\\n\\n// Normalize + sort by time (prefer numeric timestamp, fallback to datetime)\\ncandles = candles\\n .filter(r => r && r.close !== undefined)\\n .map(r => ({\\n t: (Number.isFinite(+r.timestamp) ? +r.timestamp\\n : (typeof r.datetime === 'number' ? r.datetime : Date.parse(r.datetime)/1000)),\\n close: +r.close\\n }))\\n .filter(r => Number.isFinite(r.t) && Number.isFinite(r.close))\\n .sort((a,b) => a.t - b.t);\\n\\nif (candles.length < PERIOD + 2) {\\n return [{ json: { error: 'Not enough candles for RSI', count: candles.length } }];\\n}\\n\\nconst closes = candles.map(c => c.close);\\n\\n// Wilder RSI (full series)\\nfunction rsiSeries(values, period = 14) {\\n const deltas = [];\\n for (let i = 1; i < values.length; i++) deltas.push(values[i] - values[i - 1]);\\n let gain = 0, loss = 0;\\n for (let i = 0; i < period; i++) { const d = deltas[i]; if (d >= 0) gain += d; else loss -= d; }\\n let avgGain = gain / period; let avgLoss = loss / period;\\n const rsis = new Array(values.length).fill(null);\\n rsis[period] = avgLoss === 0 ? 100 : 100 - (100 / (1 + (avgGain / avgLoss)));\\n for (let i = period + 1; i < values.length; i++) {\\n const d = deltas[i - 1];\\n const up = Math.max(d, 0);\\n const down = Math.max(-d, 0);\\n avgGain = ((avgGain * (period - 1)) + up) / period;\\n avgLoss = ((avgLoss * (period - 1)) + down) / period;\\n const rs = avgLoss === 0 ? Infinity : (avgGain / avgLoss);\\n rsis[i] = 100 - (100 / (1 + rs));\\n }\\n return rsis;\\n}\\n\\nconst rsis = rsiSeries(closes, PERIOD);\\nconst lastIdx = rsis.length - 1;\\nconst rsiNow = +rsis[lastIdx].toFixed(1);\\nconst rsiPrev = +rsis[lastIdx - 1].toFixed(1);\\nconst lastClose = +closes[lastIdx].toFixed(2);\\nconst lastTs = candles[lastIdx].t;\\n\\n// Signals\\nlet signal = null;\\nif (rsiPrev > OVERSOLD && rsiNow <= OVERSOLD) signal = 'enter_oversold';\\nelse if (rsiPrev < OVERBOUGHT && rsiNow >= OVERBOUGHT) signal = 'enter_overbought';\\nelse if (rsiPrev <= OVERSOLD && rsiNow > OVERSOLD) signal = 'exit_oversold';\\nelse if (rsiPrev >= OVERBOUGHT && rsiNow < OVERBOUGHT) signal = 'exit_overbought';\\n\\n// ======= TEST TOGGLE (set true only to test Telegram delivery) =======\\nconst FORCE_ALERT = false; // keep false in production\\nconst FORCE_SIGNAL = 'enter_overbought'; // 'enter_oversold' | 'exit_oversold' | 'exit_overbought'\\nif (FORCE_ALERT) signal = FORCE_SIGNAL;\\n// =====================================================================\\n\\n// Current symbol from the loop item (fallback to input $json)\\nconst symbol = $('Split Out')?.item?.json?.symbol || $json.symbol || 'UNKNOWN';\\n\\nconst TF = '1h';\\nconst fmt = (n, d=2) => Number(n).toLocaleString('en-US',{minimumFractionDigits:d, maximumFractionDigits:d});\\nconst tsUTC = (ts) => new Date(ts*1000).toISOString().replace('T',' ').slice(0,16) + ' UTC';\\n\\nlet emoji = '🔔', headline = '';\\nif (signal === 'enter_oversold') { emoji='🔻'; headline = `enters <u>oversold</u> (RSI ${rsiNow} ≤ 30)`; }\\nelse if (signal === 'enter_overbought') { emoji='🚀'; headline = `enters <u>overbought</u> (RSI ${rsiNow} ≥ 70)`; }\\nelse if (signal === 'exit_oversold') { emoji='✅'; headline = `exits <u>oversold</u> (RSI ${rsiNow})`; }\\nelse if (signal === 'exit_overbought') { emoji='✅'; headline = `exits <u>overbought</u> (RSI ${rsiNow})`; }\\n\\nconst alertTextHtml = signal ? (\\n `${emoji} <b>${symbol}</b> ${headline}\\n` +\\n `Price: <b>$${fmt(lastClose)}</b> · TF: <b>${TF}</b> · ${tsUTC(lastTs)}\\n` +\\n `RSI: <b>${rsiPrev} → ${rsiNow}</b> (30/70)\\n` +\\n `— <i>RSI Heatwave</i>`\\n) : null;\\n\\nconst alertText = signal ? `${symbol} | ${headline.replace(/<[^>]*>/g,'')} | Price $${fmt(lastClose)} · TF ${TF} · ${tsUTC(lastTs)} | RSI ${rsiPrev}→${rsiNow}` : null;\\n\\n// TradingView link (BINANCE + USD) from EODHD symbol (e.g., BTC-USD.CC → BTCUSD)\\nconst rawSymbol = $('Split Out')?.item?.json?.symbol ?? $json.symbol ?? symbol;\\nconst sym = Array.isArray(rawSymbol) ? rawSymbol[0] : rawSymbol; \\nconst base = String(sym).split('-')[0].toUpperCase();\\nconst tradingViewUrl = `https://www.tradingview.com/symbols/${base}USD/?exchange=BINANCE`;\\n\\nreturn [{ json: { symbol, rsi: rsiNow, rsiPrev, period: PERIOD, lastClose, signal, timestamp: lastTs, alertText, alertTextHtml, tradingViewUrl } }];"
},
"typeVersion": 2
},
{
"id": "note_code",
"name": "Sticky Note — 코드",
"type": "n8n-nodes-base.stickyNote",
"position": [
560,
-420
],
"parameters": {
"color": 6,
"width": 560,
"height": 170,
"content": "### Code (RSI + message)\n- Sorts candles, computes **RSI(14)** (Wilder), detects 30/70 crossings.\n- Builds HTML message + TradingView URL (BINANCE/USD).\n- Testing: set `FORCE_ALERT = true`, then back to `false`."
},
"typeVersion": 1
},
{
"id": "if",
"name": "IF (신호 있음?)",
"type": "n8n-nodes-base.if",
"position": [
140,
40
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond1",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.signal }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "tg",
"name": "텍스트 메시지 전송",
"type": "n8n-nodes-base.telegram",
"position": [
360,
40
],
"parameters": {
"text": "={{ $json.alertTextHtml }}",
"chatId": "={{ $env.TELEGRAM_CHAT_ID }}",
"replyMarkup": "inlineKeyboard",
"inlineKeyboard": {
"rows": [
{
"row": {
"buttons": [
{
"text": "View chart",
"additionalFields": {
"url": "={{ $json.tradingViewUrl }}"
}
}
]
}
}
]
},
"additionalFields": {
"parse_mode": "HTML",
"disable_web_page_preview": true
}
},
"credentials": {
"telegramApi": {
"id": "REPLACE_WITH_YOUR_TELEGRAM_CRED_ID",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "note_tg",
"name": "Sticky Note — Telegram",
"type": "n8n-nodes-base.stickyNote",
"position": [
340,
180
],
"parameters": {
"color": 6,
"width": 560,
"height": 140,
"content": "### Telegram (delivery)\n- Parse Mode: **HTML**.\n- Text: `{{$json.alertTextHtml}}`.\n- Button: **View chart** → `{{$json.tradingViewUrl}}`.\n- Chat ID via env var `TELEGRAM_CHAT_ID`.\n- Bot token stays in Credentials."
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"split_out": {
"main": [
[
{
"node": "loop",
"type": "main",
"index": 0
}
]
]
},
"loop": {
"main": [
[
{
"node": "if",
"type": "main",
"index": 0
}
],
[
{
"node": "http",
"type": "main",
"index": 0
}
]
]
},
"if": {
"main": [
[
{
"node": "tg",
"type": "main",
"index": 0
}
]
]
},
"code": {
"main": [
[
{
"node": "loop",
"type": "main",
"index": 0
}
]
]
},
"set1": {
"main": [
[
{
"node": "split_out",
"type": "main",
"index": 0
}
]
]
},
"http": {
"main": [
[
{
"node": "code",
"type": "main",
"index": 0
}
]
]
},
"m1": {
"main": [
[
{
"node": "set1",
"type": "main",
"index": 0
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급 - 암호화폐 거래, 멀티모달 AI
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
콘텐츠생성기 v3
AI驱动블로그자동화:사용GPT-4생성并게시SEO기사至WordPress및Twitter
If
Set
Code
+
If
Set
Code
144 노드Jay Emp0
콘텐츠 제작
Freelancer.com 자동 입찰기 (Telegram 승인 및 AI 제안 포함)
Telegram 승인 및 AI 제안 생성을 지원하는 Freelancer.com 자동 입찰기
If
Set
Split Out
+
If
Set
Split Out
26 노드Mohamed Abdelwahab
리드 육성
Printify 자동화 - 제목 및 설명 업데이트 - AlexK1919
GPT-4o-mini를 사용하여 Printify용 SEO 제품 제목 및 설명 자동 생성
If
Set
Code
+
If
Set
Code
26 노드Amit Mehta
콘텐츠 제작
사이트 맵에서 벡터 스토리지로 크롤링: 효율적인 RAG 워크플로우 생성
사이트 맵에서 벡터 스토리지로: 효율적인 RAG 워크플로우 생성
If
Set
Xml
+
If
Set
Xml
40 노드Mariela Slavenova
콘텐츠 제작
자동화된 Leadfeeder와 Apollo.io 데이터 수집
유인물 풍부한 파이프라인: Leadfeeder에서 Apollo로 이동하여 Google 스프레드시트까지
If
Set
Code
+
If
Set
Code
23 노드Khairul Muhtadin
리드 생성
주식 분석 템플릿
기술 분석, AI, Telegram을 결합하여 주식 시장 통찰력 생성
If
Set
Code
+
If
Set
Code
25 노드Sergey Skorobogatov
암호화폐 거래