UNPKG

ta-pattern-lib

Version:

Technical Analysis and Backtesting Framework for Node.js

186 lines 8.48 kB
"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