移动平均线交叉股票提醒机器人
中级
这是一个Crypto Trading领域的自动化工作流,包含 14 个节点。主要使用 If, Set, Code, Postgres, SplitOut 等节点。 使用 Alpha Vantage 和 Discord 的股市技术分析提醒
前置要求
- •PostgreSQL 数据库连接信息
- •可能需要目标 API 的认证凭证
分类
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"name": "移动平均线交叉股票提醒机器人",
"tags": [],
"nodes": [
{
"id": "f3acef6a-a25d-4f8a-ba03-62731eb2caff",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
-480,
0
],
"parameters": {
"color": 4,
"width": 700,
"height": 200,
"content": "## 📊 股票市场分析工作流"
},
"typeVersion": 1
},
{
"id": "5860551a-7e28-4a2e-9b57-9c9eee052027",
"name": "触发器 - 每日收盘",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-420,
260
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1,
2,
3,
4,
5
],
"triggerAtHour": 17
}
]
}
},
"typeVersion": 1.2
},
{
"id": "db84cf85-94dd-401e-996c-30961d12d496",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
220,
460
],
"parameters": {
"width": 400,
"height": 220,
"content": "## 📈 数据收集阶段"
},
"typeVersion": 1
},
{
"id": "694bb8c3-5c80-44db-831d-70dce4ba872b",
"name": "计算60/120日SMA",
"type": "n8n-nodes-base.code",
"position": [
1120,
260
],
"parameters": {
"jsCode": "try {\n // 1. Configurable windows\n const SMA_SHORT = 60;\n const SMA_LONG = 120;\n const NEEDED = SMA_LONG + 1; // long window + one extra day\n // 2. Pull in all rows (each item.json has { id, symbol, Date, Close })\n const rows = items.map(i => i.json);\n // 3. Group rows by symbol\n const bySymbol = {};\n for (const r of rows) {\n if (!bySymbol[r.symbol]) bySymbol[r.symbol] = [];\n bySymbol[r.symbol].push(r);\n }\n // 4. SMA helper\n function sma(arr, n, offset = 0) {\n const slice = arr.slice(offset, offset + n);\n return slice.reduce((sum, v) => sum + v, 0) / n;\n }\n // 5. Compute per‐symbol\n const output = [];\n for (const symbol of Object.keys(bySymbol)) {\n const group = bySymbol[symbol]\n .sort((a, b) => new Date(b.Date) - new Date(a.Date)) // newest→oldest\n .slice(0, NEEDED);\n if (group.length < NEEDED) {\n output.push({\n json: {\n symbol,\n error: `Not enough data for ${symbol}: ${group.length}/${NEEDED} days`\n }\n });\n continue;\n }\n const closes = group.map(r => parseFloat(r.Close));\n output.push({\n json: {\n symbol,\n sma60_current: sma(closes, SMA_SHORT, 0),\n sma120_current: sma(closes, SMA_LONG, 0),\n sma60_previous: sma(closes, SMA_SHORT, 1),\n sma120_previous: sma(closes, SMA_LONG, 1),\n }\n });\n }\n return output;\n} catch (err) {\n // global error fallback\n return [{ json: { error: err.message } }];\n}"
},
"retryOnFail": false,
"typeVersion": 2,
"alwaysOutputData": false
},
{
"id": "f6aae56c-2129-4435-8895-89759829cdcf",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
940,
460
],
"parameters": {
"color": 5,
"width": 380,
"height": 260,
"content": "## 🧮 技术分析引擎"
},
"typeVersion": 1
},
{
"id": "c9101e92-f8af-4ef6-b28e-18a1ccc932b5",
"name": "拆分 - 股票代码",
"type": "n8n-nodes-base.splitOut",
"position": [
20,
260
],
"parameters": {
"options": {},
"fieldToSplitOut": "symbol"
},
"typeVersion": 1
},
{
"id": "c31e3d37-1926-46bc-86a9-f89e3927704b",
"name": "获取每日历史数据",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
240,
260
],
"parameters": {
"url": "https://www.alphavantage.co/query",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "function",
"value": "TIME_SERIES_DAILY"
},
{
"name": "symbol",
"value": "={{$json[\"symbol\"]}}"
},
{
"name": "apikey",
"value": "YOURKEYHERE"
},
{
"name": "outputsize",
"value": "compact"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "f9b56387-fef9-4efc-943b-ac0971f9d522",
"name": "设置 - 股票代码列表",
"type": "n8n-nodes-base.set",
"notes": "* IWM: Russell 2000 small-cap ETF – captures breadth/volatility outside the large-cap space\n\n* INTC: Intel – value-oriented semiconductor play that often lags/drifts differently than NVDA\n\n* JPM: JP Morgan – bellwether for the financial sector (banks & credit), interest-rate sensitivity\n\n* META: Meta Platforms – mega-cap digital advertising/social media momentum outside pure semis\n\n* NVDA: NVIDIA – leading-edge GPU/AI growth driver, often the pace-setter in tech rallies\n\n* PG: Procter & Gamble – defensive consumer staples, counter-cyclical when risk assets wobble\n\n* SPY: S\\&P 500 ETF – broad large-cap benchmark, anchors the overall market trend\n\n* TSLA: Tesla – high-beta auto/EV hybrid, adds extra swing-intensity vs. other tech names\n\n* XLB: Materials ETF – pure play on chemicals, metals & mining, driven by commodity cycles\n\n* XLE: Energy ETF – oil & gas sector, sensitive to crude swings, low correlation with semis\n\n* XLI: Industrials ETF – aerospace, transport & machinery, captures industrial-cycle turns\n\n* XLU: Utilities ETF – defensive “bond-like” sector, shines in risk-off/stress regimes\n\n* XLV: Health Care ETF – pharma/biotech/devices, often moves independently of the wider market",
"position": [
-200,
260
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ec70e7cd-3ee8-45d9-bf2e-c53483296ec9",
"name": "symbol",
"type": "array",
"value": "[\"NVDA\",\"JPM\",\"PG\",\"SPY\"]"
}
]
}
},
"notesInFlow": false,
"typeVersion": 3.4
},
{
"id": "2aec21da-cbba-4574-9794-98ccdafb268b",
"name": "获取今日数据",
"type": "n8n-nodes-base.code",
"position": [
460,
260
],
"parameters": {
"jsCode": "return items.map(item => {\n const payload = item.json;\n const ts = payload[\"Time Series (Daily)\"];\n \n // ← GUARD against missing or bad TS object\n if (!ts || typeof ts !== 'object') {\n return {\n json: {\n symbol: payload[\"Meta Data\"]?.[\"2. Symbol\"] || null,\n error: payload.Note \n || payload[\"Error Message\"] \n || \"No time series returned\",\n }\n };\n }\n\n const dates = Object.keys(ts).sort((a,b) => new Date(a) - new Date(b));\n if (dates.length < 2) {\n throw new Error(\"Not enough data to pick the day before last\");\n }\n\n const date = dates[dates.length - 2];\n const day = ts[date];\n\n return {\n json: {\n symbol: payload[\"Meta Data\"][\"2. Symbol\"],\n date,\n close: day[\"4. close\"],\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "825fab86-cdea-49f3-b162-65b2d4e26ea0",
"name": "在表中插入行",
"type": "n8n-nodes-base.postgres",
"position": [
680,
260
],
"parameters": {
"table": {
"__rl": true,
"mode": "list",
"value": "historical_stocks",
"cachedResultName": "historical_stocks"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"value": {
"Date": "={{ $json.date }}",
"Close": "={{ $json.close }}",
"symbol": "={{ $json.symbol }}"
},
"schema": [
{
"id": "id",
"type": "number",
"display": true,
"removed": true,
"required": false,
"displayName": "id",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "symbol",
"type": "string",
"display": true,
"required": true,
"displayName": "symbol",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Date",
"type": "dateTime",
"display": true,
"required": true,
"displayName": "Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Close",
"type": "number",
"display": true,
"required": true,
"displayName": "Close",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"skipOnConflict": true
}
},
"retryOnFail": false,
"typeVersion": 2.6,
"alwaysOutputData": false
},
{
"id": "6389ccdd-02e0-48b8-93e8-730cbdfda1b6",
"name": "HTTP请求",
"type": "n8n-nodes-base.httpRequest",
"position": [
2200,
260
],
"parameters": {
"url": "https://discord.com/api/webhooks/YOURWEBHOOKHERE",
"method": "POST",
"options": {},
"sendBody": true,
"sendQuery": true,
"bodyParameters": {
"parameters": [
{
"name": "content",
"value": "={{$json[\"content\"]}}"
}
]
},
"queryParameters": {
"parameters": [
{}
]
}
},
"typeVersion": 4.2
},
{
"id": "d0eb4106-9bb0-4752-bb68-59c9d5d15d1a",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2080,
0
],
"parameters": {
"color": 7,
"width": 380,
"height": 220,
"content": "## 🔔 Discord通知"
},
"typeVersion": 1
},
{
"id": "6420c403-0bf5-4de3-a82b-080292be75ec",
"name": "如果(📈)",
"type": "n8n-nodes-base.if",
"position": [
1340,
260
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f2b85554-8cec-49f5-8bd4-cccc1e15cc6f",
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ $json.sma60_previous }}",
"rightValue": "={{ $json.sma120_previous }}"
},
{
"id": "83d8772f-d217-4b76-84d1-b23bf245357c",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.sma60_current }}",
"rightValue": "={{ $json.sma120_current }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "1910f369-5404-45e6-a225-048b5fc34316",
"name": "便签4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1320,
-120
],
"parameters": {
"color": 6,
"width": 320,
"height": 260,
"content": "## 🚦 信号检测逻辑"
},
"typeVersion": 1
},
{
"id": "1ef9e403-7021-45d5-91b6-cea5f7e30a3e",
"name": "如果(📉)",
"type": "n8n-nodes-base.if",
"position": [
1560,
360
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f9e8087a-af4d-43e9-b0da-f12d9564b48f",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.sma60_previous }}",
"rightValue": "={{ $json.sma120_previous }}"
},
{
"id": "53c773b1-9740-43d7-b5cd-04e8b6256ace",
"operator": {
"type": "number",
"operation": "lt"
},
"leftValue": "={{ $json.sma60_current }}",
"rightValue": "={{ $json.sma120_current }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "77a4c20b-a7a1-46e2-8ffa-70b4310d75f9",
"name": "设置 - 无信号消息",
"type": "n8n-nodes-base.set",
"position": [
1780,
260
],
"parameters": {
"values": {
"string": [
{
"name": "content",
"value": "={{ `🟡↔️ No crossover today for ${$items().map(i=>i.json.symbol).sort((a,b)=>a.localeCompare(b)).join(\", \")}. Monitoring continues…` }}"
}
]
},
"options": {},
"keepOnlySet": true
},
"executeOnce": true,
"typeVersion": 2
},
{
"id": "c7f0cba2-2d46-483a-9f8e-ca741da5c2e6",
"name": "设置 - 死亡交叉消息",
"type": "n8n-nodes-base.set",
"position": [
1780,
460
],
"parameters": {
"values": {
"string": [
{
"name": "content",
"value": "={{ `🔴📉 Death Cross Alert for **${$json[\"symbol\"]}**! The 60-day SMA has crossed below the 120-day SMA.` }}"
}
]
},
"options": {},
"keepOnlySet": true
},
"typeVersion": 2
},
{
"id": "3926135c-6308-479d-9e1f-1da7248a01ea",
"name": "设置 - 黄金交叉消息",
"type": "n8n-nodes-base.set",
"position": [
1780,
60
],
"parameters": {
"values": {
"string": [
{
"name": "content",
"value": "={{ `🟢📈 Golden Cross Alert for **${$json[\"symbol\"]}**! The 60-day SMA has crossed above the 120-day SMA.` }}"
}
]
},
"options": {},
"keepOnlySet": true
},
"typeVersion": 2
},
{
"id": "34474f1f-9caf-4d6e-bbd9-294aa8a73cfb",
"name": "执行 SQL 查询",
"type": "n8n-nodes-base.postgres",
"position": [
900,
260
],
"parameters": {
"query": "WITH numbered AS (\n SELECT\n *,\n ROW_NUMBER() OVER (\n PARTITION BY symbol\n ORDER BY \"Date\" DESC\n ) AS rn\n FROM public.historical_stocks\nWHERE symbol IN ('NVDA','JPM','SPY','PG')\n)\nSELECT\n id,\n symbol,\n \"Date\",\n \"Close\"\nFROM numbered\nWHERE rn <= 121\nORDER BY symbol, \"Date\" DESC;",
"options": {},
"operation": "executeQuery"
},
"executeOnce": true,
"typeVersion": 2.6
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"connections": {
"If (📈)": {
"main": [
[
{
"node": "Set - Golden Cross Msg",
"type": "main",
"index": 0
}
],
[
{
"node": "If (📉)",
"type": "main",
"index": 0
}
]
]
},
"If (📉)": {
"main": [
[
{
"node": "Set - Death Cross Msg",
"type": "main",
"index": 0
}
],
[
{
"node": "Set - No Signal Msg",
"type": "main",
"index": 0
}
]
]
},
"Split - Tickers": {
"main": [
[
{
"node": "Fetch Daily History",
"type": "main",
"index": 0
}
]
]
},
"Set - Ticker List": {
"main": [
[
{
"node": "Split - Tickers",
"type": "main",
"index": 0
}
]
]
},
"Compute 60/120 SMAs": {
"main": [
[
{
"node": "If (📈)",
"type": "main",
"index": 0
}
]
]
},
"Execute a SQL query": {
"main": [
[
{
"node": "Compute 60/120 SMAs",
"type": "main",
"index": 0
}
]
]
},
"Fetch Daily History": {
"main": [
[
{
"node": "Getting today's data",
"type": "main",
"index": 0
}
]
]
},
"Set - No Signal Msg": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Getting today's data": {
"main": [
[
{
"node": "Insert rows in a table",
"type": "main",
"index": 0
}
]
]
},
"Set - Death Cross Msg": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Trigger - Daily Close": {
"main": [
[
{
"node": "Set - Ticker List",
"type": "main",
"index": 0
}
]
]
},
"Insert rows in a table": {
"main": [
[
{
"node": "Execute a SQL query",
"type": "main",
"index": 0
}
]
]
},
"Set - Golden Cross Msg": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
中级 - 加密货币交易
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
情感分析机器人
使用Google Gemini和EODHD新闻API实现自动化股票情感分析
If
Code
Http Request
+6
18 节点Raz Hadas
加密货币交易
Alapaca 交易自动化
AI驱动的自动化股票交易:集成Alpaca和Google Sheets
Code
Wait
Merge
+5
19 节点Raz Hadas
加密货币交易
股票分析模板
结合技术分析、AI和Telegram发布生成股票市场洞察
If
Set
Code
+12
25 节点Sergey Skorobogatov
加密货币交易
使用GPT-4.1、Outlook和Mem.ai自动化Microsoft Teams会议分析
使用GPT-4.1、Outlook和Mem.ai自动化Microsoft Teams会议分析
If
Set
Code
+19
61 节点Wayne Simpson
人力资源
宠物店 4
🐶 宠物店预约 AI 代理
If
Set
Code
+41
187 节点Bruno Dias
人工智能
交付汉堡店MVP
🤖 餐厅与配送自动化的 AI 驱动 WhatsApp 助手
If
Set
Code
+37
152 节点Bruno Dias
工作流信息
难度等级
中级
节点数量14
分类1
节点类型7
作者
Raz Hadas
@raz-hadasCo-founder of buildmyflow, on a mission to create powerful and easy-to-use n8n automation templates. With a background in AI and a passion for social impact as the co-founder of TovTech, I'm dedicated to building a community-focused resource for free and premium workflows that save you time and unlock new possibilities. Let's automate together! https://www.linkedin.com/in/raz-hadas/
外部链接
在 n8n.io 查看 →
分享此工作流