Skip to content

Instantly share code, notes, and snippets.

@vitqst
Created October 22, 2025 16:34
Show Gist options
  • Save vitqst/1017f9841f5d5fd27689ccec062de06a to your computer and use it in GitHub Desktop.
Save vitqst/1017f9841f5d5fd27689ccec062de06a to your computer and use it in GitHub Desktop.
{
"name": "Layer 1 - BB Trend Detection",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "*/1 * * * *"
}
]
}
},
"id": "schedule-trigger-1min",
"name": "Every 1 Minute",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [250, 300]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "symbol-config",
"name": "symbol",
"value": "BTCUSDT",
"type": "string"
},
{
"id": "bb-period",
"name": "bb_period",
"value": 20,
"type": "number"
},
{
"id": "bb-deviation",
"name": "bb_deviation",
"value": 2,
"type": "number"
},
{
"id": "volume-multiplier",
"name": "volume_multiplier",
"value": 1.5,
"type": "number"
}
]
}
},
"id": "config-node",
"name": "Configuration",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [470, 300]
},
{
"parameters": {
"method": "GET",
"url": "=https://api.binance.com/api/v3/klines",
"authentication": "none",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "symbol",
"value": "={{ $json.symbol }}"
},
{
"name": "interval",
"value": "1m"
},
{
"name": "limit",
"value": "50"
}
]
},
"options": {}
},
"id": "binance-1m-klines",
"name": "Binance 1m Klines",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [690, 300]
},
{
"parameters": {
"mode": "runOnceForAllItems",
"language": "javaScript",
"jsCode": "// Get configuration from previous node\nconst config = $input.first().json;\nconst symbol = config.symbol;\nconst bb_period = config.bb_period || 20;\nconst bb_deviation = config.bb_deviation || 2;\nconst volume_multiplier = config.volume_multiplier || 1.5;\n\n// Get klines data from Binance\nconst klines = $input.item.json;\n\n// Parse klines data\nconst candles = klines.map(k => ({\n openTime: k[0],\n open: parseFloat(k[1]),\n high: parseFloat(k[2]),\n low: parseFloat(k[3]),\n close: parseFloat(k[4]),\n volume: parseFloat(k[5]),\n closeTime: k[6]\n}));\n\n// Calculate Bollinger Bands\nfunction calculateBollingerBands(prices, period, deviation) {\n const result = [];\n \n for (let i = period - 1; i < prices.length; i++) {\n // Calculate SMA (Simple Moving Average)\n const slice = prices.slice(i - period + 1, i + 1);\n const sma = slice.reduce((sum, val) => sum + val, 0) / period;\n \n // Calculate Standard Deviation\n const squaredDiffs = slice.map(val => Math.pow(val - sma, 2));\n const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / period;\n const stdDev = Math.sqrt(variance);\n \n // Calculate bands\n const upperBand = sma + (deviation * stdDev);\n const lowerBand = sma - (deviation * stdDev);\n const middleBand = sma;\n \n result.push({\n index: i,\n upperBand,\n middleBand,\n lowerBand,\n stdDev\n });\n }\n \n return result;\n}\n\n// Extract close prices\nconst closePrices = candles.map(c => c.close);\nconst volumes = candles.map(c => c.volume);\n\n// Calculate BB values\nconst bbValues = calculateBollingerBands(closePrices, bb_period, bb_deviation);\n\n// Get current (latest) values\nconst currentCandle = candles[candles.length - 1];\nconst previousCandle = candles[candles.length - 2];\nconst currentBB = bbValues[bbValues.length - 1];\nconst previousBB = bbValues[bbValues.length - 2];\n\n// Calculate average volume\nconst recentVolumes = volumes.slice(-bb_period);\nconst avgVolume = recentVolumes.reduce((sum, v) => sum + v, 0) / bb_period;\nconst volumeSurge = currentCandle.volume > (avgVolume * volume_multiplier);\n\n// Detect BB cross and trend\nlet signal = null;\nlet crossType = null;\n\n// Check for LONG signal (price crosses below lower band)\nif (previousCandle.close > previousBB.lowerBand && \n currentCandle.close < currentBB.lowerBand &&\n volumeSurge) {\n signal = 'LONG';\n crossType = 'BB_LOWER_CROSS';\n}\n\n// Check for SHORT signal (price crosses above upper band)\nif (previousCandle.close < previousBB.upperBand && \n currentCandle.close > currentBB.upperBand &&\n volumeSurge) {\n signal = 'SHORT';\n crossType = 'BB_UPPER_CROSS';\n}\n\n// Return result\nreturn [{\n json: {\n layer: 1,\n symbol: symbol,\n timeframe: '1m',\n timestamp: currentCandle.closeTime,\n signal: signal,\n crossType: crossType,\n price: {\n current: currentCandle.close,\n open: currentCandle.open,\n high: currentCandle.high,\n low: currentCandle.low\n },\n bollingerBands: {\n upper: currentBB.upperBand,\n middle: currentBB.middleBand,\n lower: currentBB.lowerBand,\n stdDev: currentBB.stdDev\n },\n volume: {\n current: currentCandle.volume,\n average: avgVolume,\n multiplier: (currentCandle.volume / avgVolume).toFixed(2),\n surge: volumeSurge\n },\n conditions: {\n bb_cross: signal !== null,\n volume_confirmed: volumeSurge,\n ready_for_layer2: signal !== null && volumeSurge\n }\n }\n}];"
},
"id": "calculate-bb-volume",
"name": "Calculate BB + Volume",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [910, 300]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "signal-exists",
"leftValue": "={{ $json.signal }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty"
}
},
{
"id": "volume-confirmed",
"leftValue": "={{ $json.volume.surge }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "check-signal-conditions",
"name": "Check Signal Conditions",
"type": "n8n-nodes-base.if",
"typeVersion": 2.1,
"position": [1130, 300]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "layer1-complete",
"name": "layer1_status",
"value": "SIGNAL_DETECTED",
"type": "string"
},
{
"id": "proceed-layer2",
"name": "proceed_to_layer2",
"value": true,
"type": "boolean"
},
{
"id": "alert-message",
"name": "alert_message",
"value": "=Layer 1: {{ $json.signal }} signal detected for {{ $json.symbol }} at {{ $json.price.current }}. BB Cross: {{ $json.crossType }}. Volume surge: {{ $json.volume.multiplier }}x average.",
"type": "string"
}
]
}
},
"id": "signal-detected",
"name": "Signal Detected ✅",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [1350, 200]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "no-signal",
"name": "layer1_status",
"value": "NO_SIGNAL",
"type": "string"
},
{
"id": "no-proceed",
"name": "proceed_to_layer2",
"value": false,
"type": "boolean"
},
{
"id": "no-alert",
"name": "alert_message",
"value": "=Layer 1: No signal for {{ $json.symbol }}. Current price: {{ $json.price.current }}",
"type": "string"
}
]
}
},
"id": "no-signal-detected",
"name": "No Signal ❌",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [1350, 400]
}
],
"connections": {
"Every 1 Minute": {
"main": [
[
{
"node": "Configuration",
"type": "main",
"index": 0
}
]
]
},
"Configuration": {
"main": [
[
{
"node": "Binance 1m Klines",
"type": "main",
"index": 0
}
]
]
},
"Binance 1m Klines": {
"main": [
[
{
"node": "Calculate BB + Volume",
"type": "main",
"index": 0
}
]
]
},
"Calculate BB + Volume": {
"main": [
[
{
"node": "Check Signal Conditions",
"type": "main",
"index": 0
}
]
]
},
"Check Signal Conditions": {
"main": [
[
{
"node": "Signal Detected ✅",
"type": "main",
"index": 0
}
],
[
{
"node": "No Signal ❌",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment