UNPKG

@dendaio/n8n-nodes-collection

Version:

🚀 Comprehensive n8n node collection for financial analysis and automation. Features 55+ technical indicators (RSI, MACD, Bollinger Bands), 32+ candlestick patterns (Doji, Hammer, Engulfing), automated trading strategies, RSS feed processing, and OAuth2 v

354 lines • 16.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SupportResistanceDetector = void 0; const n8n_workflow_1 = require("n8n-workflow"); const ti = __importStar(require("technicalindicators")); class SupportResistanceDetector { constructor() { this.description = { displayName: 'Support Resistance Detector', name: 'supportResistanceDetector', icon: 'file:icon.svg', group: ['transform'], version: 1, description: 'Detect support and resistance levels using pivot points and swing highs/lows', defaults: { name: 'Support Resistance Detector', }, inputs: ["main"], outputs: ["main"], properties: [ { displayName: 'Detection Methods', name: 'detectionMethods', type: 'multiOptions', options: [ { name: 'Pivot Points', value: 'pivotPoints', description: 'Calculate pivot points (PP, R1, R2, R3, S1, S2, S3)', }, { name: 'Swing Highs and Lows', value: 'swingHighsLows', description: 'Detect swing highs and swing lows for support/resistance levels', }, { name: 'Fibonacci Retracement', value: 'fibonacciRetracement', description: 'Calculate Fibonacci retracement levels', }, ], default: ['pivotPoints', 'swingHighsLows'], noDataExpression: true, }, { displayName: 'OHLCV Data', name: 'ohlcvData', type: 'string', default: '={{ $json.ohlcv }}', description: 'JSON string containing OHLCV data array', }, { displayName: 'Swing Period', name: 'swingPeriod', type: 'number', default: 5, description: 'Period for swing high/low detection', displayOptions: { show: { detectionMethods: ['swingHighsLows'], }, }, }, { displayName: 'Fibonacci Start Price', name: 'fibonacciStart', type: 'number', default: 0, description: 'Starting price for Fibonacci retracement calculation (0 = use lowest low)', displayOptions: { show: { detectionMethods: ['fibonacciRetracement'], }, }, }, { displayName: 'Fibonacci End Price', name: 'fibonacciEnd', type: 'number', default: 0, description: 'Ending price for Fibonacci retracement calculation (0 = use highest high)', displayOptions: { show: { detectionMethods: ['fibonacciRetracement'], }, }, }, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add Option', default: {}, options: [ { displayName: 'Return Type', name: 'returnType', type: 'options', options: [ { name: 'Values Only', value: 'values', description: 'Return only the calculated values', }, { name: 'With Metadata', value: 'metadata', description: 'Return values with additional metadata', }, ], default: 'values', }, { displayName: 'Include Current Levels', name: 'includeCurrent', type: 'boolean', default: true, description: 'Whether to include current support/resistance levels in output', }, ], }, ], }; } async execute() { var _a, _b, _c, _d; const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const detectionMethods = this.getNodeParameter('detectionMethods', i); const ohlcvDataString = this.getNodeParameter('ohlcvData', i); const options = this.getNodeParameter('options', i, {}); let ohlcvData; try { ohlcvData = JSON.parse(ohlcvDataString); } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to parse OHLCV data: ${error instanceof Error ? error.message : 'Unknown error'}`); } if (!Array.isArray(ohlcvData) || ohlcvData.length === 0) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'OHLCV data must be a non-empty array'); } const highs = ohlcvData.map((candle) => candle[1]); const lows = ohlcvData.map((candle) => candle[2]); const closes = ohlcvData.map((candle) => candle[3]); const results = {}; if (detectionMethods.includes('pivotPoints')) { const pivotPoints = []; for (let j = 0; j < closes.length; j++) { const high = highs[j]; const low = lows[j]; const close = closes[j]; const pp = (high + low + close) / 3; const r1 = 2 * pp - low; const s1 = 2 * pp - high; const r2 = pp + (high - low); const s2 = pp - (high - low); const r3 = high + 2 * (pp - low); const s3 = low - 2 * (high - pp); pivotPoints.push({ index: j, high, low, close, pp: Number(pp.toFixed(4)), r1: Number(r1.toFixed(4)), r2: Number(r2.toFixed(4)), r3: Number(r3.toFixed(4)), s1: Number(s1.toFixed(4)), s2: Number(s2.toFixed(4)), s3: Number(s3.toFixed(4)), }); } results.pivotPoints = { levels: pivotPoints, current: pivotPoints[pivotPoints.length - 1] || null, }; } if (detectionMethods.includes('swingHighsLows')) { const swingPeriod = this.getNodeParameter('swingPeriod', i, 5); const swingHighs = []; const swingLows = []; const rollingHighs = ti.highest({ values: highs, period: swingPeriod }); const rollingLows = ti.lowest({ values: lows, period: swingPeriod }); for (let j = swingPeriod - 1; j < highs.length; j++) { if (highs[j] === rollingHighs[j - (swingPeriod - 1)]) { let isSwingHigh = true; for (let k = Math.max(0, j - swingPeriod); k < Math.min(highs.length, j + swingPeriod + 1); k++) { if (k !== j && highs[k] >= highs[j]) { isSwingHigh = false; break; } } if (isSwingHigh) { swingHighs.push({ index: j, price: highs[j], type: 'resistance', }); } } } for (let j = swingPeriod - 1; j < lows.length; j++) { if (lows[j] === rollingLows[j - (swingPeriod - 1)]) { let isSwingLow = true; for (let k = Math.max(0, j - swingPeriod); k < Math.min(lows.length, j + swingPeriod + 1); k++) { if (k !== j && lows[k] <= lows[j]) { isSwingLow = false; break; } } if (isSwingLow) { swingLows.push({ index: j, price: lows[j], type: 'support', }); } } } const allLevels = [...swingHighs, ...swingLows].sort((a, b) => a.index - b.index); const currentSwingHigh = swingHighs.length > 0 ? swingHighs[swingHighs.length - 1] : null; const currentSwingLow = swingLows.length > 0 ? swingLows[swingLows.length - 1] : null; results.swingHighsLows = { swingHighs, swingLows, allLevels, period: swingPeriod, current: { latestSwingHigh: currentSwingHigh, latestSwingLow: currentSwingLow, nearestResistance: (currentSwingHigh === null || currentSwingHigh === void 0 ? void 0 : currentSwingHigh.price) || null, nearestSupport: (currentSwingLow === null || currentSwingLow === void 0 ? void 0 : currentSwingLow.price) || null, }, }; } if (detectionMethods.includes('fibonacciRetracement')) { const fibonacciStart = this.getNodeParameter('fibonacciStart', i, 0); const fibonacciEnd = this.getNodeParameter('fibonacciEnd', i, 0); const actualStart = fibonacciStart === 0 ? Math.min(...lows) : fibonacciStart; const actualEnd = fibonacciEnd === 0 ? Math.max(...highs) : fibonacciEnd; const retracementLevels = ti.fibonacciretracement(actualStart, actualEnd); const levelNames = [ '0%', '23.6%', '38.2%', '50%', '61.8%', '78.6%', '100%', '127.2%', '161.8%', '261.8%', '423.6%', ]; const levels = levelNames.map((name, index) => ({ level: name, price: Number(retracementLevels[index].toFixed(4)), type: index <= 6 ? 'retracement' : 'extension', })); const currentPrice = closes[closes.length - 1]; const nearestRetracement = levels .filter((level) => level.type === 'retracement') .reduce((nearest, level) => { if (!nearest) return level; return Math.abs(level.price - currentPrice) < Math.abs(nearest.price - currentPrice) ? level : nearest; }, null); results.fibonacciRetracement = { startPrice: actualStart, endPrice: actualEnd, levels, retracementLevels: levels.filter((level) => level.type === 'retracement'), extensionLevels: levels.filter((level) => level.type === 'extension'), current: { currentPrice, nearestRetracementLevel: nearestRetracement, keyLevels: { support23_6: ((_a = levels.find((level) => level.level === '23.6%')) === null || _a === void 0 ? void 0 : _a.price) || null, resistance38_2: ((_b = levels.find((level) => level.level === '38.2%')) === null || _b === void 0 ? void 0 : _b.price) || null, support50_0: ((_c = levels.find((level) => level.level === '50%')) === null || _c === void 0 ? void 0 : _c.price) || null, resistance61_8: ((_d = levels.find((level) => level.level === '61.8%')) === null || _d === void 0 ? void 0 : _d.price) || null, }, }, }; } const outputData = { detectionMethods, results, }; if (options.returnType === 'metadata') { outputData.metadata = { inputLength: ohlcvData.length, methodsCount: detectionMethods.length, calculationTimestamp: new Date().toISOString(), }; } returnData.push({ json: outputData, }); } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { error: error instanceof Error ? error.message : 'Unknown error occurred', }, }); continue; } throw error; } } return [returnData]; } } exports.SupportResistanceDetector = SupportResistanceDetector; //# sourceMappingURL=SupportResistanceDetector.node.js.map