ta-pattern-lib
Version:
Technical Analysis and Backtesting Framework for Node.js
186 lines • 8.48 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.indicator_bollinger_bands = exports.indicator_alligator = exports.indicator_rsi = exports.indicator_obv = exports.indicator_macd = exports.indicator_adx = void 0;
const lodash_round_1 = __importDefault(require("lodash.round"));
const feature_statistics_1 = require("./feature_statistics");
const indicator_adx = (ohlc_data, period) => {
const true_ranges = [];
const positive_dms = [];
const negative_dms = [];
const dx_values = [];
const adx_values = [];
// Calculate True Range (TR), Positive Directional Movement (+DM), Negative Directional Movement (-DM)
for (let i = 1; i < ohlc_data.length; i++) {
const current_high = ohlc_data[i].high;
const current_low = ohlc_data[i].low;
const previous_close = ohlc_data[i - 1].close;
const true_range = Math.max(current_high - current_low, Math.abs(current_high - previous_close), Math.abs(current_low - previous_close));
true_ranges.push(true_range);
const positive_dm = current_high - ohlc_data[i - 1].high;
const negative_dm = ohlc_data[i - 1].low - current_low;
positive_dms.push(positive_dm > 0 ? positive_dm : 0);
negative_dms.push(negative_dm > 0 ? negative_dm : 0);
}
// Calculate the smoothed average of True Range (ATR)
const atr_values = calculate_smoothed_average(true_ranges, period);
// Calculate the smoothed averages of Positive Directional Movement (+DM) and Negative Directional Movement (-DM)
const positive_dm_mas = calculate_smoothed_average(positive_dms, period);
const negative_dm_mas = calculate_smoothed_average(negative_dms, period);
// Calculate the Directional Movement Index (DX) and ADX
for (let i = period; i < ohlc_data.length - 1; i++) {
const di_plus = (positive_dm_mas[i - period] / atr_values[i - period]) * 100;
const di_minus = (negative_dm_mas[i - period] / atr_values[i - period]) * 100;
const dx = (Math.abs(di_plus - di_minus) / (di_plus + di_minus)) * 100;
dx_values.push(dx);
if (dx_values.length >= period) {
const adx = calculate_smoothed_average(dx_values, period)[dx_values.length - period];
adx_values.push(adx);
}
}
return adx_values;
};
exports.indicator_adx = indicator_adx;
const indicator_macd = (data, short_period = 12, long_period = 26, signal_period = 9) => {
// Calculate the short EMA
const short_ema = (0, feature_statistics_1.stats_ema)(data, short_period);
// Calculate the long EMA
const long_ema = (0, feature_statistics_1.stats_ema)(data, long_period);
// Calculate the MACD line
const macd_line = [];
for (let i = 0; i < data.length; i++) {
macd_line.push(short_ema[i] - long_ema[i]);
}
// Calculate the signal line using SMA of the MACD line
const signal_line = (0, feature_statistics_1.stats_ema)(macd_line, signal_period);
// Return the MACD line and signal line
return {
macd: macd_line,
signal_line: signal_line,
};
};
exports.indicator_macd = indicator_macd;
const indicator_obv = (data) => {
const obv_values = [];
obv_values.push(0); // Initialize OBV with zero as the starting value
for (let i = 1; i < data.length; i++) {
const current_close = data[i].close;
const previous_close = data[i - 1].close;
const volume = parseInt(data[i].volume.toString());
if (current_close > previous_close) {
// Add the volume to OBV if the current close price is higher than the previous close price
obv_values.push(obv_values[i - 1] + volume);
}
else if (current_close < previous_close) {
// Subtract the volume from OBV if the current close price is lower than the previous close price
obv_values.push(obv_values[i - 1] - volume);
}
else {
// If the current close price is equal to the previous close price, keep the OBV value unchanged
obv_values.push(obv_values[i - 1]);
}
}
return obv_values;
};
exports.indicator_obv = indicator_obv;
const indicator_rsi = (data, period = 7) => {
const gains = [];
const losses = [];
const rsi = [];
// Calculate the price changes and separate gains and losses
for (let i = 1; i < data.length; i++) {
const change = data[i] - data[i - 1];
if (change >= 0) {
gains.push(change);
losses.push(0);
}
else {
gains.push(0);
losses.push(-change);
}
}
// Calculate the average gains and losses for the first period
let avg_gain = (0, feature_statistics_1.stats_mean)(gains.slice(0, period));
let avg_loss = (0, feature_statistics_1.stats_mean)(losses.slice(0, period));
// Calculate the initial RSI value
let rs = avg_gain / avg_loss;
const initial_rsi = 100 - 100 / (1 + rs);
rsi.push(initial_rsi);
// Calculate RSI for the remaining data
for (let i = period; i < data.length - 1; i++) {
const current_gain = gains[i];
const current_loss = losses[i];
// Smooth the average gains and losses using the previous averages
avg_gain = (avg_gain * (period - 1) + current_gain) / period;
avg_loss = (avg_loss * (period - 1) + current_loss) / period;
rs = avg_gain / avg_loss;
const current_rsi = 100 - 100 / (1 + rs);
rsi.push(current_rsi);
}
return rsi;
};
exports.indicator_rsi = indicator_rsi;
const indicator_alligator = (ohlc_data, jaw_period, teeth_period, lips_period) => {
const jaw_values = [];
const teeth_values = [];
const lips_values = [];
for (let i = jaw_period - 1; i < ohlc_data.length; i++) {
const jaw_start_index = i - jaw_period + 1;
const teeth_start_index = i - teeth_period + 1;
const lips_start_index = i - lips_period + 1;
const jaw_highs = ohlc_data.slice(jaw_start_index, i + 1).map((d) => d.high);
const jaw_lowest_high = Math.min(...jaw_highs);
jaw_values.push(jaw_lowest_high);
const teeth_highs = ohlc_data.slice(teeth_start_index, i + 1).map((d) => d.high);
const teeth_lowest_high = Math.min(...teeth_highs);
teeth_values.push(teeth_lowest_high);
const lips_highs = ohlc_data.slice(lips_start_index, i + 1).map((d) => d.high);
const lips_lowest_high = Math.min(...lips_highs);
lips_values.push(lips_lowest_high);
}
return {
jaw: jaw_values,
teeth: teeth_values,
lips: lips_values,
};
};
exports.indicator_alligator = indicator_alligator;
const indicator_bollinger_bands = (data, period = 20, multiplier = 2) => {
const close_prices = data.map((d) => (0, lodash_round_1.default)(Number(d.close), 2));
const middle = [];
const upper = [];
const lower = [];
function simple_moving_average(prices, period, index) {
const slice = prices.slice(index - period, index);
const sum = slice.reduce((acc, price) => acc + price, 0);
return sum / period;
}
function standard_deviation(prices, period, index, sma) {
const slice = prices.slice(index - period, index);
const variance = slice.reduce((acc, price) => acc + Math.pow(price - sma, 2), 0) / period;
return Math.sqrt(variance);
}
for (let i = period; i <= close_prices.length; i++) {
const sma = simple_moving_average(close_prices, period, i);
const std_dev = standard_deviation(close_prices, period, i, sma);
middle.push(sma);
upper.push(sma + multiplier * std_dev);
lower.push(sma - multiplier * std_dev);
}
return { middle, upper, lower };
};
exports.indicator_bollinger_bands = indicator_bollinger_bands;
const calculate_smoothed_average = (data, period) => {
const smoothed_data = [];
const sum = data.slice(0, period).reduce((acc, val) => acc + val, 0);
const average = sum / period;
smoothed_data.push(average);
for (let i = period; i < data.length; i++) {
const smoothed_value = (smoothed_data[i - period] * (period - 1) + data[i]) / period;
smoothed_data.push(smoothed_value);
}
return smoothed_data;
};
//# sourceMappingURL=feature_indicators.js.map