@thuantan2060/technicalindicators
Version:
Techincal Indicators written in javascript
163 lines (153 loc) • 4.92 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.DEFAULT_CANDLESTICK_CONFIG = void 0;
/**
* Basic configuration interface for CandlestickFinder base class.
* Only contains the scale parameter used by approximateEqual function.
*/
/**
* Default configuration for CandlestickFinder base class.
*/
const DEFAULT_CANDLESTICK_CONFIG = exports.DEFAULT_CANDLESTICK_CONFIG = {
scale: 0.1
};
class CandlestickFinder {
requiredCount;
name;
/**
* Scale parameter for price comparison precision in approximateEqual function.
* This should ONLY be used in the approximateEqual method, not in other pattern calculations.
*/
scale;
constructor(config = DEFAULT_CANDLESTICK_CONFIG) {
// Apply configuration with defaults
const finalConfig = {
...DEFAULT_CANDLESTICK_CONFIG,
...config
};
this.scale = finalConfig.scale;
}
/**
* Compares two numbers for approximate equality using scale-dependent threshold.
* This is the ONLY place where this.scale should be used in pattern detection.
*
* @param a First number to compare
* @param b Second number to compare
* @returns true if the numbers are approximately equal, false otherwise
*/
approximateEqual(a, b) {
// Handle edge cases
if (a === b) return true;
if (isNaN(a) || isNaN(b) || !isFinite(a) || !isFinite(b)) return false;
let left = parseFloat(Math.abs(a - b).toPrecision(4)) * 1;
return left <= this.scale;
}
/**
* Helper method to validate OHLC data integrity
*/
validateOHLC(open, high, low, close) {
// Check for NaN or infinite values
if (!isFinite(open) || !isFinite(high) || !isFinite(low) || !isFinite(close)) {
return false;
}
// Check basic OHLC constraints
if (high < Math.max(open, close) || low > Math.min(open, close)) {
return false;
}
// Check for negative prices (optional, depends on use case)
if (open < 0 || high < 0 || low < 0 || close < 0) {
return false;
}
return true;
}
logic(data) {
throw "this has to be implemented";
}
getAllPatternIndex(data) {
if (!data || !data.close || data.close.length < this.requiredCount) {
return [];
}
// Validate data arrays have same length
if (data.open.length !== data.close.length || data.high.length !== data.close.length || data.low.length !== data.close.length) {
return [];
}
if (data.reversedInput) {
data.open.reverse();
data.high.reverse();
data.low.reverse();
data.close.reverse();
}
let strategyFn = this.logic;
return this._generateDataForCandleStick(data).map((current, index) => {
return strategyFn.call(this, current) ? index : undefined;
}).filter(hasIndex => {
return hasIndex !== undefined;
});
}
hasPattern(data) {
if (!data || !data.close || data.close.length < this.requiredCount) {
return false;
}
// Validate data arrays have same length
if (data.open.length !== data.close.length || data.high.length !== data.close.length || data.low.length !== data.close.length) {
return false;
}
if (data.reversedInput) {
data.open.reverse();
data.high.reverse();
data.low.reverse();
data.close.reverse();
}
let strategyFn = this.logic;
return strategyFn.call(this, this._getLastDataForCandleStick(data));
}
_getLastDataForCandleStick(data) {
let requiredCount = this.requiredCount;
if (data.close.length === requiredCount) {
return data;
} else {
let returnVal = {
open: [],
high: [],
low: [],
close: []
};
let i = 0;
// For ascending order data, get the last requiredCount elements
let startIndex = data.close.length - requiredCount;
while (i < requiredCount) {
returnVal.open.push(data.open[startIndex + i]);
returnVal.high.push(data.high[startIndex + i]);
returnVal.low.push(data.low[startIndex + i]);
returnVal.close.push(data.close[startIndex + i]);
i++;
}
return returnVal;
}
}
_generateDataForCandleStick(data) {
let requiredCount = this.requiredCount;
let generatedData = [];
// Generate sliding windows for pattern detection
for (let index = 0; index <= data.close.length - requiredCount; index++) {
let returnVal = {
open: [],
high: [],
low: [],
close: []
};
for (let i = 0; i < requiredCount; i++) {
returnVal.open.push(data.open[index + i]);
returnVal.high.push(data.high[index + i]);
returnVal.low.push(data.low[index + i]);
returnVal.close.push(data.close[index + i]);
}
generatedData.push(returnVal);
}
return generatedData;
}
}
exports.default = CandlestickFinder;
//# sourceMappingURL=CandlestickFinder.js.map