Binance-Technische-Indikatoren-Webhook-Tool

Experte

Dies ist ein Finance, AI-Bereich Automatisierungsworkflow mit 69 Nodes. Hauptsächlich werden Code, Merge, Webhook, HttpRequest, RespondToWebhook und andere Nodes verwendet, kombiniert mit KI-Technologie für intelligente Automatisierung. Binance technischer Indikator Webhook-Tool

Voraussetzungen
  • HTTP Webhook-Endpunkt (wird von n8n automatisch generiert)
  • Möglicherweise sind Ziel-API-Anmeldedaten erforderlich
Workflow-Vorschau
Visualisierung der Node-Verbindungen, mit Zoom und Pan
Workflow exportieren
Kopieren Sie die folgende JSON-Konfiguration und importieren Sie sie in n8n
{
  "id": "HrTD222kWpvKsy2j",
  "meta": {
    "instanceId": "a5283507e1917a33cc3ae615b2e7d5ad2c1e50955e6f831272ddd5ab816f3fb6"
  },
  "name": "Binance SM Indicators Webhook Tool",
  "tags": [],
  "nodes": [
    {
      "id": "61cb3bd7-60ba-4dcf-a080-146b7612a99b",
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1080,
        0
      ],
      "parameters": {
        "url": "https://api.binance.com/api/v3/klines",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{$json.body.symbol}}"
            },
            {
              "name": "interval",
              "value": "15m"
            },
            {
              "name": "limit",
              "value": "40"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b372f57e-917a-42d2-964c-25a296ea982d",
      "name": "Webhook 15m Indikatoren",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1460,
        0
      ],
      "webhookId": "39cc366c-af5f-472a-9d48-bbe30a4fe3ea",
      "parameters": {
        "path": "39cc366c-af5f-472a-9d48-bbe30a4fe3ea",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "46b937d5-7338-457f-b2f0-eb85ac44a8bb",
      "name": "In 1 Array zusammenführen",
      "type": "n8n-nodes-base.code",
      "position": [
        -740,
        0
      ],
      "parameters": {
        "jsCode": "const klines = $input.all().map(item => item.json);\nreturn [{ json: { klines } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "5b8c1049-bcf4-41d4-bba3-5f76e940bd3e",
      "name": "Bollinger Bands berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -280,
        -460
      ],
      "parameters": {
        "jsCode": "// Get raw klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20;\nconst stdMultiplier = 2;\n\n// Validate type\nif (!Array.isArray(klines)) {\n  throw new Error(\"klines is not an array\");\n}\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Ensure enough data\nif (closes.length < period) {\n  throw new Error(`Not enough data: got ${closes.length}, expected at least ${period}`);\n}\n\n// BB calculation logic\nfunction calculateBB(prices, period) {\n  const result = [];\n  for (let i = period - 1; i < prices.length; i++) {\n    const slice = prices.slice(i - period + 1, i + 1);\n    const mean = slice.reduce((sum, val) => sum + val, 0) / period;\n    const variance = slice.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / period;\n    const stdDev = Math.sqrt(variance);\n\n    result.push({\n      close: prices[i],\n      basis: mean,\n      upper: mean + stdMultiplier * stdDev,\n      lower: mean - stdMultiplier * stdDev,\n      timestamp: klines[i][0]\n    });\n  }\n  return result;\n}\n\n// Calculate bands\nconst bands = calculateBB(closes, period);\n\n// Format output\nreturn bands.map(b => ({\n  json: {\n    timestamp: b.timestamp,\n    close: b.close,\n    bb_basis: b.basis,\n    bb_upper: b.upper,\n    bb_lower: b.lower\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "b0391e28-03a3-40f0-8182-72df0928ca2c",
      "name": "RSI berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -280,
        -240
      ],
      "parameters": {
        "jsCode": "// Pull klines array from JSON\nconst klines = $input.first().json.klines;\n\n// === CONFIGURATION ===\nconst period = 14; // RSI period\n\n// === Extract closing prices ===\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// === Ensure enough data ===\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for RSI. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// === RSI Calculation ===\nfunction calculateRSI(prices, period) {\n  const result = [];\n\n  for (let i = period; i < prices.length; i++) {\n    let gains = 0;\n    let losses = 0;\n\n    for (let j = i - period + 1; j <= i; j++) {\n      const change = prices[j] - prices[j - 1];\n      if (change >= 0) gains += change;\n      else losses -= change;\n    }\n\n    const avgGain = gains / period;\n    const avgLoss = losses / period;\n\n    const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n    const rsi = avgLoss === 0 ? 100 : 100 - (100 / (1 + rs));\n\n    result.push({\n      timestamp: klines[i][0],\n      close: prices[i],\n      rsi: rsi\n    });\n  }\n\n  return result;\n}\n\n// === Run RSI ===\nconst rsiSeries = calculateRSI(closes, period);\n\n// === Return formatted RSI output ===\nreturn rsiSeries.map(r => ({\n  json: {\n    timestamp: r.timestamp,\n    close: r.close,\n    rsi: r.rsi\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "2c598960-630c-4687-91c0-bfae10da1943",
      "name": "MACD berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -280,
        -40
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Parameters\nconst fastPeriod = 12;\nconst slowPeriod = 26;\nconst signalPeriod = 9;\n\n// Validate data length\nconst minRequired = slowPeriod + signalPeriod;\nif (closes.length < minRequired) {\n  throw new Error(`Not enough data for MACD. Need at least ${minRequired}, got ${closes.length}`);\n}\n\n// === Helper: EMA function ===\nfunction calculateEMA(prices, period) {\n  const k = 2 / (period + 1);\n  const ema = [prices.slice(0, period).reduce((a, b) => a + b, 0) / period];\n\n  for (let i = period; i < prices.length; i++) {\n    ema.push(prices[i] * k + ema[ema.length - 1] * (1 - k));\n  }\n\n  return ema;\n}\n\n// === MACD Core Calculation ===\nconst slowEMA = calculateEMA(closes, slowPeriod);\nconst fastEMA = calculateEMA(closes.slice(slowPeriod - fastPeriod), fastPeriod);\n\n// Align lengths\nconst alignedFastEMA = fastEMA.slice(fastEMA.length - slowEMA.length);\nconst macdLine = alignedFastEMA.map((val, i) => val - slowEMA[i]);\n\n// Signal line\nconst signalLine = calculateEMA(macdLine, signalPeriod);\n\n// Histogram\nconst histogram = macdLine.slice(signalPeriod - 1).map((macd, i) => macd - signalLine[i]);\n\n// Final output start index\nconst startIndex = closes.length - histogram.length;\n\nconst result = [];\n\nfor (let i = 0; i < histogram.length; i++) {\n  const idx = startIndex + i;\n  result.push({\n    timestamp: klines[idx]?.[0] || null,\n    close: closes[idx],\n    macd: macdLine[i + signalPeriod - 1],\n    signal: signalLine[i],\n    histogram: histogram[i]\n  });\n}\n\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "c50f7fa5-2707-47a4-9b32-1d582bf5d600",
      "name": "SMA berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -280,
        200
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 50, 100, 200, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute SMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// Calculate SMA values\nconst result = [];\n\nfor (let i = period - 1; i < closes.length; i++) {\n  const slice = closes.slice(i - period + 1, i + 1);\n  const sum = slice.reduce((a, b) => a + b, 0);\n  const average = sum / period;\n\n  result.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    sma: average\n  });\n}\n\n// Return formatted output\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "25ef2964-c9b1-4b9e-b6ce-3766b215d163",
      "name": "EMA berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -280,
        420
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 9, 12, 26, 50, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute EMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// EMA Calculation\nconst k = 2 / (period + 1);\nconst ema = [];\n\n// Start with SMA of first period\nlet sma = closes.slice(0, period).reduce((a, b) => a + b, 0) / period;\nema.push({\n  timestamp: klines[period - 1][0],\n  close: closes[period - 1],\n  ema: sma\n});\n\n// Continue EMA calculation\nfor (let i = period; i < closes.length; i++) {\n  const value = closes[i] * k + ema[ema.length - 1].ema * (1 - k);\n  ema.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    ema: value\n  });\n}\n\n// Return result\nreturn ema.map(e => ({\n  json: e\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "df6546be-3778-4fba-801a-870c0fe883c3",
      "name": "ADX berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -280,
        660
      ],
      "parameters": {
        "jsCode": "// Get kline array from merged item\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 14;\n\n// Parse high, low, close arrays\nconst highs = klines.map(k => parseFloat(k[2]));\nconst lows = klines.map(k => parseFloat(k[3]));\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Validation\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for ADX. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// Initialize arrays\nconst tr = [];\nconst plusDM = [];\nconst minusDM = [];\n\n// Step 1: Calculate TR, +DM, -DM\nfor (let i = 1; i < klines.length; i++) {\n  const highDiff = highs[i] - highs[i - 1];\n  const lowDiff = lows[i - 1] - lows[i];\n  const upMove = highDiff > 0 && highDiff > lowDiff ? highDiff : 0;\n  const downMove = lowDiff > 0 && lowDiff > highDiff ? lowDiff : 0;\n\n  const trueRange = Math.max(\n    highs[i] - lows[i],\n    Math.abs(highs[i] - closes[i - 1]),\n    Math.abs(lows[i] - closes[i - 1])\n  );\n\n  tr.push(trueRange);\n  plusDM.push(upMove);\n  minusDM.push(downMove);\n}\n\n// Step 2: Smooth TR, +DM, -DM and calculate DI\nconst smoothedTR = [tr.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedPlusDM = [plusDM.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedMinusDM = [minusDM.slice(0, period).reduce((a, b) => a + b, 0)];\n\nfor (let i = period; i < tr.length; i++) {\n  smoothedTR.push(smoothedTR[smoothedTR.length - 1] - smoothedTR[smoothedTR.length - 1] / period + tr[i]);\n  smoothedPlusDM.push(smoothedPlusDM[smoothedPlusDM.length - 1] - smoothedPlusDM[smoothedPlusDM.length - 1] / period + plusDM[i]);\n  smoothedMinusDM.push(smoothedMinusDM[smoothedMinusDM.length - 1] - smoothedMinusDM[smoothedMinusDM.length - 1] / period + minusDM[i]);\n}\n\nconst plusDI = smoothedPlusDM.map((val, i) => 100 * val / smoothedTR[i]);\nconst minusDI = smoothedMinusDM.map((val, i) => 100 * val / smoothedTR[i]);\n\n// Step 3: Calculate DX\nconst dx = plusDI.map((val, i) => {\n  const diff = Math.abs(plusDI[i] - minusDI[i]);\n  const sum = plusDI[i] + minusDI[i];\n  return sum === 0 ? 0 : 100 * (diff / sum);\n});\n\n// Step 4: Smooth DX into ADX\nconst adx = [];\nconst firstADX = dx.slice(0, period).reduce((a, b) => a + b, 0) / period;\nadx.push(firstADX);\n\nfor (let i = period; i < dx.length; i++) {\n  const newADX = (adx[adx.length - 1] * (period - 1) + dx[i]) / period;\n  adx.push(newADX);\n}\n\n// Step 5: Final result formatting\nconst output = [];\n\nfor (let i = 0; i < adx.length; i++) {\n  const index = i + (2 * period); // account for offset\n  if (klines[index]) {\n    output.push({\n      timestamp: klines[index][0],\n      close: closes[index],\n      adx: adx[i],\n      plusDI: plusDI[i + period],\n      minusDI: minusDI[i + period]\n    });\n  }\n}\n\nreturn output.map(r => ({ json: r }));"
      },
      "typeVersion": 2
    },
    {
      "id": "5ea4756f-a20a-444c-8471-57d209457823",
      "name": "15 Min. Indikatoren zusammenführen",
      "type": "n8n-nodes-base.merge",
      "position": [
        180,
        -80
      ],
      "parameters": {
        "numberInputs": 6
      },
      "typeVersion": 3.1
    },
    {
      "id": "0a46ea7c-a85d-4fc6-b20c-7e65ab962ea6",
      "name": "Auf 15m Webhook antworten",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        560,
        -20
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.3
    },
    {
      "id": "40fcbba9-489b-4452-baf8-9a41ef0f9b07",
      "name": "Webhook 1h Indikatoren",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -3220,
        1760
      ],
      "webhookId": "78948764-5cdb-4808-8ef9-2155f10dd721",
      "parameters": {
        "path": "78948764-5cdb-4808-8ef9-2155f10dd721",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "39e6b058-d800-4751-b75b-5e62be379c61",
      "name": "Auf 1h Webhook antworten",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -1060,
        1740
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.3
    },
    {
      "id": "af471a36-1107-4ccd-be14-efa06ba27330",
      "name": "1h Indikatoren zusammenführen",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1460,
        1680
      ],
      "parameters": {
        "numberInputs": 6
      },
      "typeVersion": 3.1
    },
    {
      "id": "d7d9bb31-6986-42e6-9f0c-7d7f00d66ee1",
      "name": "In 1 Array 1h zusammenführen",
      "type": "n8n-nodes-base.code",
      "position": [
        -2540,
        1760
      ],
      "parameters": {
        "jsCode": "const klines = $input.all().map(item => item.json);\nreturn [{ json: { klines } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "a2e9d933-47d7-47f5-8d49-224860d8600a",
      "name": "Bollinger Bands (1h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -2060,
        1220
      ],
      "parameters": {
        "jsCode": "// Get raw klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20;\nconst stdMultiplier = 2;\n\n// Validate type\nif (!Array.isArray(klines)) {\n  throw new Error(\"klines is not an array\");\n}\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Ensure enough data\nif (closes.length < period) {\n  throw new Error(`Not enough data: got ${closes.length}, expected at least ${period}`);\n}\n\n// BB calculation logic\nfunction calculateBB(prices, period) {\n  const result = [];\n  for (let i = period - 1; i < prices.length; i++) {\n    const slice = prices.slice(i - period + 1, i + 1);\n    const mean = slice.reduce((sum, val) => sum + val, 0) / period;\n    const variance = slice.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / period;\n    const stdDev = Math.sqrt(variance);\n\n    result.push({\n      close: prices[i],\n      basis: mean,\n      upper: mean + stdMultiplier * stdDev,\n      lower: mean - stdMultiplier * stdDev,\n      timestamp: klines[i][0]\n    });\n  }\n  return result;\n}\n\n// Calculate bands\nconst bands = calculateBB(closes, period);\n\n// Format output\nreturn bands.map(b => ({\n  json: {\n    timestamp: b.timestamp,\n    close: b.close,\n    bb_basis: b.basis,\n    bb_upper: b.upper,\n    bb_lower: b.lower\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "702e8bc8-d8f0-4fb4-b44f-fd19479c8b03",
      "name": "RSI (1h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -2060,
        1500
      ],
      "parameters": {
        "jsCode": "// Pull klines array from JSON\nconst klines = $input.first().json.klines;\n\n// === CONFIGURATION ===\nconst period = 14; // RSI period\n\n// === Extract closing prices ===\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// === Ensure enough data ===\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for RSI. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// === RSI Calculation ===\nfunction calculateRSI(prices, period) {\n  const result = [];\n\n  for (let i = period; i < prices.length; i++) {\n    let gains = 0;\n    let losses = 0;\n\n    for (let j = i - period + 1; j <= i; j++) {\n      const change = prices[j] - prices[j - 1];\n      if (change >= 0) gains += change;\n      else losses -= change;\n    }\n\n    const avgGain = gains / period;\n    const avgLoss = losses / period;\n\n    const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n    const rsi = avgLoss === 0 ? 100 : 100 - (100 / (1 + rs));\n\n    result.push({\n      timestamp: klines[i][0],\n      close: prices[i],\n      rsi: rsi\n    });\n  }\n\n  return result;\n}\n\n// === Run RSI ===\nconst rsiSeries = calculateRSI(closes, period);\n\n// === Return formatted RSI output ===\nreturn rsiSeries.map(r => ({\n  json: {\n    timestamp: r.timestamp,\n    close: r.close,\n    rsi: r.rsi\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "d16b7829-59ca-45ca-a5ae-4e315168c96d",
      "name": "MACD (1h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -2060,
        1720
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Parameters\nconst fastPeriod = 12;\nconst slowPeriod = 26;\nconst signalPeriod = 9;\n\n// Validate data length\nconst minRequired = slowPeriod + signalPeriod;\nif (closes.length < minRequired) {\n  throw new Error(`Not enough data for MACD. Need at least ${minRequired}, got ${closes.length}`);\n}\n\n// === Helper: EMA function ===\nfunction calculateEMA(prices, period) {\n  const k = 2 / (period + 1);\n  const ema = [prices.slice(0, period).reduce((a, b) => a + b, 0) / period];\n\n  for (let i = period; i < prices.length; i++) {\n    ema.push(prices[i] * k + ema[ema.length - 1] * (1 - k));\n  }\n\n  return ema;\n}\n\n// === MACD Core Calculation ===\nconst slowEMA = calculateEMA(closes, slowPeriod);\nconst fastEMA = calculateEMA(closes.slice(slowPeriod - fastPeriod), fastPeriod);\n\n// Align lengths\nconst alignedFastEMA = fastEMA.slice(fastEMA.length - slowEMA.length);\nconst macdLine = alignedFastEMA.map((val, i) => val - slowEMA[i]);\n\n// Signal line\nconst signalLine = calculateEMA(macdLine, signalPeriod);\n\n// Histogram\nconst histogram = macdLine.slice(signalPeriod - 1).map((macd, i) => macd - signalLine[i]);\n\n// Final output start index\nconst startIndex = closes.length - histogram.length;\n\nconst result = [];\n\nfor (let i = 0; i < histogram.length; i++) {\n  const idx = startIndex + i;\n  result.push({\n    timestamp: klines[idx]?.[0] || null,\n    close: closes[idx],\n    macd: macdLine[i + signalPeriod - 1],\n    signal: signalLine[i],\n    histogram: histogram[i]\n  });\n}\n\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "15bf5c3b-de07-412b-988c-f5c59fa7a06f",
      "name": "SMA (1h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -2060,
        1960
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 50, 100, 200, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute SMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// Calculate SMA values\nconst result = [];\n\nfor (let i = period - 1; i < closes.length; i++) {\n  const slice = closes.slice(i - period + 1, i + 1);\n  const sum = slice.reduce((a, b) => a + b, 0);\n  const average = sum / period;\n\n  result.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    sma: average\n  });\n}\n\n// Return formatted output\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "83fa487a-1a67-4059-9007-d5298d8bb105",
      "name": "EMA (1h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -2060,
        2220
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 9, 12, 26, 50, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute EMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// EMA Calculation\nconst k = 2 / (period + 1);\nconst ema = [];\n\n// Start with SMA of first period\nlet sma = closes.slice(0, period).reduce((a, b) => a + b, 0) / period;\nema.push({\n  timestamp: klines[period - 1][0],\n  close: closes[period - 1],\n  ema: sma\n});\n\n// Continue EMA calculation\nfor (let i = period; i < closes.length; i++) {\n  const value = closes[i] * k + ema[ema.length - 1].ema * (1 - k);\n  ema.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    ema: value\n  });\n}\n\n// Return result\nreturn ema.map(e => ({\n  json: e\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "7301ba32-ab0c-4c80-b041-cd398f7daa2c",
      "name": "ADX1 berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -2060,
        2420
      ],
      "parameters": {
        "jsCode": "// Get kline array from merged item\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 14;\n\n// Parse high, low, close arrays\nconst highs = klines.map(k => parseFloat(k[2]));\nconst lows = klines.map(k => parseFloat(k[3]));\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Validation\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for ADX. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// Initialize arrays\nconst tr = [];\nconst plusDM = [];\nconst minusDM = [];\n\n// Step 1: Calculate TR, +DM, -DM\nfor (let i = 1; i < klines.length; i++) {\n  const highDiff = highs[i] - highs[i - 1];\n  const lowDiff = lows[i - 1] - lows[i];\n  const upMove = highDiff > 0 && highDiff > lowDiff ? highDiff : 0;\n  const downMove = lowDiff > 0 && lowDiff > highDiff ? lowDiff : 0;\n\n  const trueRange = Math.max(\n    highs[i] - lows[i],\n    Math.abs(highs[i] - closes[i - 1]),\n    Math.abs(lows[i] - closes[i - 1])\n  );\n\n  tr.push(trueRange);\n  plusDM.push(upMove);\n  minusDM.push(downMove);\n}\n\n// Step 2: Smooth TR, +DM, -DM and calculate DI\nconst smoothedTR = [tr.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedPlusDM = [plusDM.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedMinusDM = [minusDM.slice(0, period).reduce((a, b) => a + b, 0)];\n\nfor (let i = period; i < tr.length; i++) {\n  smoothedTR.push(smoothedTR[smoothedTR.length - 1] - smoothedTR[smoothedTR.length - 1] / period + tr[i]);\n  smoothedPlusDM.push(smoothedPlusDM[smoothedPlusDM.length - 1] - smoothedPlusDM[smoothedPlusDM.length - 1] / period + plusDM[i]);\n  smoothedMinusDM.push(smoothedMinusDM[smoothedMinusDM.length - 1] - smoothedMinusDM[smoothedMinusDM.length - 1] / period + minusDM[i]);\n}\n\nconst plusDI = smoothedPlusDM.map((val, i) => 100 * val / smoothedTR[i]);\nconst minusDI = smoothedMinusDM.map((val, i) => 100 * val / smoothedTR[i]);\n\n// Step 3: Calculate DX\nconst dx = plusDI.map((val, i) => {\n  const diff = Math.abs(plusDI[i] - minusDI[i]);\n  const sum = plusDI[i] + minusDI[i];\n  return sum === 0 ? 0 : 100 * (diff / sum);\n});\n\n// Step 4: Smooth DX into ADX\nconst adx = [];\nconst firstADX = dx.slice(0, period).reduce((a, b) => a + b, 0) / period;\nadx.push(firstADX);\n\nfor (let i = period; i < dx.length; i++) {\n  const newADX = (adx[adx.length - 1] * (period - 1) + dx[i]) / period;\n  adx.push(newADX);\n}\n\n// Step 5: Final result formatting\nconst output = [];\n\nfor (let i = 0; i < adx.length; i++) {\n  const index = i + (2 * period); // account for offset\n  if (klines[index]) {\n    output.push({\n      timestamp: klines[index][0],\n      close: closes[index],\n      adx: adx[i],\n      plusDI: plusDI[i + period],\n      minusDI: minusDI[i + period]\n    });\n  }\n}\n\nreturn output.map(r => ({ json: r }));"
      },
      "typeVersion": 2
    },
    {
      "id": "9ec777b1-eb23-43b0-8bb3-726adcc3418f",
      "name": "HTTP Request 1h",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2880,
        1760
      ],
      "parameters": {
        "url": "https://api.binance.com/api/v3/klines",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{$json.body.symbol}}"
            },
            {
              "name": "interval",
              "value": "1h"
            },
            {
              "name": "limit",
              "value": "40"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "c4a718f8-f2d6-464b-8120-1e4067d760ad",
      "name": "Webhook 4h Indikatoren",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -40,
        1680
      ],
      "webhookId": "55fd9665-ed4a-4e1a-8062-890cad0fb6ac",
      "parameters": {
        "path": "55fd9665-ed4a-4e1a-8062-890cad0fb6ac",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "bbb81a2a-b1c4-4c1e-80e9-a4990c45fae8",
      "name": "HTTP Request 4h",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        300,
        1680
      ],
      "parameters": {
        "url": "https://api.binance.com/api/v3/klines",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{$json.body.symbol}}"
            },
            {
              "name": "interval",
              "value": "4h"
            },
            {
              "name": "limit",
              "value": "40"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "20e58706-035f-46ef-b235-78fe468875bc",
      "name": "In 1 Array 4h zusammenführen",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        1680
      ],
      "parameters": {
        "jsCode": "const klines = $input.all().map(item => item.json);\nreturn [{ json: { klines } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "1675168b-97ab-4be4-92d2-734d075e2222",
      "name": "Bollinger Bands (4h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        1180
      ],
      "parameters": {
        "jsCode": "// Get raw klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20;\nconst stdMultiplier = 2;\n\n// Validate type\nif (!Array.isArray(klines)) {\n  throw new Error(\"klines is not an array\");\n}\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Ensure enough data\nif (closes.length < period) {\n  throw new Error(`Not enough data: got ${closes.length}, expected at least ${period}`);\n}\n\n// BB calculation logic\nfunction calculateBB(prices, period) {\n  const result = [];\n  for (let i = period - 1; i < prices.length; i++) {\n    const slice = prices.slice(i - period + 1, i + 1);\n    const mean = slice.reduce((sum, val) => sum + val, 0) / period;\n    const variance = slice.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / period;\n    const stdDev = Math.sqrt(variance);\n\n    result.push({\n      close: prices[i],\n      basis: mean,\n      upper: mean + stdMultiplier * stdDev,\n      lower: mean - stdMultiplier * stdDev,\n      timestamp: klines[i][0]\n    });\n  }\n  return result;\n}\n\n// Calculate bands\nconst bands = calculateBB(closes, period);\n\n// Format output\nreturn bands.map(b => ({\n  json: {\n    timestamp: b.timestamp,\n    close: b.close,\n    bb_basis: b.basis,\n    bb_upper: b.upper,\n    bb_lower: b.lower\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "d1c2fcc7-65ac-446c-8f59-c4cd8ac7d795",
      "name": "RSI (4h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        1420
      ],
      "parameters": {
        "jsCode": "// Pull klines array from JSON\nconst klines = $input.first().json.klines;\n\n// === CONFIGURATION ===\nconst period = 14; // RSI period\n\n// === Extract closing prices ===\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// === Ensure enough data ===\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for RSI. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// === RSI Calculation ===\nfunction calculateRSI(prices, period) {\n  const result = [];\n\n  for (let i = period; i < prices.length; i++) {\n    let gains = 0;\n    let losses = 0;\n\n    for (let j = i - period + 1; j <= i; j++) {\n      const change = prices[j] - prices[j - 1];\n      if (change >= 0) gains += change;\n      else losses -= change;\n    }\n\n    const avgGain = gains / period;\n    const avgLoss = losses / period;\n\n    const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n    const rsi = avgLoss === 0 ? 100 : 100 - (100 / (1 + rs));\n\n    result.push({\n      timestamp: klines[i][0],\n      close: prices[i],\n      rsi: rsi\n    });\n  }\n\n  return result;\n}\n\n// === Run RSI ===\nconst rsiSeries = calculateRSI(closes, period);\n\n// === Return formatted RSI output ===\nreturn rsiSeries.map(r => ({\n  json: {\n    timestamp: r.timestamp,\n    close: r.close,\n    rsi: r.rsi\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "df32e55c-2dbc-44d6-8161-f70db651cfcd",
      "name": "MACD (4h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        1660
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Parameters\nconst fastPeriod = 12;\nconst slowPeriod = 26;\nconst signalPeriod = 9;\n\n// Validate data length\nconst minRequired = slowPeriod + signalPeriod;\nif (closes.length < minRequired) {\n  throw new Error(`Not enough data for MACD. Need at least ${minRequired}, got ${closes.length}`);\n}\n\n// === Helper: EMA function ===\nfunction calculateEMA(prices, period) {\n  const k = 2 / (period + 1);\n  const ema = [prices.slice(0, period).reduce((a, b) => a + b, 0) / period];\n\n  for (let i = period; i < prices.length; i++) {\n    ema.push(prices[i] * k + ema[ema.length - 1] * (1 - k));\n  }\n\n  return ema;\n}\n\n// === MACD Core Calculation ===\nconst slowEMA = calculateEMA(closes, slowPeriod);\nconst fastEMA = calculateEMA(closes.slice(slowPeriod - fastPeriod), fastPeriod);\n\n// Align lengths\nconst alignedFastEMA = fastEMA.slice(fastEMA.length - slowEMA.length);\nconst macdLine = alignedFastEMA.map((val, i) => val - slowEMA[i]);\n\n// Signal line\nconst signalLine = calculateEMA(macdLine, signalPeriod);\n\n// Histogram\nconst histogram = macdLine.slice(signalPeriod - 1).map((macd, i) => macd - signalLine[i]);\n\n// Final output start index\nconst startIndex = closes.length - histogram.length;\n\nconst result = [];\n\nfor (let i = 0; i < histogram.length; i++) {\n  const idx = startIndex + i;\n  result.push({\n    timestamp: klines[idx]?.[0] || null,\n    close: closes[idx],\n    macd: macdLine[i + signalPeriod - 1],\n    signal: signalLine[i],\n    histogram: histogram[i]\n  });\n}\n\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "93a115f7-e876-43f0-8ecb-70a47a06f41c",
      "name": "SMA (4h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        1880
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 50, 100, 200, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute SMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// Calculate SMA values\nconst result = [];\n\nfor (let i = period - 1; i < closes.length; i++) {\n  const slice = closes.slice(i - period + 1, i + 1);\n  const sum = slice.reduce((a, b) => a + b, 0);\n  const average = sum / period;\n\n  result.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    sma: average\n  });\n}\n\n// Return formatted output\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "17bf4e9f-7053-4a04-b439-3e23fe5cb7f9",
      "name": "EMA (4h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        2100
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 9, 12, 26, 50, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute EMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// EMA Calculation\nconst k = 2 / (period + 1);\nconst ema = [];\n\n// Start with SMA of first period\nlet sma = closes.slice(0, period).reduce((a, b) => a + b, 0) / period;\nema.push({\n  timestamp: klines[period - 1][0],\n  close: closes[period - 1],\n  ema: sma\n});\n\n// Continue EMA calculation\nfor (let i = period; i < closes.length; i++) {\n  const value = closes[i] * k + ema[ema.length - 1].ema * (1 - k);\n  ema.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    ema: value\n  });\n}\n\n// Return result\nreturn ema.map(e => ({\n  json: e\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "d61db420-9def-4ec1-bf8a-94afeabda186",
      "name": "ADX (4h) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        2360
      ],
      "parameters": {
        "jsCode": "// Get kline array from merged item\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 14;\n\n// Parse high, low, close arrays\nconst highs = klines.map(k => parseFloat(k[2]));\nconst lows = klines.map(k => parseFloat(k[3]));\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Validation\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for ADX. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// Initialize arrays\nconst tr = [];\nconst plusDM = [];\nconst minusDM = [];\n\n// Step 1: Calculate TR, +DM, -DM\nfor (let i = 1; i < klines.length; i++) {\n  const highDiff = highs[i] - highs[i - 1];\n  const lowDiff = lows[i - 1] - lows[i];\n  const upMove = highDiff > 0 && highDiff > lowDiff ? highDiff : 0;\n  const downMove = lowDiff > 0 && lowDiff > highDiff ? lowDiff : 0;\n\n  const trueRange = Math.max(\n    highs[i] - lows[i],\n    Math.abs(highs[i] - closes[i - 1]),\n    Math.abs(lows[i] - closes[i - 1])\n  );\n\n  tr.push(trueRange);\n  plusDM.push(upMove);\n  minusDM.push(downMove);\n}\n\n// Step 2: Smooth TR, +DM, -DM and calculate DI\nconst smoothedTR = [tr.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedPlusDM = [plusDM.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedMinusDM = [minusDM.slice(0, period).reduce((a, b) => a + b, 0)];\n\nfor (let i = period; i < tr.length; i++) {\n  smoothedTR.push(smoothedTR[smoothedTR.length - 1] - smoothedTR[smoothedTR.length - 1] / period + tr[i]);\n  smoothedPlusDM.push(smoothedPlusDM[smoothedPlusDM.length - 1] - smoothedPlusDM[smoothedPlusDM.length - 1] / period + plusDM[i]);\n  smoothedMinusDM.push(smoothedMinusDM[smoothedMinusDM.length - 1] - smoothedMinusDM[smoothedMinusDM.length - 1] / period + minusDM[i]);\n}\n\nconst plusDI = smoothedPlusDM.map((val, i) => 100 * val / smoothedTR[i]);\nconst minusDI = smoothedMinusDM.map((val, i) => 100 * val / smoothedTR[i]);\n\n// Step 3: Calculate DX\nconst dx = plusDI.map((val, i) => {\n  const diff = Math.abs(plusDI[i] - minusDI[i]);\n  const sum = plusDI[i] + minusDI[i];\n  return sum === 0 ? 0 : 100 * (diff / sum);\n});\n\n// Step 4: Smooth DX into ADX\nconst adx = [];\nconst firstADX = dx.slice(0, period).reduce((a, b) => a + b, 0) / period;\nadx.push(firstADX);\n\nfor (let i = period; i < dx.length; i++) {\n  const newADX = (adx[adx.length - 1] * (period - 1) + dx[i]) / period;\n  adx.push(newADX);\n}\n\n// Step 5: Final result formatting\nconst output = [];\n\nfor (let i = 0; i < adx.length; i++) {\n  const index = i + (2 * period); // account for offset\n  if (klines[index]) {\n    output.push({\n      timestamp: klines[index][0],\n      close: closes[index],\n      adx: adx[i],\n      plusDI: plusDI[i + period],\n      minusDI: minusDI[i + period]\n    });\n  }\n}\n\nreturn output.map(r => ({ json: r }));"
      },
      "typeVersion": 2
    },
    {
      "id": "7940bc3f-c5f4-4b4b-b542-8737f17afbbf",
      "name": "4h Indikatoren zusammenführen",
      "type": "n8n-nodes-base.merge",
      "position": [
        1720,
        1600
      ],
      "parameters": {
        "numberInputs": 6
      },
      "typeVersion": 3.1
    },
    {
      "id": "8585a269-b70d-4af7-b264-db9f638c8a77",
      "name": "Auf 4h Webhook antworten",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2120,
        1660
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.3
    },
    {
      "id": "91a05310-bd36-4ee0-8381-b3d16c3346be",
      "name": "Webhook 1d Indikatoren",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1600,
        3140
      ],
      "webhookId": "23c8ec04-5aec-49c6-9c2c-c352ccec11b4",
      "parameters": {
        "path": "23c8ec04-5aec-49c6-9c2c-c352ccec11b4",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "29121e2e-f6e2-4d42-8e5b-e8eea1e61ec8",
      "name": "HTTP Request 1d",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1260,
        3140
      ],
      "parameters": {
        "url": "https://api.binance.com/api/v3/klines",
        "options": {},
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "symbol",
              "value": "={{$json.body.symbol}}"
            },
            {
              "name": "interval",
              "value": "1d"
            },
            {
              "name": "limit",
              "value": "40"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1be26316-3d08-4983-9d12-6bc36465e76e",
      "name": "In 1 Array 1d zusammenführen",
      "type": "n8n-nodes-base.code",
      "position": [
        -900,
        3140
      ],
      "parameters": {
        "jsCode": "const klines = $input.all().map(item => item.json);\nreturn [{ json: { klines } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "6e9c7976-737b-4377-b412-633a1a67097b",
      "name": "Bollinger Bands (1d) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        2620
      ],
      "parameters": {
        "jsCode": "// Get raw klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20;\nconst stdMultiplier = 2;\n\n// Validate type\nif (!Array.isArray(klines)) {\n  throw new Error(\"klines is not an array\");\n}\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Ensure enough data\nif (closes.length < period) {\n  throw new Error(`Not enough data: got ${closes.length}, expected at least ${period}`);\n}\n\n// BB calculation logic\nfunction calculateBB(prices, period) {\n  const result = [];\n  for (let i = period - 1; i < prices.length; i++) {\n    const slice = prices.slice(i - period + 1, i + 1);\n    const mean = slice.reduce((sum, val) => sum + val, 0) / period;\n    const variance = slice.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / period;\n    const stdDev = Math.sqrt(variance);\n\n    result.push({\n      close: prices[i],\n      basis: mean,\n      upper: mean + stdMultiplier * stdDev,\n      lower: mean - stdMultiplier * stdDev,\n      timestamp: klines[i][0]\n    });\n  }\n  return result;\n}\n\n// Calculate bands\nconst bands = calculateBB(closes, period);\n\n// Format output\nreturn bands.map(b => ({\n  json: {\n    timestamp: b.timestamp,\n    close: b.close,\n    bb_basis: b.basis,\n    bb_upper: b.upper,\n    bb_lower: b.lower\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "ac84877a-75cf-4ef2-bad1-8d0f444ecead",
      "name": "RSI (1d) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        2880
      ],
      "parameters": {
        "jsCode": "// Pull klines array from JSON\nconst klines = $input.first().json.klines;\n\n// === CONFIGURATION ===\nconst period = 14; // RSI period\n\n// === Extract closing prices ===\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// === Ensure enough data ===\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for RSI. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// === RSI Calculation ===\nfunction calculateRSI(prices, period) {\n  const result = [];\n\n  for (let i = period; i < prices.length; i++) {\n    let gains = 0;\n    let losses = 0;\n\n    for (let j = i - period + 1; j <= i; j++) {\n      const change = prices[j] - prices[j - 1];\n      if (change >= 0) gains += change;\n      else losses -= change;\n    }\n\n    const avgGain = gains / period;\n    const avgLoss = losses / period;\n\n    const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n    const rsi = avgLoss === 0 ? 100 : 100 - (100 / (1 + rs));\n\n    result.push({\n      timestamp: klines[i][0],\n      close: prices[i],\n      rsi: rsi\n    });\n  }\n\n  return result;\n}\n\n// === Run RSI ===\nconst rsiSeries = calculateRSI(closes, period);\n\n// === Return formatted RSI output ===\nreturn rsiSeries.map(r => ({\n  json: {\n    timestamp: r.timestamp,\n    close: r.close,\n    rsi: r.rsi\n  }\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "a5da65c5-db5a-4f9d-a6cf-228f081ffb3c",
      "name": "MACD (1d) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        3120
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Parameters\nconst fastPeriod = 12;\nconst slowPeriod = 26;\nconst signalPeriod = 9;\n\n// Validate data length\nconst minRequired = slowPeriod + signalPeriod;\nif (closes.length < minRequired) {\n  throw new Error(`Not enough data for MACD. Need at least ${minRequired}, got ${closes.length}`);\n}\n\n// === Helper: EMA function ===\nfunction calculateEMA(prices, period) {\n  const k = 2 / (period + 1);\n  const ema = [prices.slice(0, period).reduce((a, b) => a + b, 0) / period];\n\n  for (let i = period; i < prices.length; i++) {\n    ema.push(prices[i] * k + ema[ema.length - 1] * (1 - k));\n  }\n\n  return ema;\n}\n\n// === MACD Core Calculation ===\nconst slowEMA = calculateEMA(closes, slowPeriod);\nconst fastEMA = calculateEMA(closes.slice(slowPeriod - fastPeriod), fastPeriod);\n\n// Align lengths\nconst alignedFastEMA = fastEMA.slice(fastEMA.length - slowEMA.length);\nconst macdLine = alignedFastEMA.map((val, i) => val - slowEMA[i]);\n\n// Signal line\nconst signalLine = calculateEMA(macdLine, signalPeriod);\n\n// Histogram\nconst histogram = macdLine.slice(signalPeriod - 1).map((macd, i) => macd - signalLine[i]);\n\n// Final output start index\nconst startIndex = closes.length - histogram.length;\n\nconst result = [];\n\nfor (let i = 0; i < histogram.length; i++) {\n  const idx = startIndex + i;\n  result.push({\n    timestamp: klines[idx]?.[0] || null,\n    close: closes[idx],\n    macd: macdLine[i + signalPeriod - 1],\n    signal: signalLine[i],\n    histogram: histogram[i]\n  });\n}\n\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "5b1f34b3-e675-46b1-a754-679080680058",
      "name": "SMA (1d) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        3340
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 50, 100, 200, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute SMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// Calculate SMA values\nconst result = [];\n\nfor (let i = period - 1; i < closes.length; i++) {\n  const slice = closes.slice(i - period + 1, i + 1);\n  const sum = slice.reduce((a, b) => a + b, 0);\n  const average = sum / period;\n\n  result.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    sma: average\n  });\n}\n\n// Return formatted output\nreturn result.map(r => ({\n  json: r\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "0b3278a6-5793-4d70-8954-70562c43a3eb",
      "name": "EMA (1d) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        3580
      ],
      "parameters": {
        "jsCode": "// Get klines array\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 20; // Change to 9, 12, 26, 50, etc. as needed\n\n// Extract closing prices\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Check for sufficient data\nif (closes.length < period) {\n  throw new Error(`Not enough data to compute EMA. Need at least ${period}, got ${closes.length}`);\n}\n\n// EMA Calculation\nconst k = 2 / (period + 1);\nconst ema = [];\n\n// Start with SMA of first period\nlet sma = closes.slice(0, period).reduce((a, b) => a + b, 0) / period;\nema.push({\n  timestamp: klines[period - 1][0],\n  close: closes[period - 1],\n  ema: sma\n});\n\n// Continue EMA calculation\nfor (let i = period; i < closes.length; i++) {\n  const value = closes[i] * k + ema[ema.length - 1].ema * (1 - k);\n  ema.push({\n    timestamp: klines[i][0],\n    close: closes[i],\n    ema: value\n  });\n}\n\n// Return result\nreturn ema.map(e => ({\n  json: e\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "28dd6fb1-a161-49ed-9c14-740e88e2195d",
      "name": "ADX (1d) berechnen",
      "type": "n8n-nodes-base.code",
      "position": [
        -440,
        3780
      ],
      "parameters": {
        "jsCode": "// Get kline array from merged item\nconst klines = $input.first().json.klines;\n\n// Parameters\nconst period = 14;\n\n// Parse high, low, close arrays\nconst highs = klines.map(k => parseFloat(k[2]));\nconst lows = klines.map(k => parseFloat(k[3]));\nconst closes = klines.map(k => parseFloat(k[4]));\n\n// Validation\nif (closes.length < period + 1) {\n  throw new Error(`Not enough data for ADX. Need at least ${period + 1}, got ${closes.length}`);\n}\n\n// Initialize arrays\nconst tr = [];\nconst plusDM = [];\nconst minusDM = [];\n\n// Step 1: Calculate TR, +DM, -DM\nfor (let i = 1; i < klines.length; i++) {\n  const highDiff = highs[i] - highs[i - 1];\n  const lowDiff = lows[i - 1] - lows[i];\n  const upMove = highDiff > 0 && highDiff > lowDiff ? highDiff : 0;\n  const downMove = lowDiff > 0 && lowDiff > highDiff ? lowDiff : 0;\n\n  const trueRange = Math.max(\n    highs[i] - lows[i],\n    Math.abs(highs[i] - closes[i - 1]),\n    Math.abs(lows[i] - closes[i - 1])\n  );\n\n  tr.push(trueRange);\n  plusDM.push(upMove);\n  minusDM.push(downMove);\n}\n\n// Step 2: Smooth TR, +DM, -DM and calculate DI\nconst smoothedTR = [tr.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedPlusDM = [plusDM.slice(0, period).reduce((a, b) => a + b, 0)];\nconst smoothedMinusDM = [minusDM.slice(0, period).reduce((a, b) => a + b, 0)];\n\nfor (let i = period; i < tr.length; i++) {\n  smoothedTR.push(smoothedTR[smoothedTR.length - 1] - smoothedTR[smoothedTR.length - 1] / period + tr[i]);\n  smoothedPlusDM.push(smoothedPlusDM[smoothedPlusDM.length - 1] - smoothedPlusDM[smoothedPlusDM.length - 1] / period + plusDM[i]);\n  smoothedMinusDM.push(smoothedMinusDM[smoothedMinusDM.length - 1] - smoothedMinusDM[smoothedMinusDM.length - 1] / period + minusDM[i]);\n}\n\nconst plusDI = smoothedPlusDM.map((val, i) => 100 * val / smoothedTR[i]);\nconst minusDI = smoothedMinusDM.map((val, i) => 100 * val / smoothedTR[i]);\n\n// Step 3: Calculate DX\nconst dx = plusDI.map((val, i) => {\n  const diff = Math.abs(plusDI[i] - minusDI[i]);\n  const sum = plusDI[i] + minusDI[i];\n  return sum === 0 ? 0 : 100 * (diff / sum);\n});\n\n// Step 4: Smooth DX into ADX\nconst adx = [];\nconst firstADX = dx.slice(0, period).reduce((a, b) => a + b, 0) / period;\nadx.push(firstADX);\n\nfor (let i = period; i < dx.length; i++) {\n  const newADX = (adx[adx.length - 1] * (period - 1) + dx[i]) / period;\n  adx.push(newADX);\n}\n\n// Step 5: Final result formatting\nconst output = [];\n\nfor (let i = 0; i < adx.length; i++) {\n  const index = i + (2 * period); // account for offset\n  if (klines[index]) {\n    output.push({\n      timestamp: klines[index][0],\n      close: closes[index],\n      adx: adx[i],\n      plusDI: plusDI[i + period],\n      minusDI: minusDI[i + period]\n    });\n  }\n}\n\nreturn output.map(r => ({ json: r }));"
      },
      "typeVersion": 2
    },
    {
      "id": "660dfcb4-ed63-4c53-806b-cba96c105fee",
      "name": "1d Indikatoren zusammenführen",
      "type": "n8n-nodes-base.merge",
      "position": [
        160,
        3060
      ],
      "parameters": {
        "numberInputs": 6
      },
      "typeVersion": 3.1
    },
    {
      "id": "939665ce-7a50-4a6d-9b37-62ad800350b0",
      "name": "Auf 1d Webhook antworten",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        560,
        3120
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.3
    },
    {
      "id": "2f44a150-0c5a-4b74-92b5-fe941fccb86d",
      "name": "Notizzettel13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        100,
        -340
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 620,
        "content": "## Technical Indicator API Merge \n\n**Binance API** for 6 indicators:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "fbf6424d-8bfc-469b-ae56-c9824e657e75",
      "name": "Notizzettel",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1640,
        1340
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 620,
        "content": "## Technical Indicator API Merge \n\n**Binance API** for 6 indicators:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "fba15cd9-e7a0-4a03-813e-444dd2cd25f7",
      "name": "Notizzettel14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1540,
        1380
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 620,
        "content": "## Technical Indicator API Merge \n\n**Binance API** for 6 indicators:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "ab87eb1b-3048-4717-af97-b7fa92c0f260",
      "name": "Notizzettel15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        2760
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 620,
        "content": "## Technical Indicator API Merge \n\n**Binance API** for 6 indicators:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "b3abceb1-3605-4cca-8b4e-1f85f792fff2",
      "name": "Notizzettel1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1540,
        -500
      ],
      "parameters": {
        "color": 4,
        "height": 680,
        "content": "## Webhooks (15minData)\n\n• **Entry point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "2143ce8a-8746-4187-bc43-c9cc294da7b8",
      "name": "Notizzettel2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3300,
        1240
      ],
      "parameters": {
        "color": 4,
        "height": 680,
        "content": "## Webhooks (1hourData)\n\n• **Entry point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "d9150fb0-42b5-48e8-b123-e588672a653f",
      "name": "Notizzettel3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -100,
        1200
      ],
      "parameters": {
        "color": 4,
        "height": 680,
        "content": "## Webhooks (4hourData)\n\n• **Entry point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "50737e51-ef67-42af-bf15-ac228c750f4d",
      "name": "Notizzettel4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1680,
        2640
      ],
      "parameters": {
        "color": 4,
        "height": 680,
        "content": "## Webhooks (1dayData)\n\n• **Entry point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "61972dcf-2bd5-454e-880b-0d3e97c82f98",
      "name": "Notizzettel5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -480
      ],
      "parameters": {
        "color": 4,
        "width": 280,
        "height": 640,
        "content": "## Webhooks Respond (15minData)\n\n• **Exit point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "c545e86b-8f90-4e3b-8e72-25f56de0b214",
      "name": "Notizzettel6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2020,
        1200
      ],
      "parameters": {
        "color": 4,
        "width": 280,
        "height": 640,
        "content": "## Webhooks Respond (4hourData)\n\n• **Exit point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "9e838383-1c81-45c6-98f1-f735dc5789df",
      "name": "Notizzettel7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1140,
        1260
      ],
      "parameters": {
        "color": 4,
        "width": 280,
        "height": 640,
        "content": "## Webhooks Respond (1hourData)\n\n• **Exit point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "f05d2def-9593-46c5-862d-378a5d1f64d4",
      "name": "Notizzettel8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        2660
      ],
      "parameters": {
        "color": 4,
        "width": 280,
        "height": 640,
        "content": "## Webhooks Respond (1dayData)\n\n• **Exit point** for external workflows calling indicator batches\n\n• Orchestrates trigger for **indicator API pull + formatter nodes**\n\n• Returns **cleaned batch** back via Respond to Webhook node\n\n📎 Notes:\n\n• Must pass through correct **webhook** path\n\n• Responds after **merge with all 6 formatted outputs** per timeframe"
      },
      "typeVersion": 1
    },
    {
      "id": "ceb5f8e0-e51f-4554-a752-a319033c14c4",
      "name": "Notizzettel9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1160,
        -300
      ],
      "parameters": {
        "color": 6,
        "width": 260,
        "height": 520,
        "content": "## Binance API Calls 15m \n\n• Triggers **Binance API** for k-line data:\n\n\n• Each node pulls and returns **raw API data** for formatting"
      },
      "typeVersion": 1
    },
    {
      "id": "8e1aeb88-3232-4ea7-85c3-49a75097341b",
      "name": "Notizzettel10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2940,
        1480
      ],
      "parameters": {
        "color": 6,
        "width": 260,
        "height": 500,
        "content": "## Binance API Calls 1h \n\n• Triggers **Binance API** for k-line data:\n\n\n• Each node pulls and returns **raw API data** for formatting"
      },
      "typeVersion": 1
    },
    {
      "id": "53996473-bdd7-44d3-9060-96a2f072077c",
      "name": "Notizzettel11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        1420
      ],
      "parameters": {
        "color": 6,
        "width": 260,
        "height": 500,
        "content": "## Binance API Calls 4h \n\n• Triggers **Binance API** for k-line data:\n\n\n• Each node pulls and returns **raw API data** for formatting"
      },
      "typeVersion": 1
    },
    {
      "id": "6b138f55-5f0e-4844-a570-47919c8888b5",
      "name": "Notizzettel12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1340,
        2920
      ],
      "parameters": {
        "color": 6,
        "width": 260,
        "height": 420,
        "content": "## Binance API Calls 1d \n\n• Triggers **Binance API** for k-line data:\n\n\n• Each node pulls and returns **raw API data** for formatting"
      },
      "typeVersion": 1
    },
    {
      "id": "8d8bd17f-c437-4e57-a517-3b6eb44eeff7",
      "name": "Notizzettel16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2120,
        720
      ],
      "parameters": {
        "color": 2,
        "height": 1860,
        "content": "## Calculate Technical Indicators \n  \n\n• Ensures consistent **downstream format for merge and reasoning**\n\n📎 Notes:\n\n• **Format logic** is customized per indicator\n\n• Essential for **standardizing multi-timeframe data ingestion**"
      },
      "typeVersion": 1
    },
    {
      "id": "ee6e6788-7b9d-4d29-9cdd-0af5256289ef",
      "name": "Notizzettel17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -360,
        -860
      ],
      "parameters": {
        "color": 2,
        "width": 260,
        "height": 1720,
        "content": "## Calculate Technical Indicators \n  \n\n• Ensures consistent **downstream format for merge and reasoning**\n\n📎 Notes:\n\n• **Format logic** is customized per indicator\n\n• Essential for **standardizing multi-timeframe data ingestion**"
      },
      "typeVersion": 1
    },
    {
      "id": "e53bcd92-c603-43f3-b25f-0d7dd46afca0",
      "name": "Notizzettel18",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        720
      ],
      "parameters": {
        "color": 2,
        "width": 260,
        "height": 1820,
        "content": "## Calculate Technical Indicators \n  \n\n• Ensures consistent **downstream format for merge and reasoning**\n\n📎 Notes:\n\n• **Format logic** is customized per indicator\n\n• Essential for **standardizing multi-timeframe data ingestion**"
      },
      "typeVersion": 1
    },
    {
      "id": "7d27698c-bbc5-4faf-aa41-4c4332abb484",
      "name": "Notizzettel19",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -520,
        2180
      ],
      "parameters": {
        "color": 2,
        "width": 260,
        "height": 1800,
        "content": "## Calculate Technical Indicators \n  \n\n• Ensures consistent **downstream format for merge and reasoning**\n\n📎 Notes:\n\n• **Format logic** is customized per indicator\n\n• Essential for **standardizing multi-timeframe data ingestion**"
      },
      "typeVersion": 1
    },
    {
      "id": "309f22d1-aeb2-4d48-a7c4-deaad51070e3",
      "name": "Notizzettel20",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -820,
        -240
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 460,
        "content": "## API Merge \n\n**Binance API** for k-line Data to calculate:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "5a27535c-5bc9-4718-b895-5a8eccf37974",
      "name": "Notizzettel21",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2600,
        1560
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 420,
        "content": "## API Merge \n\n**Binance API** for k-line Data to calculate:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "52180c40-a1d3-4c41-aea6-73f62f7c98ee",
      "name": "Notizzettel22",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -980,
        2920
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 460,
        "content": "## API Merge \n\n**Binance API** for k-line Data to calculate:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "08697498-181c-417f-8027-8d6de1471bed",
      "name": "Notizzettel23",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        1480
      ],
      "parameters": {
        "color": 5,
        "width": 260,
        "height": 440,
        "content": "## API Merge \n\n**Binance API** for k-line Data to calculate:\n\n• **RSI, MACD, BBANDS, SMA, EMA, ADX**"
      },
      "typeVersion": 1
    },
    {
      "id": "773a2b4f-f716-41ab-ba18-1412e2dd50e5",
      "name": "Notizzettel24",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2800,
        -60
      ],
      "parameters": {
        "width": 1460,
        "height": 3020,
        "content": "# 🧪 Binance SM Indicators Webhook Tool – Documentation\n\nA backend webhook processor that performs **real-time indicator calculations** for 15m, 1h, 4h, and 1d Binance candlestick data. This workflow powers all the indicator sub-agents and is essential for maintaining modular and scalable logic across timeframes.\n\n---\n\n## 🎯 Purpose\n\n* Accepts HTTP POST requests from the 15m, 1h, 4h, or 1d sub-agents\n* Pulls raw OHLCV kline data for the symbol and interval\n* Calculates technical indicators:\n\n  * RSI, MACD, Bollinger Bands, SMA, EMA, ADX\n* Returns structured JSON with labeled values (e.g., `\"MACD_Cross\": \"Bullish\"`)\n\nThis tool ensures **centralized logic reuse** across all timeframe agents while improving speed and maintainability.\n\n---\n\n## ⚙️ Key Components\n\n| Node Name              | Description                                               |\n| ---------------------- | --------------------------------------------------------- |\n| `Webhook Trigger`      | Handles POST at `/15m-indicators`, `/1h-indicators`, etc. |\n| `Extract Symbol`       | Validates input payload (must include `symbol`)           |\n| `Fetch Binance Klines` | Gets latest 100 candles using `/api/v3/klines`            |\n| `RSI Calculator`       | Computes 14-period RSI from closing prices                |\n| `MACD Calculator`      | Computes 12, 26, 9 MACD with signal and histogram         |\n| `BBANDS Calculator`    | Calculates 20-period bands using std. deviation           |\n| `SMA/EMA Node`         | Computes 20-period simple and exponential MAs             |\n| `ADX Calculator`       | Computes 14-period trend strength (DI+/DI− included)      |\n| `Prepare Output`       | Returns JSON with all labeled indicators                  |\n\n---\n\n## 📥 Expected Input (from sub-agent)\n\n```json\n{\n  \"symbol\": \"BTCUSDT\"\n}\n```\n\nMust be a valid Binance Spot trading pair. Interval is auto-routed based on webhook endpoint:\n\n| Endpoint Path     | Interval |\n| ----------------- | -------- |\n| `/15m-indicators` | 15m      |\n| `/1h-indicators`  | 1h       |\n| `/4h-indicators`  | 4h       |\n| `/1d-indicators`  | 1d       |\n\n---\n\n## 📊 Sample Output JSON\n\n```json\n{\n  \"symbol\": \"BTCUSDT\",\n  \"interval\": \"4h\",\n  \"rsi\": 65.1,\n  \"macd\": {\n    \"value\": 24.3,\n    \"signal\": 21.7,\n    \"histogram\": 2.6,\n    \"cross\": \"Bullish\"\n  },\n  \"bb\": {\n    \"basis\": 62600,\n    \"upper\": 63500,\n    \"lower\": 61700,\n    \"status\": \"Expanding\"\n  },\n  \"sma\": 62000,\n  \"ema\": 62400,\n  \"adx\": {\n    \"value\": 32.8,\n    \"strength\": \"Strong\"\n  }\n}\n```\n\n---\n\n## 🧩 Use Case Scenarios\n\n| Scenario                                   | Result                                                   |\n| ------------------------------------------ | -------------------------------------------------------- |\n| 15m tool POSTs to `/15m-indicators`        | Webhook responds with 15m indicator set                  |\n| Financial Analyst Tool requests 1h signals | This workflow handles the actual math for each indicator |\n| 1d tool needs RSI + MACD + ADX             | Centralized logic avoids duplication across agents       |\n\n---\n\n## 🛠️ Installation Instructions\n\n### 1. Import & Activate\n\n* Import JSON into n8n\n* Ensure webhook is live and accessible\n* Test each endpoint (e.g., `/1h-indicators`) via POST\n\n### 2. Connect to Binance API\n\n* Public API: no credentials required\n* Ensure the HTTP request node for klines uses:\n\n  ```\n  https://api.binance.com/api/v3/klines\n  ```\n\n### 3. Link to Sub-Agent Workflows\n\n* 15m, 1h, 4h, 1d tools all depend on this for logic\n* Must POST `symbol` to the correct interval endpoint\n\n---\n\n## 📦 Example Workflow Integration\n\nAll the following tools rely on this:\n\n* `Binance SM 15min Indicators Tool`\n* `Binance SM 1hour Indicators Tool`\n* `Binance SM 4hour Indicators Tool`\n* `Binance SM 1day Indicators Tool`\n\nThis acts as the math engine behind each.\n\n---\n\n## 🔐 Licensing & Support\n\n🔗 **Don Jayamaha – LinkedIn**\n[http://linkedin.com/in/donjayamahajr](http://linkedin.com/in/donjayamahajr)\n\n© 2025 Treasurium Capital Limited Company. All rights reserved.\nThe design, architecture, and indicator calculation code within this system are proprietary. Reuse, resale, or modification without license is strictly prohibited.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "f7032997-bb7f-4e2a-9982-7ee18c95cfbe",
  "connections": {
    "61cb3bd7-60ba-4dcf-a080-146b7612a99b": {
      "main": [
        [
          {
            "node": "46b937d5-7338-457f-b2f0-eb85ac44a8bb",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "df6546be-3778-4fba-801a-870c0fe883c3": {
      "main": [
        [
          {
            "node": "5ea4756f-a20a-444c-8471-57d209457823",
            "type": "main",
            "index": 5
          }
        ]
      ]
    },
    "25ef2964-c9b1-4b9e-b6ce-3766b215d163": {
      "main": [
        [
          {
            "node": "5ea4756f-a20a-444c-8471-57d209457823",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "b0391e28-03a3-40f0-8182-72df0928ca2c": {
      "main": [
        [
          {
            "node": "5ea4756f-a20a-444c-8471-57d209457823",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "c50f7fa5-2707-47a4-9b32-1d582bf5d600": {
      "main": [
        [
          {
            "node": "5ea4756f-a20a-444c-8471-57d209457823",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "7301ba32-ab0c-4c80-b041-cd398f7daa2c": {
      "main": [
        [
          {
            "node": "af471a36-1107-4ccd-be14-efa06ba27330",
            "type": "main",
            "index": 5
          }
        ]
      ]
    },
    "2c598960-630c-4687-91c0-bfae10da1943": {
      "main": [
        [
          {
            "node": "5ea4756f-a20a-444c-8471-57d209457823",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "29121e2e-f6e2-4d42-8e5b-e8eea1e61ec8": {
      "main": [
        [
          {
            "node": "1be26316-3d08-4983-9d12-6bc36465e76e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "9ec777b1-eb23-43b0-8bb3-726adcc3418f": {
      "main": [
        [
          {
            "node": "d7d9bb31-6986-42e6-9f0c-7d7f00d66ee1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "bbb81a2a-b1c4-4c1e-80e9-a4990c45fae8": {
      "main": [
        [
          {
            "node": "20e58706-035f-46ef-b235-78fe468875bc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0b3278a6-5793-4d70-8954-70562c43a3eb": {
      "main": [
        [
          {
            "node": "660dfcb4-ed63-4c53-806b-cba96c105fee",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "83fa487a-1a67-4059-9007-d5298d8bb105": {
      "main": [
        [
          {
            "node": "af471a36-1107-4ccd-be14-efa06ba27330",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "17bf4e9f-7053-4a04-b439-3e23fe5cb7f9": {
      "main": [
        [
          {
            "node": "7940bc3f-c5f4-4b4b-b542-8737f17afbbf",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "ac84877a-75cf-4ef2-bad1-8d0f444ecead": {
      "main": [
        [
          {
            "node": "660dfcb4-ed63-4c53-806b-cba96c105fee",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "702e8bc8-d8f0-4fb4-b44f-fd19479c8b03": {
      "main": [
        [
          {
            "node": "af471a36-1107-4ccd-be14-efa06ba27330",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "d1c2fcc7-65ac-446c-8f59-c4cd8ac7d795": {
      "main": [
        [
          {
            "node": "7940bc3f-c5f4-4b4b-b542-8737f17afbbf",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "5b1f34b3-e675-46b1-a754-679080680058": {
      "main": [
        [
          {
            "node": "660dfcb4-ed63-4c53-806b-cba96c105fee",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "15bf5c3b-de07-412b-988c-f5c59fa7a06f": {
      "main": [
        [
          {
            "node": "af471a36-1107-4ccd-be14-efa06ba27330",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "93a115f7-e876-43f0-8ecb-70a47a06f41c": {
      "main": [
        [
          {
            "node": "7940bc3f-c5f4-4b4b-b542-8737f17afbbf",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "28dd6fb1-a161-49ed-9c14-740e88e2195d": {
      "main": [
        [
          {
            "node": "660dfcb4-ed63-4c53-806b-cba96c105fee",
            "type": "main",
            "index": 5
          }
        ]
      ]
    },
    "d61db420-9def-4ec1-bf8a-94afeabda186": {
      "main": [
        [
          {
            "node": "7940bc3f-c5f4-4b4b-b542-8737f17afbbf",
            "type": "main",
            "index": 5
          }
        ]
      ]
    },
    "a5da65c5-db5a-4f9d-a6cf-228f081ffb3c": {
      "main": [
        [
          {
            "node": "660dfcb4-ed63-4c53-806b-cba96c105fee",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "d16b7829-59ca-45ca-a5ae-4e315168c96d": {
      "main": [
        [
          {
            "node": "af471a36-1107-4ccd-be14-efa06ba27330",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "df32e55c-2dbc-44d6-8161-f70db651cfcd": {
      "main": [
        [
          {
            "node": "7940bc3f-c5f4-4b4b-b542-8737f17afbbf",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "46b937d5-7338-457f-b2f0-eb85ac44a8bb": {
      "main": [
        [
          {
            "node": "5b8c1049-bcf4-41d4-bba3-5f76e940bd3e",
            "type": "main",
            "index": 0
          },
          {
            "node": "b0391e28-03a3-40f0-8182-72df0928ca2c",
            "type": "main",
            "index": 0
          },
          {
            "node": "2c598960-630c-4687-91c0-bfae10da1943",
            "type": "main",
            "index": 0
          },
          {
            "node": "c50f7fa5-2707-47a4-9b32-1d582bf5d600",
            "type": "main",
            "index": 0
          },
          {
            "node": "25ef2964-c9b1-4b9e-b6ce-3766b215d163",
            "type": "main",
            "index": 0
          },
          {
            "node": "df6546be-3778-4fba-801a-870c0fe883c3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "660dfcb4-ed63-4c53-806b-cba96c105fee": {
      "main": [
        [
          {
            "node": "939665ce-7a50-4a6d-9b37-62ad800350b0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "af471a36-1107-4ccd-be14-efa06ba27330": {
      "main": [
        [
          {
            "node": "39e6b058-d800-4751-b75b-5e62be379c61",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7940bc3f-c5f4-4b4b-b542-8737f17afbbf": {
      "main": [
        [
          {
            "node": "8585a269-b70d-4af7-b264-db9f638c8a77",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1be26316-3d08-4983-9d12-6bc36465e76e": {
      "main": [
        [
          {
            "node": "6e9c7976-737b-4377-b412-633a1a67097b",
            "type": "main",
            "index": 0
          },
          {
            "node": "ac84877a-75cf-4ef2-bad1-8d0f444ecead",
            "type": "main",
            "index": 0
          },
          {
            "node": "a5da65c5-db5a-4f9d-a6cf-228f081ffb3c",
            "type": "main",
            "index": 0
          },
          {
            "node": "5b1f34b3-e675-46b1-a754-679080680058",
            "type": "main",
            "index": 0
          },
          {
            "node": "0b3278a6-5793-4d70-8954-70562c43a3eb",
            "type": "main",
            "index": 0
          },
          {
            "node": "28dd6fb1-a161-49ed-9c14-740e88e2195d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d7d9bb31-6986-42e6-9f0c-7d7f00d66ee1": {
      "main": [
        [
          {
            "node": "a2e9d933-47d7-47f5-8d49-224860d8600a",
            "type": "main",
            "index": 0
          },
          {
            "node": "702e8bc8-d8f0-4fb4-b44f-fd19479c8b03",
            "type": "main",
            "index": 0
          },
          {
            "node": "d16b7829-59ca-45ca-a5ae-4e315168c96d",
            "type": "main",
            "index": 0
          },
          {
            "node": "15bf5c3b-de07-412b-988c-f5c59fa7a06f",
            "type": "main",
            "index": 0
          },
          {
            "node": "83fa487a-1a67-4059-9007-d5298d8bb105",
            "type": "main",
            "index": 0
          },
          {
            "node": "7301ba32-ab0c-4c80-b041-cd398f7daa2c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "20e58706-035f-46ef-b235-78fe468875bc": {
      "main": [
        [
          {
            "node": "1675168b-97ab-4be4-92d2-734d075e2222",
            "type": "main",
            "index": 0
          },
          {
            "node": "d1c2fcc7-65ac-446c-8f59-c4cd8ac7d795",
            "type": "main",
            "index": 0
          },
          {
            "node": "df32e55c-2dbc-44d6-8161-f70db651cfcd",
            "type": "main",
            "index": 0
          },
          {
            "node": "93a115f7-e876-43f0-8ecb-70a47a06f41c",
            "type": "main",
            "index": 0
          },
          {
            "node": "17bf4e9f-7053-4a04-b439-3e23fe5cb7f9",
            "type": "main",
            "index": 0
          },
          {
            "node": "d61db420-9def-4ec1-bf8a-94afeabda186",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "91a05310-bd36-4ee0-8381-b3d16c3346be": {
      "main": [
        [
          {
            "node": "29121e2e-f6e2-4d42-8e5b-e8eea1e61ec8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "40fcbba9-489b-4452-baf8-9a41ef0f9b07": {
      "main": [
        [
          {
            "node": "9ec777b1-eb23-43b0-8bb3-726adcc3418f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "c4a718f8-f2d6-464b-8120-1e4067d760ad": {
      "main": [
        [
          {
            "node": "bbb81a2a-b1c4-4c1e-80e9-a4990c45fae8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b372f57e-917a-42d2-964c-25a296ea982d": {
      "main": [
        [
          {
            "node": "61cb3bd7-60ba-4dcf-a080-146b7612a99b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5ea4756f-a20a-444c-8471-57d209457823": {
      "main": [
        [
          {
            "node": "0a46ea7c-a85d-4fc6-b20c-7e65ab962ea6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5b8c1049-bcf4-41d4-bba3-5f76e940bd3e": {
      "main": [
        [
          {
            "node": "5ea4756f-a20a-444c-8471-57d209457823",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6e9c7976-737b-4377-b412-633a1a67097b": {
      "main": [
        [
          {
            "node": "660dfcb4-ed63-4c53-806b-cba96c105fee",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a2e9d933-47d7-47f5-8d49-224860d8600a": {
      "main": [
        [
          {
            "node": "af471a36-1107-4ccd-be14-efa06ba27330",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1675168b-97ab-4be4-92d2-734d075e2222": {
      "main": [
        [
          {
            "node": "7940bc3f-c5f4-4b4b-b542-8737f17afbbf",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Häufig gestellte Fragen

Wie verwende ich diesen Workflow?

Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.

Für welche Szenarien ist dieser Workflow geeignet?

Experte - Finanzen, Künstliche Intelligenz

Ist es kostenpflichtig?

Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.

Workflow-Informationen
Schwierigkeitsgrad
Experte
Anzahl der Nodes69
Kategorie2
Node-Typen6
Schwierigkeitsbeschreibung

Für fortgeschrittene Benutzer, komplexe Workflows mit 16+ Nodes

Autor
Don Jayamaha Jr

Don Jayamaha Jr

@don-the-gem-dealer

With 12 years of experience as a Blockchain Strategist and Web3 Architect, I specialize in bridging the gap between traditional industries and decentralized technologies. My expertise spans tokenized assets, crypto payment integrations, and blockchain-driven market solutions.

Externe Links
Auf n8n.io ansehen

Diesen Workflow teilen

Kategorien

Kategorien: 34