- 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
396 lines
12 KiB
JSON
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"
|
|
}
|
|
}
|