Files
trading_bot_v3/v4/n8n-workflow-complete.json
mindesbunister 4a9a80f045 feat: Add n8n workflow for TradingView webhook automation
- Complete 13-node workflow connecting TradingView alerts to trade execution
- Webhook validation, risk checking, and trade execution via v4 API
- Telegram notifications for all scenarios (success/error/blocked)
- Comprehensive setup guide with testing and troubleshooting
- Supports SOL/BTC/ETH and LONG/SHORT signals from TradingView
2025-10-23 15:11:37 +02:00

396 lines
12 KiB
JSON

{
"name": "Trading Bot v4 - Execute Trade",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "tradingview-webhook",
"options": {
"rawBody": true
}
},
"name": "Webhook - TradingView Alert",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
250,
300
],
"webhookId": "your-unique-webhook-id",
"id": "webhook-node"
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.body.secret}}",
"operation": "equals",
"value2": "={{$env.TRADINGVIEW_WEBHOOK_SECRET}}"
}
]
}
},
"name": "Validate Secret",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
450,
300
],
"id": "validate-secret-node"
},
{
"parameters": {
"functionCode": "// Parse TradingView alert data\nconst body = $input.item.json.body;\n\n// Extract signal information\nconst signal = {\n symbol: body.symbol || body.ticker || 'SOLUSDT',\n action: body.action || body.signal_type || 'buy',\n timeframe: body.timeframe || body.interval || '5',\n price: body.price || body.close,\n timestamp: body.timestamp || body.timenow || new Date().toISOString(),\n strength: body.strength || 'strong',\n strategy: body.strategy || '5min_scalp_v4'\n};\n\n// Normalize action to 'long' or 'short'\nlet direction = 'long';\nif (signal.action) {\n const actionLower = signal.action.toLowerCase();\n if (actionLower.includes('sell') || actionLower.includes('short')) {\n direction = 'short';\n }\n}\n\n// Build API payload for v4 execute endpoint\nconst payload = {\n symbol: signal.symbol,\n direction: direction,\n timeframe: signal.timeframe,\n signalStrength: signal.strength,\n signalPrice: parseFloat(signal.price)\n};\n\n// Pass through original data + processed payload\nreturn {\n json: {\n original: body,\n signal: signal,\n apiPayload: payload,\n timestamp: new Date().toISOString()\n }\n};"
},
"name": "Parse TradingView Signal",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
650,
200
],
"id": "parse-signal-node"
},
{
"parameters": {
"url": "={{$env.TRADING_BOT_API_URL}}/api/trading/check-risk",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{$env.API_SECRET_KEY}}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "symbol",
"value": "={{$json.apiPayload.symbol}}"
},
{
"name": "direction",
"value": "={{$json.apiPayload.direction}}"
}
]
},
"options": {}
},
"name": "Check Risk Limits",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
850,
200
],
"id": "check-risk-node"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{$json.allowed}}",
"value2": true
}
]
}
},
"name": "Risk Check Passed?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
1050,
200
],
"id": "risk-check-if-node"
},
{
"parameters": {
"url": "={{$env.TRADING_BOT_API_URL}}/api/trading/execute",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{$env.API_SECRET_KEY}}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "json",
"body": "={{JSON.stringify($('Parse TradingView Signal').item.json.apiPayload)}}",
"options": {
"timeout": 30000
}
},
"name": "Execute Trade on Drift",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1250,
100
],
"id": "execute-trade-node"
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{$json.success}}",
"value2": true
}
]
}
},
"name": "Trade Executed?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
1450,
100
],
"id": "trade-executed-if-node"
},
{
"parameters": {
"functionCode": "// Format success message for Telegram\nconst trade = $json.trade || {};\nconst signal = $('Parse TradingView Signal').item.json.signal;\n\nconst direction = trade.direction?.toUpperCase() || signal.action?.toUpperCase();\nconst emoji = direction === 'LONG' ? '🟢' : '🔴';\n\nconst message = `${emoji} **TRADE EXECUTED**\\n\\n` +\n `📊 **Symbol:** ${trade.symbol || signal.symbol}\\n` +\n `📈 **Direction:** ${direction}\\n` +\n `💰 **Entry Price:** $${trade.entryPrice?.toFixed(4) || signal.price}\\n` +\n `💵 **Position Size:** $${trade.positionSize?.toFixed(2) || 'N/A'}\\n` +\n `⚡ **Leverage:** ${trade.leverage || 10}x\\n` +\n `\\n` +\n `🎯 **Targets:**\\n` +\n ` Stop Loss: $${trade.stopLoss?.toFixed(4) || 'N/A'} (${trade.stopLossPercent || -1.5}%)\\n` +\n ` TP1: $${trade.takeProfit1?.toFixed(4) || 'N/A'} (+${trade.tp1Percent || 0.7}%)\\n` +\n ` TP2: $${trade.takeProfit2?.toFixed(4) || 'N/A'} (+${trade.tp2Percent || 1.5}%)\\n` +\n `\\n` +\n `📊 **Slippage:** ${trade.entrySlippage?.toFixed(3) || '0'}%\\n` +\n `⏰ **Time:** ${new Date(trade.timestamp || Date.now()).toLocaleString()}\\n` +\n `\\n` +\n `✅ Position is now being monitored automatically.\\n` +\n `Auto-exit at TP/SL levels.`;\n\nreturn {\n json: {\n message: message,\n trade: trade,\n signal: signal\n }\n};"
},
"name": "Format Success Message",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
1650,
50
],
"id": "format-success-node"
},
{
"parameters": {
"functionCode": "// Format error message for Telegram\nconst error = $json.error || $json.message || 'Unknown error';\nconst signal = $('Parse TradingView Signal').item.json.signal;\n\nconst message = `❌ **TRADE FAILED**\\n\\n` +\n `📊 **Symbol:** ${signal.symbol}\\n` +\n `📈 **Direction:** ${signal.action?.toUpperCase()}\\n` +\n `⏰ **Time:** ${new Date().toLocaleString()}\\n` +\n `\\n` +\n `🚫 **Error:** ${error}\\n` +\n `\\n` +\n `Please check logs and try again.`;\n\nreturn {\n json: {\n message: message,\n error: error,\n signal: signal\n }\n};"
},
"name": "Format Error Message",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
1650,
200
],
"id": "format-error-node"
},
{
"parameters": {
"functionCode": "// Format risk blocked message\nconst reason = $json.reason || 'Risk limit exceeded';\nconst signal = $('Parse TradingView Signal').item.json.signal;\n\nconst message = `⚠️ **TRADE BLOCKED**\\n\\n` +\n `📊 **Symbol:** ${signal.symbol}\\n` +\n `📈 **Direction:** ${signal.action?.toUpperCase()}\\n` +\n `⏰ **Time:** ${new Date().toLocaleString()}\\n` +\n `\\n` +\n `🛑 **Reason:** ${reason}\\n` +\n `\\n` +\n `Trade was not executed due to risk limits.`;\n\nreturn {\n json: {\n message: message,\n reason: reason,\n signal: signal\n }\n};"
},
"name": "Format Risk Blocked Message",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
1250,
300
],
"id": "format-risk-blocked-node"
},
{
"parameters": {
"chatId": "={{$env.TELEGRAM_CHAT_ID}}",
"text": "={{$json.message}}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"name": "Telegram - Send Notification",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1850,
150
],
"credentials": {
"telegramApi": {
"id": "1",
"name": "Telegram Bot"
}
},
"id": "telegram-node"
},
{
"parameters": {
"functionCode": "// Invalid webhook secret\nconst message = `🚨 **UNAUTHORIZED WEBHOOK**\\n\\n` +\n `⚠️ Someone tried to trigger a trade with invalid credentials.\\n` +\n `\\n` +\n `⏰ **Time:** ${new Date().toLocaleString()}\\n` +\n `\\n` +\n `Please check your TradingView webhook configuration.`;\n\nreturn {\n json: {\n message: message\n }\n};"
},
"name": "Format Invalid Secret",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
650,
400
],
"id": "format-invalid-secret-node"
}
],
"connections": {
"Webhook - TradingView Alert": {
"main": [
[
{
"node": "Validate Secret",
"type": "main",
"index": 0
}
]
]
},
"Validate Secret": {
"main": [
[
{
"node": "Parse TradingView Signal",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Invalid Secret",
"type": "main",
"index": 0
}
]
]
},
"Parse TradingView Signal": {
"main": [
[
{
"node": "Check Risk Limits",
"type": "main",
"index": 0
}
]
]
},
"Check Risk Limits": {
"main": [
[
{
"node": "Risk Check Passed?",
"type": "main",
"index": 0
}
]
]
},
"Risk Check Passed?": {
"main": [
[
{
"node": "Execute Trade on Drift",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Risk Blocked Message",
"type": "main",
"index": 0
}
]
]
},
"Execute Trade on Drift": {
"main": [
[
{
"node": "Trade Executed?",
"type": "main",
"index": 0
}
]
]
},
"Trade Executed?": {
"main": [
[
{
"node": "Format Success Message",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Error Message",
"type": "main",
"index": 0
}
]
]
},
"Format Success Message": {
"main": [
[
{
"node": "Telegram - Send Notification",
"type": "main",
"index": 0
}
]
]
},
"Format Error Message": {
"main": [
[
{
"node": "Telegram - Send Notification",
"type": "main",
"index": 0
}
]
]
},
"Format Risk Blocked Message": {
"main": [
[
{
"node": "Telegram - Send Notification",
"type": "main",
"index": 0
}
]
]
},
"Format Invalid Secret": {
"main": [
[
{
"node": "Telegram - Send Notification",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}