// Self-contained HTS Trend System 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; } } // MA calculations inline (SMA/EMA/RMA/WMA/VWMA) function calculateSMA(candles, period, source = 'close') { const results = new Array(candles.length).fill(null); let sum = 0; for (let i = 0; i < candles.length; i++) { sum += candles[i][source]; if (i >= period) sum -= candles[i - period][source]; if (i >= period - 1) results[i] = sum / period; } return results; } function calculateEMA(candles, period, source = 'close') { const multiplier = 2 / (period + 1); const results = new Array(candles.length).fill(null); let ema = 0; let sum = 0; for (let i = 0; i < candles.length; i++) { if (i < period) { sum += candles[i][source]; if (i === period - 1) { ema = sum / period; results[i] = ema; } } else { ema = (candles[i][source] - ema) * multiplier + ema; results[i] = ema; } } return results; } function calculateRMA(candles, period, source = 'close') { const multiplier = 1 / period; const results = new Array(candles.length).fill(null); let rma = 0; let sum = 0; for (let i = 0; i < candles.length; i++) { if (i < period) { sum += candles[i][source]; if (i === period - 1) { rma = sum / period; results[i] = rma; } } else { rma = (candles[i][source] - rma) * multiplier + rma; results[i] = rma; } } return results; } function calculateWMA(candles, period, source = 'close') { const results = new Array(candles.length).fill(null); const weightSum = (period * (period + 1)) / 2; for (let i = period - 1; i < candles.length; i++) { let sum = 0; for (let j = 0; j < period; j++) { sum += candles[i - j][source] * (period - j); } results[i] = sum / weightSum; } return results; } function calculateVWMA(candles, period, source = 'close') { const results = new Array(candles.length).fill(null); for (let i = period - 1; i < candles.length; i++) { let sumPV = 0; let sumV = 0; for (let j = 0; j < period; j++) { sumPV += candles[i - j][source] * candles[i - j].volume; sumV += candles[i - j].volume; } results[i] = sumV !== 0 ? sumPV / sumV : null; } return results; } // MA dispatcher function function getMA(type, candles, period, source = 'close') { switch (type.toUpperCase()) { case 'SMA': return calculateSMA(candles, period, source); case 'EMA': return calculateEMA(candles, period, source); case 'RMA': return calculateRMA(candles, period, source); case 'WMA': return calculateWMA(candles, period, source); case 'VWMA': return calculateVWMA(candles, period, source); default: return calculateSMA(candles, period, source); } } // Signal calculation for HTS function calculateHTSSignal(indicator, lastCandle, prevCandle, values, prevValues) { const slowLow = values?.slowLow; const slowHigh = values?.slowHigh; const prevSlowLow = prevValues?.slowLow; const prevSlowHigh = prevValues?.slowHigh; if (!slowLow || !slowHigh || !prevSlowLow || !prevSlowHigh) { return null; } const close = lastCandle.close; const prevClose = prevCandle?.close; if (prevClose === undefined) return null; // BUY: Price crosses UP through slow low if (prevClose <= prevSlowLow && close > slowLow) { return { type: SIGNAL_TYPES.BUY, strength: 85, value: close, reasoning: `Price crossed UP through slow low` }; } // SELL: Price crosses DOWN through slow high else if (prevClose >= prevSlowHigh && close < slowHigh) { return { type: SIGNAL_TYPES.SELL, strength: 85, value: close, reasoning: `Price crossed DOWN through slow high` }; } return null; } // HTS Indicator class export class HTSIndicator extends BaseIndicator { constructor(config) { super(config); this.lastSignalTimestamp = null; this.lastSignalType = null; } calculate(candles, oneMinCandles = null, targetTF = null) { const shortPeriod = this.params.short || 33; const longPeriod = this.params.long || 144; const maType = this.params.maType || 'RMA'; const useAutoHTS = this.params.useAutoHTS || false; let workingCandles = candles; if (useAutoHTS && oneMinCandles && targetTF) { const tfMultipliers = { '5m': 5, '15m': 15, '30m': 30, '37m': 37, '1h': 60, '4h': 240 }; const tfGroup = tfMultipliers[targetTF] || 5; const grouped = []; let currentGroup = []; for (let i = 0; i < oneMinCandles.length; i++) { currentGroup.push(oneMinCandles[i]); if (currentGroup.length >= tfGroup) { grouped.push({ time: currentGroup[tfGroup - 1].time, open: currentGroup[tfGroup - 1].open, high: currentGroup[tfGroup - 1].high, low: currentGroup[tfGroup - 1].low, close: currentGroup[tfGroup - 1].close, volume: currentGroup[tfGroup - 1].volume }); currentGroup = []; } } workingCandles = grouped; } const shortHigh = getMA(maType, workingCandles, shortPeriod, 'high'); const shortLow = getMA(maType, workingCandles, shortPeriod, 'low'); const longHigh = getMA(maType, workingCandles, longPeriod, 'high'); const longLow = getMA(maType, workingCandles, longPeriod, 'low'); return workingCandles.map((_, i) => ({ fastHigh: shortHigh[i], fastLow: shortLow[i], slowHigh: longHigh[i], slowLow: longLow[i], fastMidpoint: ((shortHigh[i] || 0) + (shortLow[i] || 0)) / 2, slowMidpoint: ((longHigh[i] || 0) + (longLow[i] || 0)) / 2 })); } getMetadata() { const useAutoHTS = this.params?.useAutoHTS || false; const fastLineWidth = useAutoHTS ? 1 : 1; const slowLineWidth = useAutoHTS ? 2 : 2; return { name: 'HTS Trend System', description: 'High/Low Trend System with Fast and Slow MAs', inputs: [ { name: 'short', label: 'Fast Period', type: 'number', default: 33, min: 1, max: 500 }, { name: 'long', label: 'Slow Period', type: 'number', default: 144, min: 1, max: 500 }, { name: 'maType', label: 'MA Type', type: 'select', options: ['SMA', 'EMA', 'RMA', 'WMA', 'VWMA'], default: 'RMA' }, { name: 'useAutoHTS', label: 'Auto HTS (TF/4)', type: 'boolean', default: false } ], plots: [ { id: 'fastHigh', color: '#00bcd4', title: 'Fast High', width: fastLineWidth }, { id: 'fastLow', color: '#00bcd4', title: 'Fast Low', width: fastLineWidth }, { id: 'slowHigh', color: '#f44336', title: 'Slow High', width: slowLineWidth }, { id: 'slowLow', color: '#f44336', title: 'Slow Low', width: slowLineWidth } ], displayMode: 'overlay' }; } } export { calculateHTSSignal };