UNPKG

@thuantan2060/technicalindicators

Version:
225 lines (198 loc) 8.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.DEFAULT_HANGING_MAN_UNCONFIRMED_CONFIG = void 0; exports.hangingmanunconfirmed = hangingmanunconfirmed; var _HangingMan = _interopRequireWildcard(require("./HangingMan")); var _BearishHammerStick = require("./BearishHammerStick"); var _BullishHammerStick = require("./BullishHammerStick"); var _AverageGain = require("../Utils/AverageGain"); var _AverageLoss = require("../Utils/AverageLoss"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * Configuration interface for HangingManUnconfirmed pattern. * Extends HangingMan configuration. */ /** * Default configuration for HangingManUnconfirmed pattern. */ const DEFAULT_HANGING_MAN_UNCONFIRMED_CONFIG = exports.DEFAULT_HANGING_MAN_UNCONFIRMED_CONFIG = { ..._HangingMan.DEFAULT_HANGING_MAN_CONFIG, ..._BullishHammerStick.DEFAULT_BULLISH_HAMMER_CONFIG, ..._BearishHammerStick.DEFAULT_BEARISH_HAMMER_STICK_CONFIG }; class HangingManUnconfirmed extends _HangingMan.default { config; constructor(config) { const finalConfig = { ...DEFAULT_HANGING_MAN_UNCONFIRMED_CONFIG, ...config }; super(finalConfig); this.name = 'HangingManUnconfirmed'; this.requiredCount = 4; // Reduced from 5 since no confirmation needed this.config = finalConfig; } logic(data) { // Validate data integrity first for (let i = 0; i < data.close.length; i++) { if (!this.validateOHLC(data.open[i], data.high[i], data.low[i], data.close[i])) { return false; } } // Check for upward trend and hammer pattern without confirmation let isPattern = this.upwardTrend(data, false); isPattern = isPattern && this.includesHammer(data, false); return isPattern; } upwardTrend(data, confirm = false) { // For unconfirmed pattern, we need an uptrend in the first 3 candles (indices 0-2) // The 4th candle (index 3) is the potential hanging man // Ensure we have enough data if (data.close.length < 4) { return false; } // Analyze trends in closing prices of the first three candlesticks (indices 0-2) let trendData = data.close.slice(0, 3); // Check for overall upward movement let firstClose = trendData[0]; let lastClose = trendData[trendData.length - 1]; // Must have overall upward movement if (lastClose <= firstClose) { return false; } // Calculate gains and losses for trend analysis let gains = (0, _AverageGain.averagegain)({ values: trendData, period: trendData.length - 1 }); let losses = (0, _AverageLoss.averageloss)({ values: trendData, period: trendData.length - 1 }); // Get the latest values from the arrays let latestGain = gains.length > 0 ? gains[gains.length - 1] : 0; let latestLoss = losses.length > 0 ? losses[losses.length - 1] : 0; // Additional validation: ensure there's meaningful price movement let priceRange = Math.max(...trendData) - Math.min(...trendData); let totalMovement = Math.abs(lastClose - firstClose); // Use direct calculation instead of removed utility function let minMovement = Math.max(priceRange * 0.01, 0.001 * 10); // Replaces: this.getAbsoluteMinimum() * 10 // Upward trend: more gains than losses, and significant upward movement return latestGain > latestLoss && totalMovement >= minMovement; } includesHammer(data, confirm = false) { // For unconfirmed pattern, check the last candle (index 3) for hammer pattern let hammerIndex = 3; // Ensure we have the required data if (data.close.length < 4) { return false; } // Create data for just the potential hammer candle let hammerData = { open: [data.open[hammerIndex]], close: [data.close[hammerIndex]], low: [data.low[hammerIndex]], high: [data.high[hammerIndex]] }; // Use the updated hammer functions with config objects let isBearishHammer = (0, _BearishHammerStick.bearishhammerstick)(hammerData, this.config); let isBullishHammer = (0, _BullishHammerStick.bullishhammerstick)(hammerData, this.config); // Also check using our custom hammer-like detection for more flexibility let isCustomHammer = this.isCustomHammerLike(data.open[hammerIndex], data.high[hammerIndex], data.low[hammerIndex], data.close[hammerIndex]); return isBearishHammer || isBullishHammer || isCustomHammer; } // Custom hammer-like detection that's very lenient for hanging man pattern isCustomHammerLike(open, high, low, close) { // Basic OHLC validation if (!this.validateOHLC(open, high, low, close)) { return false; } let bodySize = Math.abs(close - open); let lowerShadow = Math.min(open, close) - low; let upperShadow = high - Math.max(open, close); let totalRange = high - low; // Ensure we have a meaningful range if (totalRange <= 0) { return false; } // Simplified approach focusing on key characteristics let score = 0; // Primary criteria: Either shadow should be meaningful if (lowerShadow >= totalRange * 0.08 || upperShadow >= totalRange * 0.08) { score += 2; // Base points for having shadows } // Bonus for traditional hammer (shadow >= body) if (lowerShadow >= bodySize || upperShadow >= bodySize) { score += 3; // Strong bonus for classic hammer shape } // Bonus for reasonable body size if (bodySize <= totalRange * 0.70) { score += 1; } // Bonus for small body (doji-like patterns) if (bodySize <= totalRange * 0.2) { score += 2; } // Bonus for having both shadows (balanced) if (lowerShadow > 0 && upperShadow > 0) { score += 1; // Extra bonus for perfectly balanced shadows let shadowDifference = Math.abs(lowerShadow - upperShadow); if (shadowDifference <= totalRange * 0.01) { // Very similar shadows score += 1; } } // Special case: For very small ranges, be more lenient // Use direct calculation instead of removed utility function if (totalRange <= 1.0 * this.scale) { // Replaces: this.getMovementThreshold() * 10 score += 1; } // Need at least 4 points to be considered a hammer-like pattern return score >= 4; } } /** * Detects HangingManUnconfirmed candlestick pattern in the provided stock data. * * A HangingManUnconfirmed is a bearish reversal pattern that appears at the end of an uptrend. * Unlike the confirmed version, this pattern doesn't require confirmation from the next candle. * It consists of: * 1. An uptrend in the first 3 candles * 2. A hammer-like candle (small body with long lower shadow) at the 4th position * * This pattern suggests potential bearish reversal but is less reliable than the confirmed version. * * @param data - Stock data containing OHLC values for at least 4 periods * @param config - Configuration object for pattern detection * @param config.scale - Scale parameter for approximateEqual function precision (default: 0.001) * @param config.minimumThreshold - Minimum threshold for absolute measurements (default: 0.01) * @param config.absoluteMinimum - Absolute minimum for very small values (default: 0.001) * @param config.movementThresholdBase - Movement threshold multiplier for confirmation (default: 1.0) * @param config.movementThresholdScale - Movement threshold scale factor (default: 0.3) * @returns True if HangingManUnconfirmed pattern is detected, false otherwise * * @example * ```typescript * // Using default configuration * const hasHangingManUnconfirmedPattern = hangingmanunconfirmed(stockData); * * // Using custom configuration * const hasHangingManUnconfirmedPattern = hangingmanunconfirmed(stockData, { * scale: 0.002, * minimumThreshold: 0.02, * movementThresholdBase: 1.5 * }); * * // Backward compatibility with scale parameter * const hasHangingManUnconfirmedPattern = hangingmanunconfirmed(stockData, { scale: 0.002 }); * ``` */ exports.default = HangingManUnconfirmed; function hangingmanunconfirmed(data, config = DEFAULT_HANGING_MAN_UNCONFIRMED_CONFIG) { return new HangingManUnconfirmed(config).hasPattern(data); } //# sourceMappingURL=HangingManUnconfirmed.js.map