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

491 lines • 22.4 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.CandlestickPatternDetector = void 0; const n8n_workflow_1 = require("n8n-workflow"); const technicalindicators = __importStar(require("technicalindicators")); class CandlestickPatternDetector { constructor() { this.description = { displayName: 'Candlestick Pattern Detector', name: 'candlestickPatternDetector', icon: 'file:icon.svg', group: ['transform'], version: 1, description: 'Detect candlestick patterns using technical analysis', defaults: { name: 'Candlestick Pattern Detector', }, inputs: ["main"], outputs: ["main"], properties: [ { displayName: 'Patterns', name: 'patterns', type: 'multiOptions', options: [ { name: 'Abandoned Baby', value: 'abandonedbaby', description: 'Strong reversal signal', }, { name: 'Bearish Engulfing', value: 'bearishengulfingpattern', description: 'Strong bearish reversal signal', }, { name: 'Bearish Hammer Stick', value: 'bearishhammerstick', description: 'Bearish hammer candlestick', }, { name: 'Bearish Harami', value: 'bearishharami', description: 'Potential bearish reversal', }, { name: 'Bearish Harami Cross', value: 'bearishharamicross', description: 'Bearish reversal with doji', }, { name: 'Bearish Inverted Hammer', value: 'bearishinvertedhammerstick', }, { name: 'Bearish Marubozu', value: 'bearishmarubozu', description: 'Strong bearish trend continuation', }, { name: 'Bearish Spinning Top', value: 'bearishspinningtop', description: 'Indecision with bearish bias', }, { name: 'Bullish Engulfing', value: 'bullishengulfingpattern', description: 'Strong bullish reversal signal', }, { name: 'Bullish Hammer Stick', value: 'bullishhammerstick', description: 'Bullish hammer candlestick', }, { name: 'Bullish Harami', value: 'bullishharami', description: 'Potential bullish reversal', }, { name: 'Bullish Harami Cross', value: 'bullishharamicross', description: 'Bullish reversal with doji', }, { name: 'Bullish Inverted Hammer', value: 'bullishinvertedhammerstick', }, { name: 'Bullish Marubozu', value: 'bullishmarubozu', description: 'Strong bullish trend continuation', }, { name: 'Bullish Spinning Top', value: 'bullishspinningtop', description: 'Indecision with bullish bias', }, { name: 'Dark Cloud Cover', value: 'darkcloudcover', description: 'Bearish reversal pattern', }, { name: 'Doji', value: 'doji', description: 'Indicates indecision, potential trend reversal', }, { name: 'Downside Tasuki Gap', value: 'downsidetasukigap', description: 'Bearish continuation pattern', }, { name: 'Dragonfly Doji', value: 'dragonflydoji', description: 'Bullish doji pattern', }, { name: 'Evening Doji Star', value: 'eveningdojistar', description: 'Bearish reversal with doji', }, { name: 'Evening Star', value: 'eveningstar', description: 'Strong bearish reversal pattern', }, { name: 'Gravestone Doji', value: 'gravestonedoji', description: 'Bearish doji pattern', }, { name: 'Hammer Pattern', value: 'hammerpattern', description: 'Bullish reversal signal at bottoms', }, { name: 'Hammer Pattern (Unconfirmed)', value: 'hammerpatternunconfirmed', description: 'Unconfirmed hammer pattern', }, { name: 'Hanging Man', value: 'hangingman', description: 'Bearish reversal signal at tops', }, { name: 'Hanging Man (Unconfirmed)', value: 'hangingmanunconfirmed', description: 'Unconfirmed hanging man pattern', }, { name: 'Morning Doji Star', value: 'morningdojistar', description: 'Bullish reversal with doji', }, { name: 'Morning Star', value: 'morningstar', description: 'Strong bullish reversal pattern', }, { name: 'Shooting Star', value: 'shootingstar', description: 'Bearish reversal signal at tops', }, { name: 'Shooting Star (Unconfirmed)', value: 'shootingstarunconfirmed', description: 'Unconfirmed shooting star pattern', }, ], default: ['doji', 'hammerpattern', 'bullishengulfingpattern', 'bearishengulfingpattern'], noDataExpression: true, }, { displayName: 'OHLCV Data', name: 'ohlcvData', type: 'string', default: '', description: 'OHLCV data as JSON array [[open, high, low, close, volume], ...]', displayOptions: { show: { '/dataSource': ['manual'], }, }, }, { displayName: 'Data Source', name: 'dataSource', type: 'options', options: [ { name: 'Manual Input', value: 'manual', description: 'Enter OHLCV data manually', }, { name: 'From Input Data', value: 'input', description: 'Use OHLCV data from previous node', }, ], default: 'manual', }, { displayName: 'Input Data Field', name: 'inputDataField', type: 'string', default: 'ohlcv', description: 'Field name containing OHLCV data in input items', displayOptions: { show: { '/dataSource': ['input'], }, }, }, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add Option', default: {}, options: [ { displayName: 'Return Type', name: 'returnType', type: 'options', options: [ { name: 'Pattern Results Only', value: 'patterns', description: 'Return only pattern detection results', }, { name: 'With OHLCV Data', value: 'withData', description: 'Return patterns with original OHLCV data', }, { name: 'With Metadata', value: 'metadata', description: 'Return patterns with calculation metadata', }, ], default: 'patterns', }, { displayName: 'Include Pattern Strength', name: 'includeStrength', type: 'boolean', default: false, description: 'Whether to include pattern strength indicators (where available)', }, { displayName: 'Minimum Data Points', name: 'minDataPoints', type: 'number', default: 5, description: 'Minimum number of data points required for pattern detection', }, ], }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const patterns = this.getNodeParameter('patterns', i); const dataSource = this.getNodeParameter('dataSource', i); const options = this.getNodeParameter('options', i, {}); let ohlcvData; if (dataSource === 'manual') { const ohlcvDataString = this.getNodeParameter('ohlcvData', i); 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'}`); } } else { const inputDataField = this.getNodeParameter('inputDataField', i); const inputData = items[i].json[inputDataField]; if (!inputData) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Input data field '${inputDataField}' not found in item ${i}`); } if (typeof inputData === 'string') { try { ohlcvData = JSON.parse(inputData); } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to parse OHLCV data from input field: ${error instanceof Error ? error.message : 'Unknown error'}`); } } else { ohlcvData = inputData; } } if (!Array.isArray(ohlcvData) || ohlcvData.length === 0) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'OHLCV data must be a non-empty array'); } const minDataPoints = options.minDataPoints || 5; if (ohlcvData.length < minDataPoints) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Insufficient data points. Required: ${minDataPoints}, Provided: ${ohlcvData.length}`); } const opens = ohlcvData.map((candle) => candle[0]); const highs = ohlcvData.map((candle) => candle[1]); const lows = ohlcvData.map((candle) => candle[2]); const closes = ohlcvData.map((candle) => candle[3]); const volumes = ohlcvData.map((candle) => candle[4] || 0); const inputData = { open: opens, high: highs, low: lows, close: closes, volume: volumes, }; const patternResults = {}; const detectedPatterns = []; const patternLocations = {}; for (const pattern of patterns) { try { const patternFunction = technicalindicators[pattern]; if (typeof patternFunction === 'function') { const result = patternFunction(inputData); patternResults[pattern] = result; if (typeof result === 'boolean' && result) { detectedPatterns.push(pattern); patternLocations[pattern] = { start: ohlcvData.length - 1, end: ohlcvData.length - 1, location: 'last', }; } else if (Array.isArray(result) && result.length > 0) { const locations = CandlestickPatternDetector.findPatternLocations(result, pattern); if (locations.length > 0) { detectedPatterns.push(pattern); patternLocations[pattern] = { locations, totalOccurrences: locations.length, }; } } } else { patternResults[pattern] = { error: `Pattern function '${pattern}' not found`, }; } } catch (error) { patternResults[pattern] = { error: error instanceof Error ? error.message : 'Unknown error', }; } } const outputData = { detectedPatterns, patternResults, patternLocations, totalPatternsChecked: patterns.length, patternsDetected: detectedPatterns.length, }; if (options.returnType === 'withData') { outputData.ohlcvData = ohlcvData; } if (options.returnType === 'metadata') { outputData.metadata = { inputLength: ohlcvData.length, patternsChecked: patterns, detectedPatterns, calculationTimestamp: new Date().toISOString(), dataSource, }; } if (options.includeStrength) { outputData.patternStrength = CandlestickPatternDetector.calculatePatternStrength(detectedPatterns, inputData); } 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]; } static findPatternLocations(result, pattern) { const locations = []; for (let i = 0; i < result.length; i++) { if (result[i] === true) { let patternLength = 1; if (['morningstar', 'eveningstar', 'morningdojistar', 'eveningdojistar', 'abandonedbaby'].includes(pattern)) { patternLength = 3; } else if (['bullishengulfingpattern', 'bearishengulfingpattern', 'darkcloudcover', 'downsidetasukigap'].includes(pattern)) { patternLength = 2; } else if (['bullishharami', 'bearishharami', 'bullishharamicross', 'bearishharamicross'].includes(pattern)) { patternLength = 2; } const start = Math.max(0, i - patternLength + 1); const end = i; locations.push({ start, end, patternLength, location: `${start}-${end}`, candleIndex: i, }); } } return locations; } static calculatePatternStrength(detectedPatterns, inputData) { const strength = {}; const { open, high, low, close } = inputData; const lastIndex = close.length - 1; const bodySize = Math.abs(close[lastIndex] - open[lastIndex]); const totalRange = high[lastIndex] - low[lastIndex]; const bodyRatio = totalRange > 0 ? bodySize / totalRange : 0; strength.bodyRatio = bodyRatio; strength.bodySize = bodySize; strength.totalRange = totalRange; strength.patternCount = detectedPatterns.length; strength.strongPatterns = detectedPatterns.filter((pattern) => [ 'morningstar', 'eveningstar', 'bullishengulfingpattern', 'bearishengulfingpattern', 'abandonedbaby', ].includes(pattern)).length; if (inputData.volume && inputData.volume.length > 0) { const avgVolume = inputData.volume.slice(-5).reduce((a, b) => a + b, 0) / 5; const currentVolume = inputData.volume[lastIndex]; strength.volumeRatio = avgVolume > 0 ? currentVolume / avgVolume : 1; } return strength; } } exports.CandlestickPatternDetector = CandlestickPatternDetector; //# sourceMappingURL=CandlestickPatternDetector.node.js.map