/** * HTS Indicator Definition * Allows selecting average type and fast interval. */ const HTS_INDICATOR = { name: 'HTS', label: 'HTS', usesBaseData: false, params: [ { name: 'avgType', type: 'select', defaultValue: 'VWMA', options: ['RMA', 'EMA', 'SMA', 'WMA', 'VWMA'], label: 'Average Type' }, { name: 'fast', type: 'number', defaultValue: 33, min: 1, label: 'Fast Interval' } ], calculateFull: function(data, params) { // determine data and period based on autoTF let source = data; const period = params.fast; if (params.autoTF) { // current timeframe in seconds between candles const tfSec = (data.length > 1) ? (data[1].time - data[0].time) : 60; const tfMin = tfSec / 60; const autoInterval = Math.max(1, Math.floor(tfMin / 4)); // aggregate displayed data at autoInterval source = aggregateCandles(data, autoInterval); } else { source = data; } const type = params.avgType; if (!source || source.length < period) return { max: [], min: [] }; const maxSeries = []; const minSeries = []; let sumH, sumL, prevH, prevL, mult; switch (type) { case 'SMA': sumH = 0; sumL = 0; for (let i = 0; i < period; i++) { sumH += source[i].high; sumL += source[i].low; } maxSeries.push({ time: source[period - 1].time, value: sumH / period }); minSeries.push({ time: source[period - 1].time, value: sumL / period }); for (let i = period; i < source.length; i++) { sumH += source[i].high - source[i - period].high; sumL += source[i].low - source[i - period].low; maxSeries.push({ time: source[i].time, value: sumH / period }); minSeries.push({ time: source[i].time, value: sumL / period }); } break; case 'EMA': case 'VWMA': mult = 2 / (period + 1); // initialize sumH = 0; sumL = 0; for (let i = 0; i < period; i++) { sumH += source[i].high; sumL += source[i].low; } prevH = sumH / period; prevL = sumL / period; maxSeries.push({ time: source[period - 1].time, value: prevH }); minSeries.push({ time: source[period - 1].time, value: prevL }); for (let i = period; i < source.length; i++) { const h = source[i].high, l = source[i].low; prevH = (h - prevH) * mult + prevH; prevL = (l - prevL) * mult + prevL; maxSeries.push({ time: source[i].time, value: prevH }); minSeries.push({ time: source[i].time, value: prevL }); } break; case 'RMA': // Wilder's smoothing sumH = 0; sumL = 0; for (let i = 0; i < period; i++) { sumH += source[i].high; sumL += source[i].low; } prevH = sumH / period; prevL = sumL / period; maxSeries.push({ time: source[period - 1].time, value: prevH }); minSeries.push({ time: source[period - 1].time, value: prevL }); for (let i = period; i < source.length; i++) { const h = source[i].high, l = source[i].low; prevH = prevH + (h - prevH) / period; prevL = prevL + (l - prevL) / period; maxSeries.push({ time: source[i].time, value: prevH }); minSeries.push({ time: source[i].time, value: prevL }); } break; case 'WMA': const denom = period * (period + 1) / 2; for (let i = period - 1; i < source.length; i++) { let wSumH = 0, wSumL = 0; for (let j = 0; j < period; j++) { const w = period - j; wSumH += source[i - j].high * w; wSumL += source[i - j].low * w; } maxSeries.push({ time: source[i].time, value: wSumH / denom }); minSeries.push({ time: source[i].time, value: wSumL / denom }); } break; default: source.forEach(d => { maxSeries.push({ time: d.time, value: d.high }); minSeries.push({ time: d.time, value: d.low }); }); } return { max: maxSeries, min: minSeries }; } };