@thuantan2060/technicalindicators
Version:
Techincal Indicators written in javascript
225 lines (198 loc) • 8.88 kB
JavaScript
;
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