@thuantan2060/technicalindicators
Version:
Techincal Indicators written in javascript
209 lines (183 loc) • 8.63 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.DEFAULT_HANGING_MAN_CONFIG = void 0;
exports.hangingman = hangingman;
var _CandlestickFinder = _interopRequireWildcard(require("./CandlestickFinder"));
var _AverageLoss = require("../Utils/AverageLoss");
var _AverageGain = require("../Utils/AverageGain");
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 HangingMan pattern.
* Includes thresholds for movement and confirmation analysis.
*/
/**
* Default configuration for HangingMan pattern.
*/
const DEFAULT_HANGING_MAN_CONFIG = exports.DEFAULT_HANGING_MAN_CONFIG = {
..._CandlestickFinder.DEFAULT_CANDLESTICK_CONFIG,
minimumThreshold: 0.01,
absoluteMinimum: 0.001,
movementThresholdBase: 1.0,
movementThresholdScale: 0.3
};
class HangingMan extends _CandlestickFinder.default {
minimumThreshold;
absoluteMinimum;
movementThresholdBase;
movementThresholdScale;
constructor(config) {
const finalConfig = {
...DEFAULT_HANGING_MAN_CONFIG,
...config
};
super(finalConfig);
this.name = 'HangingMan';
this.requiredCount = 5;
// Apply configuration with defaults
this.minimumThreshold = finalConfig.minimumThreshold;
this.absoluteMinimum = finalConfig.absoluteMinimum;
this.movementThresholdBase = finalConfig.movementThresholdBase;
this.movementThresholdScale = finalConfig.movementThresholdScale;
}
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;
}
}
let hasUptrend = this.upwardTrend(data);
let hasHammer = this.includesHammer(data);
let hasConfirmation = this.hasConfirmation(data);
let isPattern = hasUptrend && hasHammer && hasConfirmation;
return isPattern;
}
upwardTrend(data) {
// For hanging man, we need an uptrend in the first 4 candles (indices 0-3)
// Since data is in ascending order, we analyze the trend leading up to the hammer
// Ensure we have enough data
if (data.close.length < 4) {
return false;
}
// Analyze trends in closing prices of the first four candlesticks (indices 0-3)
let trendData = data.close.slice(0, 4);
// 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);
let minMovement = priceRange * 0.02; // At least 2% movement relative to range
// Upward trend: more gains than losses, and significant upward movement
return latestGain > latestLoss && totalMovement >= minMovement;
}
includesHammer(data) {
// The hammer should be at index 3 (4th candle) in the 5-candle pattern
// This is the candle just before the confirmation candle
// Ensure we have the required data
if (data.close.length < 4) {
return false;
}
// Use a more lenient hammer detection for hanging man pattern
return this.isHammerLike(data.open[3], data.high[3], data.low[3], data.close[3]);
}
// Custom hammer-like detection that's more lenient than the strict hammer patterns
isHammerLike(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;
}
// For hanging man, we need:
// 1. A lower shadow that's meaningful relative to the total range
// 2. The lower shadow should be larger than the upper shadow
// 3. The body should not dominate the entire candle
// Lower shadow should be at least 5% of the total range (very lenient)
let minLowerShadowRatio = 0.05;
let hasSignificantLowerShadow = lowerShadow >= totalRange * minLowerShadowRatio;
// Lower shadow should be larger than or equal to upper shadow (hammer characteristic)
let lowerShadowDominates = lowerShadow >= upperShadow;
// Body should not be more than 90% of total range (very lenient)
let bodyNotTooLarge = bodySize <= totalRange * 0.9;
// For very small ranges, ensure minimum absolute lower shadow
// Use direct threshold calculation instead of utility function
let minAbsoluteLowerShadow = this.minimumThreshold * this.scale;
let hasMinimumShadow = lowerShadow >= minAbsoluteLowerShadow;
return hasSignificantLowerShadow && lowerShadowDominates && bodyNotTooLarge && hasMinimumShadow;
}
hasConfirmation(data) {
// Ensure we have enough data (need 5 candles total)
if (data.close.length < 5) {
return false;
}
let hammerCandle = {
open: data.open[3],
close: data.close[3],
low: data.low[3],
high: data.high[3]
};
let confirmationCandle = {
open: data.open[4],
close: data.close[4],
low: data.low[4],
high: data.high[4]
};
// Validate OHLC data
if (!this.validateOHLC(hammerCandle.open, hammerCandle.high, hammerCandle.low, hammerCandle.close) || !this.validateOHLC(confirmationCandle.open, confirmationCandle.high, confirmationCandle.low, confirmationCandle.close)) {
return false;
}
// Confirmation candlestick should be bearish (hanging man is bearish reversal)
let isBearishConfirmation = confirmationCandle.open > confirmationCandle.close;
// The confirmation candle itself should be meaningfully bearish
let confirmationBearishness = confirmationCandle.open - confirmationCandle.close;
let confirmationRange = confirmationCandle.high - confirmationCandle.low;
let minConfirmationBearishness = Math.max(confirmationRange * 0.1,
// At least 10% of confirmation candle's range
this.absoluteMinimum * this.scale * 5 // Use direct threshold calculation
);
let isStrongBearishConfirmation = confirmationBearishness >= minConfirmationBearishness;
// The confirmation should show meaningful downward movement
// It should close below the hammer's close
let closesLower = confirmationCandle.close < hammerCandle.close;
// Calculate the downward movement as a percentage of the hammer's range
let hammerRange = hammerCandle.high - hammerCandle.low;
let downwardMovement = hammerCandle.close - confirmationCandle.close;
// Require meaningful downward movement - at least 15% of hammer's range
// Use direct threshold calculation instead of utility function
let minMovementRatio = 0.15; // 15% of hammer range
let minAbsoluteMovement = this.movementThresholdBase + this.movementThresholdScale * this.scale;
let requiredMovement = Math.max(hammerRange * minMovementRatio, minAbsoluteMovement);
let hasSignificantDownwardMovement = downwardMovement >= requiredMovement;
return isBearishConfirmation && isStrongBearishConfirmation && closesLower && hasSignificantDownwardMovement;
}
}
exports.default = HangingMan;
function hangingman(data, config = DEFAULT_HANGING_MAN_CONFIG) {
return new HangingMan(config).hasPattern(data);
}
//# sourceMappingURL=HangingMan.js.map