// Self-contained ATR indicator // Includes math, metadata, signal calculation, and base class // Signal constants (defined in each indicator file) const SIGNAL_TYPES = { BUY: 'buy', SELL: 'sell', HOLD: 'hold' }; const SIGNAL_COLORS = { buy: '#26a69a', hold: '#787b86', sell: '#ef5350' }; // Base class (inline replacement for BaseIndicator) class BaseIndicator { constructor(config) { this.id = config.id; this.type = config.type; this.name = config.name; this.params = config.params || {}; this.timeframe = config.timeframe || '1m'; this.series = []; this.visible = config.visible !== false; this.cachedResults = null; this.cachedMeta = null; this.lastSignalTimestamp = null; this.lastSignalType = null; } } // Signal calculation for ATR function calculateATRSignal(indicator, lastCandle, prevCandle, values) { const atr = values?.atr; const close = lastCandle.close; const prevClose = prevCandle?.close; if (!atr || atr === null || !prevClose) { return null; } const atrPercent = atr / close * 100; const priceChange = Math.abs(close - prevClose); const atrRatio = priceChange / atr; if (atrRatio > 1.5) { return { type: SIGNAL_TYPES.HOLD, strength: 70, value: atr, reasoning: `High volatility: ATR (${atr.toFixed(2)}, ${atrPercent.toFixed(2)}%)` }; } return null; } // ATR Indicator class export class ATRIndicator extends BaseIndicator { constructor(config) { super(config); this.lastSignalTimestamp = null; this.lastSignalType = null; } calculate(candles) { const period = this.params.period || 14; const results = new Array(candles.length).fill(null); const tr = new Array(candles.length).fill(0); for (let i = 1; i < candles.length; i++) { const h_l = candles[i].high - candles[i].low; const h_pc = Math.abs(candles[i].high - candles[i-1].close); const l_pc = Math.abs(candles[i].low - candles[i-1].close); tr[i] = Math.max(h_l, h_pc, l_pc); } let atr = 0; let sum = 0; for (let i = 1; i <= period; i++) sum += tr[i]; atr = sum / period; results[period] = atr; for (let i = period + 1; i < candles.length; i++) { atr = (atr * (period - 1) + tr[i]) / period; results[i] = atr; } return results.map(atr => ({ atr })); } getMetadata() { return { name: 'ATR', description: 'Average True Range - measures market volatility', inputs: [{ name: 'period', label: 'Period', type: 'number', default: 14, min: 1, max: 100, description: 'Period for ATR calculation' }], plots: [{ id: 'value', color: '#795548', title: 'ATR', lineWidth: 1 }], displayMode: 'pane' }; } } export { calculateATRSignal };