This workflow follows the HTTP Request → Telegram recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Master Agent - Orchestrator",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 2
}
]
}
},
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
0,
0
]
},
{
"parameters": {
"jsCode": "// Trading Hours - Chicago Time (CT)\n// Futures Session: 5:00 PM CT to 3:55 PM CT (next day)\n// Flatten: 3:55 PM CT (close all positions - prop firm rule)\n// Maintenance: 4:00 PM - 5:00 PM CT daily\n// Weekend: Sat all day + Sun before 5 PM CT\n\nconst now = new Date();\nconst ct = new Intl.DateTimeFormat('en-US', {\n timeZone: 'America/Chicago',\n hour: 'numeric', minute: 'numeric', hour12: false,\n weekday: 'short'\n}).formatToParts(now);\n\nconst ctH = parseInt(ct.find(p => p.type === 'hour').value);\nconst ctM = parseInt(ct.find(p => p.type === 'minute').value);\nconst ctDay = ct.find(p => p.type === 'weekday').value;\n\n// Weekend: Saturday closed\nif (ctDay === 'Sat') return [];\n// Sunday before 5 PM closed\nif (ctDay === 'Sun' && ctH < 17) return [];\n// Friday after 4 PM = weekend\nif (ctDay === 'Fri' && ctH >= 16) return [];\n\n// Daily maintenance: 4:00 PM - 5:00 PM CT\nif (ctH === 16) return [];\n\n// Bot active check\nconst sd = $getWorkflowStaticData('global');\nif (sd.botActive === false) return [];\n\n// Flatten period: 3:55 PM - 4:00 PM CT\n// CLOSE ALL POSITIONS - prop firm cancels account if holding past session\nconst shouldFlatten = (ctH === 15 && ctM >= 55);\n// No new positions after 3:50 PM CT (5 min buffer)\nconst noNewPositions = (ctH === 15 && ctM >= 50);\n\nreturn [{json: {\n timestamp: now.toISOString(),\n ctHour: ctH,\n ctMinute: ctM,\n ctDay: ctDay,\n shouldFlatten: shouldFlatten,\n noNewPositions: noNewPositions,\n session: ctH >= 17 ? 'evening' : ctH < 9 ? 'overnight' : 'day'\n}}];"
},
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Check Trading Hours",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
220,
0
]
},
{
"parameters": {
"jsCode": "// Token cache logic - reuses token for 85 minutes\nconst staticData = $getWorkflowStaticData('global');\nconst now = Date.now();\n\n// Check if we have a valid cached token\nif (staticData.accessToken && staticData.tokenExpiry && now < staticData.tokenExpiry) {\n return [{\n json: {\n accessToken: staticData.accessToken,\n mdAccessToken: staticData.mdAccessToken || staticData.accessToken,\n cached: true\n }\n }];\n}\n\n// Need new token - return flag\nreturn [{ json: { needsAuth: true } }];"
},
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"name": "Auth Manager",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "cond-needs-auth",
"leftValue": "={{ $json.needsAuth }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals",
"singleValue": true
}
}
],
"combinator": "and"
}
},
"id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"name": "IF Needs Auth",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
660,
0
]
},
{
"parameters": {
"method": "POST",
"url": "https://demo.tradovateapi.com/v1/auth/accesstokenrequest",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ name: $env.TRADOVATE_USER || 'USER', password: $env.TRADOVATE_PASS || 'PASS', appId: 'DayTraderBot', appVersion: '1.0', deviceId: 'n8n-lucid-bot' }) }}"
},
"id": "e5f6a7b8-c9d0-1234-efab-345678901234",
"name": "HTTP Auth Tradovate",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
880,
-100
]
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nconst authResponse = $input.first().json;\n\nstaticData.accessToken = authResponse.accessToken;\nstaticData.mdAccessToken = authResponse.mdAccessToken || authResponse.accessToken;\nstaticData.tokenExpiry = Date.now() + 85 * 60 * 1000; // 85 minutes\n\nreturn [{\n json: {\n accessToken: authResponse.accessToken,\n mdAccessToken: staticData.mdAccessToken,\n cached: false\n }\n}];"
},
"id": "f6a7b8c9-d0e1-2345-fabc-456789012345",
"name": "Cache Token",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1100,
-100
]
},
{
"parameters": {
"mode": "chooseBranch",
"output": "empty"
},
"id": "a7b8c9d0-e1f2-3456-abcd-567890123456",
"name": "Merge Auth",
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [
1320,
0
]
},
{
"parameters": {
"method": "GET",
"url": "https://demo.tradovateapi.com/v1/account/list",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $json.accessToken }}"
}
]
}
},
"id": "b8c9d0e1-f2a3-4567-bcde-678901234567",
"name": "HTTP Get Account",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1540,
-200
]
},
{
"parameters": {
"method": "GET",
"url": "https://demo.tradovateapi.com/v1/position/list",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Merge Auth').first().json.accessToken }}"
}
]
}
},
"id": "c9d0e1f2-a3b4-5678-cdef-789012345678",
"name": "HTTP Get Positions",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1540,
0
]
},
{
"parameters": {
"jsCode": "// Get trading configuration from workflow static data\nconst staticData = $getWorkflowStaticData('global');\n\nconst config = {\n botActive: staticData.botActive !== false, // default true\n maxQtyMNQ: staticData.maxQtyMNQ || 1,\n maxQtyMGC: staticData.maxQtyMGC || 1,\n maxDrawdownMNQ: staticData.maxDrawdownMNQ || 500,\n maxDrawdownMGC: staticData.maxDrawdownMGC || 300,\n activeStrategies: staticData.activeStrategies || ['all'],\n activeAgents: staticData.activeAgents || ['ICT', 'VOLUME', 'TECHNICAL', 'NEWS', 'FIBONACCI', 'CORRELATION', 'SESSION'],\n dailyPnL: staticData.dailyPnL || 0,\n tradesThisDay: staticData.tradesThisDay || 0\n};\n\nif (!config.botActive) {\n return []; // stops execution if bot is paused\n}\n\nreturn [{ json: config }];"
},
"id": "d0e1f2a3-b4c5-6789-defa-890123456789",
"name": "Get Config",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1540,
200
]
},
{
"parameters": {
"jsCode": "// Prepare market data context for sub-workflows\nconst accessToken = $('Merge Auth').first().json.accessToken;\nconst config = $('Get Config').first().json;\n\nreturn [{\n json: {\n symbol_mnq: 'MNQM5',\n symbol_mgc: 'MGCM5',\n accessToken: accessToken,\n config: config\n }\n}];"
},
"id": "e1f2a3b4-c5d6-7890-efab-901234567890",
"name": "Get Market Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1760,
0
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.twelvedata.com/time_series?symbol=NQ&interval=1min&outputsize=100&apikey={{ $env.TWELVE_DATA_KEY || '72309ee97bfe49f29f0f7a92d5337601' }}",
"options": {}
},
"id": "f2a3b4c5-d6e7-8901-fabc-012345678901",
"name": "HTTP MNQ 1m",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1980,
-100
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.twelvedata.com/time_series?symbol=NQ&interval=5min&outputsize=100&apikey={{ $env.TWELVE_DATA_KEY || '72309ee97bfe49f29f0f7a92d5337601' }}",
"options": {}
},
"id": "a3b4c5d6-e7f8-9012-abcd-123456789abc",
"name": "HTTP MNQ 5m",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1980,
100
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.twelvedata.com/time_series?symbol=GC&interval=1min&outputsize=100&apikey={{ $env.TWELVE_DATA_KEY || '72309ee97bfe49f29f0f7a92d5337601' }}",
"options": {}
},
"id": "mgc-1m-node-id-0001",
"name": "HTTP MGC 1m",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1980,
300
]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.twelvedata.com/time_series?symbol=GC&interval=5min&outputsize=100&apikey={{ $env.TWELVE_DATA_KEY || '72309ee97bfe49f29f0f7a92d5337601' }}",
"options": {}
},
"id": "mgc-5m-node-id-0002",
"name": "HTTP MGC 5m",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1980,
500
]
},
{
"parameters": {
"jsCode": "const candles1mRaw = $('HTTP MNQ 1m').first().json;\nconst candles5mRaw = $('HTTP MNQ 5m').first().json;\n\n// MGC data\nlet mgcCandles1mRaw, mgcCandles5mRaw;\ntry { mgcCandles1mRaw = $('HTTP MGC 1m').first().json; } catch(e) { mgcCandles1mRaw = { values: [] }; }\ntry { mgcCandles5mRaw = $('HTTP MGC 5m').first().json; } catch(e) { mgcCandles5mRaw = { values: [] }; }\n\n// Flatten/noNewPositions flags from Check Trading Hours\nconst tradingHoursData = $('Check Trading Hours').first().json;\nconst shouldFlatten = tradingHoursData.shouldFlatten || false;\nconst noNewPositions = tradingHoursData.noNewPositions || false;\n\nconst config = $('Get Config').first().json;\n\n// Parse candles from Twelve Data format\nfunction parseCandles(data) {\n const values = data?.values || data?.body?.values || [];\n return values.reverse().map(v => ({\n time: v.datetime,\n open: parseFloat(v.open),\n high: parseFloat(v.high),\n low: parseFloat(v.low),\n close: parseFloat(v.close),\n volume: parseInt(v.volume || 0)\n }));\n}\n\nconst candles_1m = parseCandles(candles1mRaw);\nconst candles_5m = parseCandles(candles5mRaw);\n\nif (candles_1m.length === 0) {\n return [{ json: { error: 'No candle data available', indicators: {}, candles_1m: [], candles_5m: [] } }];\n}\n\n// === INDICATOR CALCULATIONS ===\n\n// EMA calculation\nfunction calcEMA(data, period) {\n if (data.length < period) return { value: 0, series: [] };\n const k = 2 / (period + 1);\n let ema = data.slice(0, period).reduce((s, v) => s + v, 0) / period;\n const series = [ema];\n for (let i = period; i < data.length; i++) {\n ema = data[i] * k + ema * (1 - k);\n series.push(ema);\n }\n return { value: ema, series };\n}\n\n// RSI\nfunction calcRSI(closes, period) {\n if (closes.length < period + 1) return 50;\n let gains = 0, losses = 0;\n for (let i = 1; i <= period; i++) {\n const diff = closes[i] - closes[i-1];\n if (diff > 0) gains += diff; else losses -= diff;\n }\n let avgGain = gains / period;\n let avgLoss = losses / period;\n for (let i = period + 1; i < closes.length; i++) {\n const diff = closes[i] - closes[i-1];\n avgGain = (avgGain * (period - 1) + (diff > 0 ? diff : 0)) / period;\n avgLoss = (avgLoss * (period - 1) + (diff < 0 ? -diff : 0)) / period;\n }\n if (avgLoss === 0) return 100;\n const rs = avgGain / avgLoss;\n return 100 - 100 / (1 + rs);\n}\n\n// ATR\nfunction calcATR(candles, period) {\n if (candles.length < period + 1) return 0;\n const trs = [];\n for (let i = 1; i < candles.length; i++) {\n const tr = Math.max(\n candles[i].high - candles[i].low,\n Math.abs(candles[i].high - candles[i-1].close),\n Math.abs(candles[i].low - candles[i-1].close)\n );\n trs.push(tr);\n }\n let atr = trs.slice(0, period).reduce((s, v) => s + v, 0) / period;\n for (let i = period; i < trs.length; i++) {\n atr = (atr * (period - 1) + trs[i]) / period;\n }\n return atr;\n}\n\n// Bollinger Bands\nfunction calcBB(closes, period, stdDev) {\n if (closes.length < period) return { upper: 0, middle: 0, lower: 0, width: 0 };\n const slice = closes.slice(-period);\n const mean = slice.reduce((s, v) => s + v, 0) / period;\n const variance = slice.reduce((s, v) => s + (v - mean) ** 2, 0) / period;\n const std = Math.sqrt(variance);\n return {\n upper: mean + stdDev * std,\n middle: mean,\n lower: mean - stdDev * std,\n width: (2 * stdDev * std) / mean\n };\n}\n\n// VWAP\nfunction calcVWAP(candles) {\n if (candles.length === 0) return { value: 0, upper1SD: 0, lower1SD: 0, upper2SD: 0, lower2SD: 0 };\n let cumVol = 0, cumTP = 0, cumTP2 = 0;\n for (const c of candles) {\n const tp = (c.high + c.low + c.close) / 3;\n const vol = c.volume || 1;\n cumVol += vol;\n cumTP += tp * vol;\n cumTP2 += tp * tp * vol;\n }\n const vwap = cumTP / cumVol;\n const variance = cumTP2 / cumVol - vwap * vwap;\n const std = Math.sqrt(Math.max(0, variance));\n return { value: vwap, upper1SD: vwap + std, lower1SD: vwap - std, upper2SD: vwap + 2*std, lower2SD: vwap - 2*std };\n}\n\n// Volume Delta\nfunction calcVolumeDelta(candles) {\n let cumDelta = 0;\n return candles.map(c => {\n const delta = c.close >= c.open ? c.volume : -c.volume;\n cumDelta += delta;\n return {\n time: c.time,\n buyVol: c.close >= c.open ? c.volume : 0,\n sellVol: c.close < c.open ? c.volume : 0,\n delta,\n cumDelta\n };\n });\n}\n\n// Swing Highs/Lows\nfunction findSwings(candles, lookback) {\n const highs = [], lows = [];\n for (let i = lookback; i < candles.length - lookback; i++) {\n let isHigh = true, isLow = true;\n for (let j = 1; j <= lookback; j++) {\n if (candles[i].high <= candles[i-j].high || candles[i].high <= candles[i+j].high) isHigh = false;\n if (candles[i].low >= candles[i-j].low || candles[i].low >= candles[i+j].low) isLow = false;\n }\n if (isHigh) highs.push({ price: candles[i].high, index: i, time: candles[i].time });\n if (isLow) lows.push({ price: candles[i].low, index: i, time: candles[i].time });\n }\n return { swingHighs: highs, swingLows: lows };\n}\n\n// Fair Value Gaps\nfunction findFVGs(candles, tf) {\n const fvgs = [];\n for (let i = 2; i < candles.length; i++) {\n if (candles[i].low > candles[i-2].high) {\n fvgs.push({\n type: 'BULLISH',\n high: candles[i].low,\n low: candles[i-2].high,\n timeframe: tf,\n filled: candles_1m.length > 0 && candles_1m[candles_1m.length-1].low <= candles[i-2].high,\n time: candles[i].time\n });\n }\n if (candles[i].high < candles[i-2].low) {\n fvgs.push({\n type: 'BEARISH',\n high: candles[i-2].low,\n low: candles[i].high,\n timeframe: tf,\n filled: candles_1m.length > 0 && candles_1m[candles_1m.length-1].high >= candles[i-2].low,\n time: candles[i].time\n });\n }\n }\n return fvgs;\n}\n\n// Order Blocks\nfunction findOrderBlocks(candles, tf) {\n const obs = [];\n for (let i = 1; i < candles.length - 1; i++) {\n const curr = candles[i];\n const next = candles[i+1];\n const currBearish = curr.close < curr.open;\n const currBullish = curr.close > curr.open;\n const nextBigMove = Math.abs(next.close - next.open) > Math.abs(curr.close - curr.open) * 1.5;\n \n if (currBearish && next.close > next.open && nextBigMove) {\n obs.push({ type: 'BULLISH', high: curr.open, low: curr.close, timeframe: tf, tested: false, time: curr.time });\n }\n if (currBullish && next.close < next.open && nextBigMove) {\n obs.push({ type: 'BEARISH', high: curr.close, low: curr.open, timeframe: tf, tested: false, time: curr.time });\n }\n }\n return obs;\n}\n\n// Market Structure (HH/HL/LH/LL)\nfunction detectMarketStructure(swings) {\n const { swingHighs, swingLows } = swings;\n if (swingHighs.length < 2 || swingLows.length < 2) return \"NEUTRAL\";\n const lastH = swingHighs.slice(-2);\n const lastL = swingLows.slice(-2);\n const hh = lastH[1].price > lastH[0].price;\n const hl = lastL[1].price > lastL[0].price;\n const lh = lastH[1].price < lastH[0].price;\n const ll = lastL[1].price < lastL[0].price;\n if (hh && hl) return \"BULLISH\";\n if (lh && ll) return \"BEARISH\";\n return \"NEUTRAL\";\n}\n\n// SFP Detection\nfunction detectSFP(candles, swings) {\n const sfps = [];\n const { swingHighs, swingLows } = swings;\n const last5 = candles.slice(-5);\n \n for (const c of last5) {\n for (const sl of swingLows) {\n if (c.low < sl.price && c.close > sl.price) {\n sfps.push({ type: 'BULLISH', price: sl.price, candle_time: c.time, timeframe: '5m' });\n }\n }\n for (const sh of swingHighs) {\n if (c.high > sh.price && c.close < sh.price) {\n sfps.push({ type: 'BEARISH', price: sh.price, candle_time: c.time, timeframe: '5m' });\n }\n }\n }\n return sfps;\n}\n\n// Equal Highs/Lows\nfunction detectEqualHL(swings, tolerance) {\n const result = [];\n const { swingHighs, swingLows } = swings;\n \n for (let i = 0; i < swingHighs.length; i++) {\n let count = 1;\n for (let j = i + 1; j < swingHighs.length; j++) {\n if (Math.abs(swingHighs[i].price - swingHighs[j].price) <= tolerance) count++;\n }\n if (count >= 2) result.push({ type: 'EQUAL_HIGHS', price: swingHighs[i].price, count });\n }\n for (let i = 0; i < swingLows.length; i++) {\n let count = 1;\n for (let j = i + 1; j < swingLows.length; j++) {\n if (Math.abs(swingLows[i].price - swingLows[j].price) <= tolerance) count++;\n }\n if (count >= 2) result.push({ type: 'EQUAL_LOWS', price: swingLows[i].price, count });\n }\n return result;\n}\n\n// Opening Range\nfunction calcOpeningRange(candles1m) {\n const orCandles = candles1m.filter(c => {\n const time = new Date(c.time);\n const etH = time.getUTCHours() - 5;\n const etM = time.getUTCMinutes();\n return etH === 9 && etM >= 30 && etM < 45;\n });\n if (orCandles.length === 0) return { high: 0, low: 0 };\n return {\n high: Math.max(...orCandles.map(c => c.high)),\n low: Math.min(...orCandles.map(c => c.low))\n };\n}\n\n// === CALCULATE EVERYTHING ===\nconst closes1m = candles_1m.map(c => c.close);\nconst closes5m = candles_5m.map(c => c.close);\n\nconst ema9_1m = calcEMA(closes1m, 9);\nconst ema21_1m = calcEMA(closes1m, 21);\nconst ema50_1m = calcEMA(closes1m, 50);\nconst ema9_5m = calcEMA(closes5m, 9);\nconst ema21_5m = calcEMA(closes5m, 21);\n\nconst rsi_1m = calcRSI(closes1m, 14);\nconst atr_1m = calcATR(candles_1m, 14);\nconst atr_5m = calcATR(candles_5m, 14);\nconst bb_1m = calcBB(closes1m, 20, 2);\nconst vwap = calcVWAP(candles_1m);\nconst volumeDelta = calcVolumeDelta(candles_1m);\nconst avgVolume = candles_1m.length > 0 ? candles_1m.reduce((s, c) => s + c.volume, 0) / candles_1m.length : 0;\n\nconst swings5m = findSwings(candles_5m, 3);\nconst swings1m = findSwings(candles_1m, 5);\nconst fvgs5m = findFVGs(candles_5m, '5m');\nconst fvgs1m = findFVGs(candles_1m, '1m');\nconst orderBlocks5m = findOrderBlocks(candles_5m, '5m');\nconst marketStructure5m = detectMarketStructure(swings5m);\nconst sfps = detectSFP(candles_5m, swings5m);\nconst equalHL = detectEqualHL(swings5m, atr_5m * 0.2);\nconst openingRange = calcOpeningRange(candles_1m);\n\nconst supports = swings1m.swingLows.slice(-5).map(s => ({ price: s.price, time: s.time }));\nconst resistances = swings1m.swingHighs.slice(-5).map(s => ({ price: s.price, time: s.time }));\n\nconst indicators = {\n ema9: ema9_1m.value,\n ema21: ema21_1m.value,\n ema50: ema50_1m.value,\n ema9_series: ema9_1m.series.slice(-10),\n ema21_series: ema21_1m.series.slice(-10),\n ema9_5m: ema9_5m.value,\n ema21_5m: ema21_5m.value,\n rsi: rsi_1m,\n atr: atr_1m,\n atr5m: atr_5m,\n atrAvg: atr_1m,\n bollingerBands: bb_1m,\n vwap: vwap,\n volumeDelta: volumeDelta.slice(-20),\n cumulativeDelta: volumeDelta.length > 0 ? volumeDelta[volumeDelta.length-1].cumDelta : 0,\n avgVolume: avgVolume,\n supports: supports,\n resistances: resistances,\n fairValueGaps: [...fvgs5m, ...fvgs1m].slice(-15),\n orderBlocks: orderBlocks5m.slice(-5),\n liquidityLevels: equalHL,\n marketStructure: { '5m': marketStructure5m },\n swingFailurePatterns: sfps,\n equalHighsLows: equalHL,\n openingRange: openingRange\n};\n\nconst symbol = $('Get Market Data').first().json.symbol_mnq;\n\n// Parse MGC candles\nfunction parseCandlesMGC(data) {\n const values = data?.values || data?.body?.values || [];\n return values.reverse().map(v => ({\n time: v.datetime,\n open: parseFloat(v.open),\n high: parseFloat(v.high),\n low: parseFloat(v.low),\n close: parseFloat(v.close),\n volume: parseInt(v.volume || 0)\n }));\n}\n\nconst mgc_candles_1m = parseCandlesMGC(mgcCandles1mRaw);\nconst mgc_candles_5m = parseCandlesMGC(mgcCandles5mRaw);\n\nreturn [{\n json: {\n indicators,\n candles_1m,\n candles_5m,\n candles_daily: [],\n candles_4h: [],\n mgc_candles_1m,\n mgc_candles_5m,\n positions: $('HTTP Get Positions').first().json || [],\n config: $('Get Config').first().json,\n symbol,\n symbol_mgc: $('Get Market Data').first().json.symbol_mgc,\n shouldFlatten,\n noNewPositions,\n accessToken: $('Merge Auth').first().json.accessToken,\n accountId: ($('HTTP Get Account').first().json || [])[0]?.id || 0,\n accountSpec: ($('HTTP Get Account').first().json || [])[0]?.name || ''\n }\n}];"
},
"id": "b4c5d6e7-f8a9-0123-abcd-234567890abc",
"name": "Calculate Indicators",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2200,
0
]
},
{
"parameters": {
"source": "parameter",
"workflowId": "={{ $env.SESSION_AGENT_WORKFLOW_ID || 'BK6ybQX7wBErUfzo' }}",
"mode": "each",
"options": {}
},
"id": "c5d6e7f8-a9b0-1234-bcde-345678901bcd",
"name": "Layer 1: Session Agent",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
2420,
-200
]
},
{
"parameters": {
"source": "parameter",
"workflowId": "={{ $env.CORRELATION_AGENT_WORKFLOW_ID || 'j2U5LCMVRG1pYQYW' }}",
"mode": "each",
"options": {}
},
"id": "d6e7f8a9-b0c1-2345-cdef-456789012cde",
"name": "Layer 1: Correlation Agent",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
2420,
0
]
},
{
"parameters": {
"source": "parameter",
"workflowId": "={{ $env.NEWS_AGENT_WORKFLOW_ID || 'aiDx8Q4nDoAMqaqb' }}",
"mode": "each",
"options": {}
},
"id": "e7f8a9b0-c1d2-3456-defa-567890123def",
"name": "Layer 1: News Agent",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
2420,
200
]
},
{
"parameters": {
"jsCode": "// Check Layer 1 results - should we proceed to Layer 2?\nconst sessionSignal = $('Layer 1: Session Agent').first().json;\nconst correlationSignal = $('Layer 1: Correlation Agent').first().json;\nconst newsSignal = $('Layer 1: News Agent').first().json;\n\nconst session = sessionSignal.session || {};\n\n// GATE CONDITIONS - stop if any of these:\n// 1. Bot outside trading hours for this asset\nif (!session.shouldTrade) {\n return []; // stop\n}\n\n// 2. News veto (high impact event nearby)\nif (newsSignal.urgency === 'HIGH' && newsSignal.signal === 'HOLD') {\n return [{\n json: {\n proceed: false,\n reason: 'NEWS_VETO',\n newsSignal,\n sessionSignal,\n correlationSignal,\n layer1: [sessionSignal, correlationSignal, newsSignal]\n }\n }];\n}\n\n// 3. Lunch hour\nif (session.current === 'LUNCH') {\n return []; // stop\n}\n\n// 4. Flatten warning\nif (session.flattenWarning) {\n return [{\n json: {\n proceed: false,\n reason: 'FLATTEN_WARNING',\n flattenMinutes: session.flattenMinutes,\n layer1: [sessionSignal, correlationSignal, newsSignal]\n }\n }];\n}\n\n// All clear - proceed to Layer 2\nreturn [{\n json: {\n proceed: true,\n recommendedStrategies: session.recommendedStrategies || [],\n avoidStrategies: session.avoidStrategies || [],\n killZoneActive: session.killZoneActive || false,\n killZone: session.killZone || 'NONE',\n macroBias: correlationSignal.macroBias || 'NEUTRAL',\n vixRegime: correlationSignal.vixRegime || 'NORMAL',\n layer1: [sessionSignal, correlationSignal, newsSignal]\n }\n}];"
},
"id": "f8a9b0c1-d2e3-4567-efab-678901234efa",
"name": "Layer 1 Gate",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2640,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "cond-proceed",
"leftValue": "={{ $json.proceed }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals",
"singleValue": true
}
}
],
"combinator": "and"
}
},
"id": "a9b0c1d2-e3f4-5678-abcd-789012345abc",
"name": "IF Proceed to Layer 2",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
2860,
0
]
},
{
"parameters": {
"source": "parameter",
"workflowId": "={{ $env.ICT_AGENT_WORKFLOW_ID || 'Km5Wty5vyEawreXQ' }}",
"mode": "each",
"options": {}
},
"id": "b0c1d2e3-f4a5-6789-bcde-890123456bcd",
"name": "Layer 2: ICT Agent",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
3080,
-300
]
},
{
"parameters": {
"source": "parameter",
"workflowId": "={{ $env.VOLUME_AGENT_WORKFLOW_ID || 'yhE0Mnn94e8XFHHT' }}",
"mode": "each",
"options": {}
},
"id": "c1d2e3f4-a5b6-7890-cdef-901234567cde",
"name": "Layer 2: Volume Agent",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
3080,
-100
]
},
{
"parameters": {
"source": "parameter",
"workflowId": "={{ $env.TECHNICAL_AGENT_WORKFLOW_ID || 'QwVYR6gWpN6p14mc' }}",
"mode": "each",
"options": {}
},
"id": "d2e3f4a5-b6c7-8901-defa-012345678def",
"name": "Layer 2: Technical Agent",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
3080,
100
]
},
{
"parameters": {
"source": "parameter",
"workflowId": "={{ $env.FIBONACCI_AGENT_WORKFLOW_ID || 'Ej5F6JsfGwrruKOd' }}",
"mode": "each",
"options": {}
},
"id": "e3f4a5b6-c7d8-9012-efab-123456789efa",
"name": "Layer 2: Fibonacci Agent",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
3080,
300
]
},
{
"parameters": {
"jsCode": "// Merge Layer 1 + Layer 2 signals\nconst layer1 = $('Layer 1 Gate').first().json.layer1 || [];\nconst ictSignal = $('Layer 2: ICT Agent').first().json;\nconst volumeSignal = $('Layer 2: Volume Agent').first().json;\nconst technicalSignal = $('Layer 2: Technical Agent').first().json;\nconst fibSignal = $('Layer 2: Fibonacci Agent').first().json;\n\nconst allSignals = [...layer1, ictSignal, volumeSignal, technicalSignal, fibSignal];\nconst layer1Data = $('Layer 1 Gate').first().json;\n\n// Summary for Master Agent\nconst signalSummary = allSignals.map(s => ({\n agent: s.agent,\n signal: s.signal,\n confidence: s.confidence,\n strategy: s.strategy,\n reasoning: s.reasoning,\n entry: s.entry,\n stopLoss: s.stopLoss,\n takeProfit: s.takeProfit,\n riskReward: s.riskReward,\n urgency: s.urgency,\n killZone: s.killZone\n}));\n\n// Count agreements\nconst buySignals = allSignals.filter(s => s.signal === 'BUY' && s.confidence > 0.5);\nconst sellSignals = allSignals.filter(s => s.signal === 'SELL' && s.confidence > 0.5);\n\nreturn [{\n json: {\n signals: signalSummary,\n buyCount: buySignals.length,\n sellCount: sellSignals.length,\n holdCount: allSignals.filter(s => s.signal === 'HOLD').length,\n recommendedStrategies: layer1Data.recommendedStrategies,\n avoidStrategies: layer1Data.avoidStrategies,\n killZoneActive: layer1Data.killZoneActive,\n macroBias: layer1Data.macroBias,\n vixRegime: layer1Data.vixRegime,\n symbol: $('Calculate Indicators').first().json.symbol,\n accountId: $('Calculate Indicators').first().json.accountId,\n accountSpec: $('Calculate Indicators').first().json.accountSpec,\n accessToken: $('Calculate Indicators').first().json.accessToken,\n config: $('Calculate Indicators').first().json.config\n }\n}];"
},
"id": "f4a5b6c7-d8e9-0123-fabc-234567890fab",
"name": "Merge All Signals",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3300,
0
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.anthropic.com/v1/messages",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "={{ $env.ANTHROPIC_API_KEY || '' }}"
},
{
"name": "anthropic-version",
"value": "2023-06-01"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ model: 'claude-sonnet-4-20250514', max_tokens: 1024, system: 'Voce e o MASTER TRADER, um orquestrador que recebe sinais de 7 agentes especialistas e decide a acao final.\\n\\nARQUITETURA DE 3 CAMADAS:\\n- Camada 1 (Filtros): Session, Correlation, News - fornecem contexto\\n- Camada 2 (Analistas): ICT, Volume, Technical, Fibonacci - geram sinais de trade\\n- Voce e a Camada 3 (Decisor): pondera tudo e decide\\n\\nREGRAS DE DECISAO:\\n1. Se 3+ analistas (Layer 2) concordam na direcao -> alta confianca, executar se R:R >= 2\\n2. Se 2 analistas concordam com confianca > 0.75 -> executar se R:R >= 2.5\\n3. Se apenas 1 analista com confianca > 0.90 -> executar se R:R >= 3\\n4. Se analistas conflitam (BUY vs SELL) -> HOLD\\n5. Se News Agent sinalizou HIGH urgency -> HOLD absoluto\\n6. Se Correlation/macro bias contradiz os sinais -> reduzir confianca em 20%\\n7. Se Kill Zone ativa -> aumentar peso do ICT Agent em 20%\\n8. Se VIX regime e HIGH_FEAR -> favorecer sinais SHORT para MNQ, LONG para MGC\\n\\nPESOS DOS ANALISTAS:\\n- ICT Agent: 30% (35% em Kill Zone)\\n- Volume Agent: 25%\\n- Technical Agent: 25%\\n- Fibonacci Agent: 20%\\n\\nUSE SL/TP DO ANALISTA COM MAIOR CONFIANCA quando houver consenso.\\n\\nConsidere tambem:\\n- recommendedStrategies e avoidStrategies do Session Agent\\n- macroBias do Correlation Agent\\n\\nRESPONDA SOMENTE com JSON:\\n{\\n \"action\": \"BUY | SELL | HOLD\",\\n \"symbol\": \"<symbol>\",\\n \"qty\": <number>,\\n \"entry\": <price>,\\n \"stopLoss\": <price>,\\n \"takeProfit\": <price>,\\n \"agentsAgreed\": [\"ICT\", \"VOLUME\"],\\n \"masterConfidence\": 0.82,\\n \"reasoning\": \"<explanation of decision>\",\\n \"riskPerTrade\": <dollar amount>,\\n \"strategy\": \"<primary strategy used>\"\\n}', messages: [{ role: 'user', content: JSON.stringify($json) }] }) }}"
},
"id": "a5b6c7d8-e9f0-1234-abcd-345678901abc",
"name": "Master Claude Decision",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3520,
0
]
},
{
"parameters": {
"jsCode": "const response = $input.first().json;\nconst content = response.content?.[0]?.text || response.body?.content?.[0]?.text || '{}';\n\nlet decision;\ntry {\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n decision = jsonMatch ? JSON.parse(jsonMatch[0]) : JSON.parse(content);\n} catch (e) {\n decision = { action: \"HOLD\", reasoning: \"Parse error: \" + e.message };\n}\n\ndecision.action = [\"BUY\", \"SELL\", \"HOLD\"].includes(decision.action) ? decision.action : \"HOLD\";\ndecision.symbol = decision.symbol || $('Merge All Signals').first().json.symbol;\ndecision.accountId = $('Merge All Signals').first().json.accountId;\ndecision.accountSpec = $('Merge All Signals').first().json.accountSpec;\ndecision.accessToken = $('Merge All Signals').first().json.accessToken;\ndecision.config = $('Merge All Signals').first().json.config;\n\n// Check flatten/noNewPositions flags from Calculate Indicators\nconst shouldFlatten = $('Calculate Indicators').first().json.shouldFlatten;\nconst noNewPositions = $('Calculate Indicators').first().json.noNewPositions;\n\n// If shouldFlatten - override to close all positions\nif (shouldFlatten) {\n decision.action = 'FLATTEN';\n decision.reasoning = 'FLATTEN: 3:55 PM CT - closing all positions before session end (prop firm rule)';\n}\n// If noNewPositions - block new trades\nelse if (noNewPositions && decision.action !== 'HOLD') {\n decision.action = 'HOLD';\n decision.reasoning = 'BLOCKED: No new positions after 3:50 PM CT - too close to session end';\n}\n\ndecision.shouldFlatten = shouldFlatten;\ndecision.noNewPositions = noNewPositions;\n\nreturn [{ json: decision }];"
},
"id": "b6c7d8e9-f0a1-2345-bcde-456789012bcd",
"name": "Parse Master Decision",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3740,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "cond-execute",
"leftValue": "={{ $json.action === 'BUY' || $json.action === 'SELL' || $json.action === 'FLATTEN' }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals",
"singleValue": true
}
}
],
"combinator": "and"
}
},
"id": "c7d8e9f0-a1b2-3456-cdef-567890123cde",
"name": "IF Execute Trade",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
3960,
0
]
},
{
"parameters": {
"source": "database",
"workflowId": "={{ $env.RISK_MASTER_WORKFLOW_ID || 'ZDFUioPIsSM4eZ5D' }}",
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"masterDecision": "={{ $json }}",
"accountInfo": "={{ $('HTTP Get Account').first().json }}",
"positions": "={{ $('HTTP Get Positions').first().json }}",
"todayPnL": "={{ $('Risk Manager')?.first()?.json?.totalPnL || 0 }}",
"todayTrades": "={{ $('Risk Manager')?.first()?.json?.tradesThisDay || 0 }}",
"config": "={{ $('Get Config').first().json }}",
"tradeHistory": "={{ $getWorkflowStaticData('global').tradeHistory || [] }}"
}
}
},
"id": "rm-a1b2c3d4-risk-master-node",
"name": "Risk Master",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.1,
"position": [
4180,
-100
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "cond-risk-approved",
"leftValue": "={{ $json.approved }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals",
"singleValue": true
}
}
],
"combinator": "and"
}
},
"id": "rm-b2c3d4e5-if-risk-approved",
"name": "IF Risk Approved",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
4400,
-100
]
},
{
"parameters": {
"operation": "sendMessage",
"chatId": "={{ $env.TELEGRAM_CHAT_ID || '' }}",
"text": "=\u26d4 *TRADE REJEITADO pelo Risk Master*\n\nSinal: {{ $('Parse Master Decision').first().json.action }} {{ $('Parse Master Decision').first().json.symbol }}\nMotivo: {{ $json.reason }}\n\n\ud83d\udcca Risk Report:\n- Drawdown: {{ $json.riskReport.drawdownUsed }}\n- Trades hoje: {{ $json.riskReport.tradesToday }}\n- Posi\u00e7\u00f5es abertas: {{ $json.riskReport.openPositions }}\n- Losses consecutivos: {{ $json.riskReport.consecutiveLosses }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "rm-c3d4e5f6-telegram-rejected",
"name": "Telegram Risk Rejected",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
4620,
100
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"id": "d8e9f0a1-b2c3-4567-defa-678901234def",
"name": "Place Bracket Order",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
4620,
-100
]
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nconst decision = $('Parse Master Decision').first().json;\nconst positions = $('HTTP Get Positions').first().json || [];\n\n// Track daily P&L\nconst today = new Date().toISOString().split('T')[0];\nif (staticData.lastTradeDay !== today) {\n staticData.dailyPnL = 0;\n staticData.tradesThisDay = 0;\n staticData.lastTradeDay = today;\n}\n\n// Calculate current P&L from positions\nlet currentPnL = 0;\nfor (const pos of positions) {\n currentPnL += pos.pnl || pos.openPL || 0;\n}\n\nconst totalPnL = (staticData.dailyPnL || 0) + currentPnL;\nconst maxDrawdown = decision.config?.maxDrawdownMNQ || 500;\n\nlet alert = null;\nlet closeAll = false;\n\nif (totalPnL < -maxDrawdown) {\n alert = `DRAWDOWN LIMIT HIT: $${totalPnL.toFixed(2)} exceeds max $${maxDrawdown}. Closing all positions.`;\n closeAll = true;\n staticData.botActive = false;\n} else if (totalPnL < -maxDrawdown * 0.7) {\n alert = `WARNING: P&L at $${totalPnL.toFixed(2)}, approaching max drawdown of $${maxDrawdown}`;\n}\n\nif (decision.action !== 'HOLD') {\n staticData.tradesThisDay = (staticData.tradesThisDay || 0) + 1;\n}\n\nreturn [{\n json: {\n totalPnL,\n maxDrawdown,\n alert,\n closeAll,\n tradesThisDay: staticData.tradesThisDay,\n decision\n }\n}];"
},
"id": "e9f0a1b2-c3d4-5678-efab-789012345efa",
"name": "Risk Manager",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
4400,
-100
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "cond-closeall",
"leftValue": "={{ $json.closeAll }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals",
"singleValue": true
}
}
],
"combinator": "and"
}
},
"id": "f0a1b2c3-d4e5-6789-fabc-890123456fab",
"name": "IF Close All",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
4620,
-100
]
},
{
"parameters": {
"method": "POST",
"url": "https://demo.tradovateapi.com/v1/order/liquidateposition",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Parse Master Decision').first().json.accessToken }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ accountId: $('Parse Master Decision').first().json.accountId }) }}"
},
"id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"name": "Close All Positions",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
4840,
-200
]
},
{
"parameters": {
"operation": "sendMessage",
"chatId": "={{ $env.TELEGRAM_CHAT_ID || '' }}",
"text": "=\ud83d\udea8 *DRAWDOWN ALERT*\n\n{{ $('Risk Manager').first().json.alert }}\n\nAll positions liquidated. Bot paused.\nUse /start to reactivate.",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "b2c3d4e5-f6a7-8901-2345-678901abcdef",
"name": "Telegram Alert Drawdown",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
5060,
-200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "sendMessage",
"chatId": "={{ $env.TELEGRAM_CHAT_ID || '' }}",
"text": "=\ud83e\udd16 *TRADE EXECUTED*\n\nSymbol: {{ $('Parse Master Decision').first().json.symbol }}\nAction: {{ $('Parse Master Decision').first().json.action }}\nQty: {{ $('Parse Master Decision').first().json.qty }}\nEntry: {{ $('Parse Master Decision').first().json.entry }}\nSL: {{ $('Parse Master Decision').first().json.stopLoss }}\nTP: {{ $('Parse Master Decision').first().json.takeProfit }}\nConfidence: {{ $('Parse Master Decision').first().json.masterConfidence }}\n\nAgents: {{ $('Parse Master Decision').first().json.agentsAgreed?.join(', ') }}\nStrategy: {{ $('Parse Master Decision').first().json.strategy }}\n\nReasoning: {{ $('Parse Master Decision').first().json.reasoning }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "c3d4e5f6-a7b8-9012-3456-789012abcdef",
"name": "Telegram Notify",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
4180,
100
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"updates": [
"message"
]
},
"id": "d4e5f6a7-b8c9-0123-4567-890123abcdef",
"name": "Telegram Commands",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
0,
600
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rules": {
"rules": [
{
"value": "/status",
"outputKey": "status",
"operation": "startsWith"
},
{
"value": "/config",
"outputKey": "config",
"operation": "startsWith"
},
{
"value": "/stop",
"outputKey": "stop",
"operation": "startsWith"
},
{
"value": "/start",
"outputKey": "start",
"operation": "startsWith"
},
{
"value": "/agents",
"outputKey": "agents",
"operation": "startsWith"
}
]
},
"dataPropertyName": "={{ $json.message?.text || '' }}"
},
"id": "e5f6a7b8-c9d0-1234-5678-901234abcdef",
"name": "Switch Commands",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
220,
600
]
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nconst response = `\\ud83d\\udcca *BOT STATUS*\\n\\nActive: ${staticData.botActive !== false ? '\\u2705' : '\\u274c'}\\nDaily P&L: $${(staticData.dailyPnL || 0).toFixed(2)}\\nTrades Today: ${staticData.tradesThisDay || 0}\\nMax Drawdown: $${staticData.maxDrawdownMNQ || 500}\\n\\nMNQ Qty: ${staticData.maxQtyMNQ || 1}\\nMGC Qty: ${staticData.maxQtyMGC || 1}`;\nreturn [{ json: { text: response } }];"
},
"id": "f6a7b8c9-d0e1-2345-6789-012345abcdef",
"name": "Handle Status",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
400
]
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nconst msg = $input.first().json.message?.text || '';\nconst parts = msg.split(' ');\n// /config MNQ 2 or /config MGC 1\nif (parts.length >= 3) {\n const asset = parts[1].toUpperCase();\n const qty = parseInt(parts[2]);\n if (asset === 'MNQ') staticData.maxQtyMNQ = qty;\n else if (asset === 'MGC') staticData.maxQtyMGC = qty;\n return [{ json: { text: `\\u2705 ${asset} qty set to ${qty}` } }];\n}\nreturn [{ json: { text: '\\u274c Usage: /config MNQ 2 or /config MGC 1' } }];"
},
"id": "a7b8c9d0-e1f2-3456-7890-123456abcdef",
"name": "Handle Config",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
550
]
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nstaticData.botActive = false;\nreturn [{ json: { text: '\\ud83d\\uded1 Bot STOPPED. Use /start to reactivate.' } }];"
},
"id": "b8c9d0e1-f2a3-4567-8901-234567abcdef",
"name": "Handle Stop",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
700
]
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nstaticData.botActive = true;\nreturn [{ json: { text: '\\u2705 Bot STARTED. Trading active.' } }];"
},
"id": "c9d0e1f2-a3b4-5678-9012-345678abcdef",
"name": "Handle Start",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
850
]
},
{
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('global');\nconst agents = staticData.activeAgents || ['ICT', 'VOLUME', 'TECHNICAL', 'NEWS', 'FIBONACCI', 'CORRELATION', 'SESSION'];\nconst msg = $input.first().json.message?.text || '';\nconst parts = msg.split(' ');\nif (parts.length >= 3) {\n const agent = parts[1].toUpperCase();\n const action = parts[2].toLowerCase();\n if (action === 'off') {\n staticData.activeAgents = agents.filter(a => a !== agent);\n return [{ json: { text: `\\u274c ${agent} agent DISABLED` } }];\n } else if (action === 'on') {\n if (!agents.includes(agent)) agents.push(agent);\n staticData.activeAgents = agents;\n return [{ json: { text: `\\u2705 ${agent} agent ENABLED` } }];\n }\n}\nreturn [{ json: { text: `\\ud83e\\udd16 *AGENTS*\\n\\n${agents.map(a => `\\u2022 ${a}: \\u2705`).join('\\n')}` } }];"
},
"id": "d0e1f2a3-b4c5-6789-0123-456789abcdef",
"name": "Handle Agents",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
1000
]
},
{
"parameters": {
"operation": "sendMessage",
"chatId": "={{ $input.first().json.message?.chat?.id || $env.TELEGRAM_CHAT_ID || '' }}",
"text": "={{ $json.text }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "e1f2a3b4-c5d6-7890-1234-567890abcdef",
"name": "Telegram Reply",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
660,
700
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Check Trading Hours",
"type": "main",
"index": 0
}
]
]
},
"Check Trading Hours": {
"main": [
[
{
"node": "Auth Manager",
"type": "main",
"index": 0
}
]
]
},
"Auth Manager": {
"main": [
[
{
"node": "IF Needs Auth",
"type": "main",
"index": 0
}
]
]
},
"IF Needs Auth": {
"main": [
[
{
"node": "HTTP Auth Tradovate",
"type": "main",
"index": 0
}
],
[
{
"node": "Merge Auth",
"type": "main",
"index": 1
}
]
]
},
"HTTP Auth Tradovate": {
"main": [
[
{
"node": "Cache Token",
"type": "main",
"index": 0
}
]
]
},
"Cache Token": {
"main": [
[
{
"node": "Merge Auth",
"type": "main",
"index": 0
}
]
]
},
"Merge Auth": {
"main": [
[
{
"node": "HTTP Get Account",
"type": "main",
"index": 0
},
{
"node": "HTTP Get Positions",
"type": "main",
"index": 0
},
{
"node": "Get Config",
"type": "main",
"index": 0
}
]
]
},
"HTTP Get Account": {
"main": [
[
{
"node": "Get Market Data",
"type": "main",
"index": 0
}
]
]
},
"HTTP Get Positions": {
"main": [
[
{
"node": "Get Market Data",
"type": "main",
"index": 0
}
]
]
},
"Get Config": {
"main": [
[
{
"node": "Get Market Data",
"type": "main",
"index": 0
}
]
]
},
"Get Market Data": {
"main": [
[
{
"node": "HTTP MNQ 1m",
"type": "main",
"index": 0
},
{
"node": "HTTP MNQ 5m",
"type": "main",
"index": 0
},
{
"node": "HTTP MGC 1m",
"type": "main",
"index": 0
},
{
"node": "HTTP MGC 5m",
"type": "main",
"index": 0
}
]
]
},
"HTTP MGC 1m": {
"main": [
[
{
"node": "Calculate Indicators",
"type": "main",
"index": 0
}
]
]
},
"HTTP MGC 5m": {
"main": [
[
{
"node": "Calculate Indicators",
"type": "main",
"index": 0
}
]
]
},
"HTTP MNQ 1m": {
"main": [
[
{
"node": "Calculate Indicators",
"type": "main",
"index": 0
}
]
]
},
"HTTP MNQ 5m": {
"main": [
[
{
"node": "Calculate Indicators",
"type": "main",
"index": 0
}
]
]
},
"Calculate Indicators": {
"main": [
[
{
"node": "Layer 1: Session Agent",
"type": "main",
"index": 0
},
{
"node": "Layer 1: Correlation Agent",
"type": "main",
"index": 0
},
{
"node": "Layer 1: News Agent",
"type": "main",
"index": 0
}
]
]
},
"Layer 1: Session Agent": {
"main": [
[
{
"node": "Layer 1 Gate",
"type": "main",
"index": 0
}
]
]
},
"Layer 1: Correlation Agent": {
"main": [
[
{
"node": "Layer 1 Gate",
"type": "main",
"index": 0
}
]
]
},
"Layer 1: News Agent": {
"main": [
[
{
"node": "Layer 1 Gate",
"type": "main",
"index": 0
}
]
]
},
"Layer 1 Gate": {
"main": [
[
{
"node": "IF Proceed to Layer 2",
"type": "main",
"index": 0
}
]
]
},
"IF Proceed to Layer 2": {
"main": [
[
{
"node": "Layer 2: ICT Agent",
"type": "main",
"index": 0
},
{
"node": "Layer 2: Volume Agent",
"type": "main",
"index": 0
},
{
"node": "Layer 2: Technical Agent",
"type": "main",
"index": 0
},
{
"node": "Layer 2: Fibonacci Agent",
"type": "main",
"index": 0
}
],
[]
]
},
"Layer 2: ICT Agent": {
"main": [
[
{
"node": "Merge All Signals",
"type": "main",
"index": 0
}
]
]
},
"Layer 2: Volume Agent": {
"main": [
[
{
"node": "Merge All Signals",
"type": "main",
"index": 0
}
]
]
},
"Layer 2: Technical Agent": {
"main": [
[
{
"node": "Merge All Signals",
"type": "main",
"index": 0
}
]
]
},
"Layer 2: Fibonacci Agent": {
"main": [
[
{
"node": "Merge All Signals",
"type": "main",
"index": 0
}
]
]
},
"Merge All Signals": {
"main": [
[
{
"node": "Master Claude Decision",
"type": "main",
"index": 0
}
]
]
},
"Master Claude Decision": {
"main": [
[
{
"node": "Parse Master Decision",
"type": "main",
"index": 0
}
]
]
},
"Parse Master Decision": {
"main": [
[
{
"node": "IF Execute Trade",
"type": "main",
"index": 0
}
]
]
},
"IF Execute Trade": {
"main": [
[
{
"node": "Risk Master",
"type": "main",
"index": 0
}
],
[]
]
},
"Risk Master": {
"main": [
[
{
"node": "IF Risk Approved",
"type": "main",
"index": 0
}
]
]
},
"IF Risk Approved": {
"main": [
[
{
"node": "Place Bracket Order",
"type": "main",
"index": 0
},
{
"node": "Telegram Notify",
"type": "main",
"index": 0
}
],
[
{
"node": "Telegram Risk Rejected",
"type": "main",
"index": 0
}
]
]
},
"Place Bracket Order": {
"main": [
[
{
"node": "Risk Manager",
"type": "main",
"index": 0
}
]
]
},
"Risk Manager": {
"main": [
[
{
"node": "IF Close All",
"type": "main",
"index": 0
}
]
]
},
"IF Close All": {
"main": [
[
{
"node": "Close All Positions",
"type": "main",
"index": 0
}
],
[]
]
},
"Close All Positions": {
"main": [
[
{
"node": "Telegram Alert Drawdown",
"type": "main",
"index": 0
}
]
]
},
"Telegram Commands": {
"main": [
[
{
"node": "Switch Commands",
"type": "main",
"index": 0
}
]
]
},
"Switch Commands": {
"main": [
[
{
"node": "Handle Status",
"type": "main",
"index": 0
}
],
[
{
"node": "Handle Config",
"type": "main",
"index": 0
}
],
[
{
"node": "Handle Stop",
"type": "main",
"index": 0
}
],
[
{
"node": "Handle Start",
"type": "main",
"index": 0
}
],
[
{
"node": "Handle Agents",
"type": "main",
"index": 0
}
]
]
},
"Handle Status": {
"main": [
[
{
"node": "Telegram Reply",
"type": "main",
"index": 0
}
]
]
},
"Handle Config": {
"main": [
[
{
"node": "Telegram Reply",
"type": "main",
"index": 0
}
]
]
},
"Handle Stop": {
"main": [
[
{
"node": "Telegram Reply",
"type": "main",
"index": 0
}
]
]
},
"Handle Start": {
"main": [
[
{
"node": "Telegram Reply",
"type": "main",
"index": 0
}
]
]
},
"Handle Agents": {
"main": [
[
{
"node": "Telegram Reply",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
telegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Master Agent - Orchestrator. Uses httpRequest, telegram, telegramTrigger. Scheduled trigger; 46 nodes.
Source: https://gist.github.com/milhomao/d655da62da9295eb74d7fa72d2aafcd0 — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Master Agent - Orchestrator. Uses httpRequest, telegram, telegramTrigger. Scheduled trigger; 43 nodes.
Chanchito_PROD. Uses googleGemini, postgres, telegram, httpRequest. Scheduled trigger; 94 nodes.
Author: Nguyen Thieu Toan Category: Community & Knowledge Automation Tags: Telegram, Reddit, n8n Forum, AI Summarization, Gemini, Groq
System Architecture Two integrated N8N workflows providing automated US stock portfolio management through Telegram:
This cutting-edge n8n workflow is a comprehensive automation solution designed to streamline various Instagram operations. It combines an intelligent AI chatbot for direct message management, automate