如何在Highstock中添加多指标?无blacklabel插件实现蜡烛图及多指标展示
嘿,这事儿不难!不用依赖blacklabel插件,咱们直接用Highstock原生的多面板(pane)功能就能实现你要的效果——顶部蜡烛图+成交量,下面依次排列RSI、MACD、ADX这些指标。我给你整理了完整的实现代码和关键细节,你直接拿去用就行:
核心实现思路
- 用Highstock的
panes配置多个独立图表面板,每个面板对应一类数据/指标 - 手动计算RSI、MACD、ADX这些技术指标的数值(毕竟不用插件,得自己处理数据逻辑)
- 通过
series的pane和yAxis属性,把不同数据绑定到对应面板
完整可运行代码
(function () { // 计算RSI指标(默认14周期) function calculateRSI(data, period = 14) { const rsi = []; const gains = []; const losses = []; // 计算每日涨跌额 for (let i = 1; i < data.length; i++) { const change = data[i][4] - data[i - 1][4]; gains.push(Math.max(change, 0)); losses.push(Math.max(-change, 0)); } // 初始平均涨跌幅 let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period; let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period; // 计算第一个RSI值 let rs = avgGain / avgLoss; rsi.push([data[period][0], 100 - (100 / (1 + rs))]); // 迭代计算后续RSI for (let i = period; i < data.length - 1; i++) { avgGain = (avgGain * (period - 1) + gains[i]) / period; avgLoss = (avgLoss * (period - 1) + losses[i]) / period; rs = avgGain / avgLoss; rsi.push([data[i + 1][0], 100 - (100 / (1 + rs))]); } return rsi; } // 计算MACD指标(默认12/26/9周期) function calculateMACD(data, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) { const emaFast = []; const emaSlow = []; const macdLine = []; const signalLine = []; const macdHistogram = []; // 计算快速EMA const kFast = 2 / (fastPeriod + 1); emaFast.push(data[0][4]); for (let i = 1; i < data.length; i++) { emaFast.push(data[i][4] * kFast + emaFast[i - 1] * (1 - kFast)); } // 计算慢速EMA const kSlow = 2 / (slowPeriod + 1); emaSlow.push(data[0][4]); for (let i = 1; i < data.length; i++) { emaSlow.push(data[i][4] * kSlow + emaSlow[i - 1] * (1 - kSlow)); } // 计算MACD线 for (let i = 0; i < data.length; i++) { macdLine.push([data[i][0], emaFast[i] - emaSlow[i]]); } // 计算信号线(MACD的EMA) const kSignal = 2 / (signalPeriod + 1); signalLine.push([macdLine[0][0], macdLine[0][1]]); for (let i = 1; i < macdLine.length; i++) { signalLine.push([macdLine[i][0], macdLine[i][1] * kSignal + signalLine[i - 1][1] * (1 - kSignal)]); } // 计算MACD柱状图 for (let i = 0; i < macdLine.length; i++) { macdHistogram.push([macdLine[i][0], macdLine[i][1] - signalLine[i][1]]); } return { macdLine, signalLine, macdHistogram }; } // 计算ADX指标(默认14周期) function calculateADX(data, period = 14) { const tr = []; const dmPlus = []; const dmMinus = []; const trSmooth = []; const dmPlusSmooth = []; const dmMinusSmooth = []; const diPlus = []; const diMinus = []; const dx = []; const adx = []; // 计算真实波幅TR和动向值DM+/- for (let i = 1; i < data.length; i++) { const high = data[i][2]; const low = data[i][3]; const prevHigh = data[i - 1][2]; const prevLow = data[i - 1][3]; const close = data[i - 1][4]; // 真实波幅TR const tr1 = high - low; const tr2 = Math.abs(high - close); const tr3 = Math.abs(low - close); tr.push(Math.max(tr1, tr2, tr3)); // 动向值DM+/- const upMove = high - prevHigh; const downMove = prevLow - low; dmPlus.push(upMove > downMove && upMove > 0 ? upMove : 0); dmMinus.push(downMove > upMove && downMove > 0 ? downMove : 0); } // 平滑处理TR、DM+、DM- trSmooth.push(tr.slice(0, period).reduce((a, b) => a + b, 0)); dmPlusSmooth.push(dmPlus.slice(0, period).reduce((a, b) => a + b, 0)); dmMinusSmooth.push(dmMinus.slice(0, period).reduce((a, b) => a + b, 0)); for (let i = period; i < tr.length; i++) { trSmooth.push(trSmooth[i - period] - trSmooth[i - period] / period + tr[i]); dmPlusSmooth.push(dmPlusSmooth[i - period] - dmPlusSmooth[i - period] / period + dmPlus[i]); dmMinusSmooth.push(dmMinusSmooth[i - period] - dmMinusSmooth[i - period] / period + dmMinus[i]); } // 计算DI+、DI- for (let i = 0; i < trSmooth.length; i++) { diPlus.push([data[i + period][0], (dmPlusSmooth[i] / trSmooth[i]) * 100]); diMinus.push([data[i + period][0], (dmMinusSmooth[i] / trSmooth[i]) * 100]); } // 计算DX for (let i = 0; i < diPlus.length; i++) { const diff = Math.abs(diPlus[i][1] - diMinus[i][1]); const sum = diPlus[i][1] + diMinus[i][1]; dx.push([diPlus[i][0], (diff / sum) * 100]); } // 计算ADX adx.push([dx[period - 1][0], dx.slice(0, period).reduce((a, b) => a + b[1], 0) / period]); for (let i = period; i < dx.length; i++) { adx.push([dx[i][0], (adx[i - period][1] * (period - 1) + dx[i][1]) / period]); } return adx; } $.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename=aapl-ohlcv.json&callback=?', function (data) { // 将数据集拆分为ohlc和成交量数据 const ohlc = []; const volume = []; const dataLength = data.length; // 处理原始数据 for (let i = 0; i < dataLength; i++) { ohlc.push([ data[i][0], // 时间戳 data[i][1], // 开盘价 data[i][2], // 最高价 data[i][3], // 最低价 data[i][4] // 收盘价 ]); volume.push([ data[i][0], // 时间戳 data[i][5] // 成交量 ]); } // 计算各指标数据 const rsiData = calculateRSI(data); const { macdLine, signalLine, macdHistogram } = calculateMACD(data); const adxData = calculateADX(data); // 初始化Highstock图表 Highcharts.stockChart('container', { title: { text: 'AAPL 股票分析' }, rangeSelector: { selected: 1 }, // 配置多面板 panes: [ { height: '60%', spacingBottom: 20 }, { height: '15%', spacingBottom: 10 }, { height: '15%', spacingBottom: 10 }, { height: '10%' } ], // 对应每个面板的Y轴 yAxis: [ { title: { text: '价格' }, height: '60%', lineWidth: 2, pane: 0 }, { title: { text: '成交量' }, top: '60%', height: '10%', offset: 0, lineWidth: 2, pane: 0 }, { title: { text: 'RSI' }, top: '70%', height: '15%', offset: 0, lineWidth: 2, pane: 1, plotLines: [ { value: 70, color: 'red', dashStyle: 'shortdash', width: 1, label: { text: '超买' } }, { value: 30, color: 'green', dashStyle: 'shortdash', width: 1, label: { text: '超卖' } } ] }, { title: { text: 'MACD' }, top: '85%', height: '15%', offset: 0, lineWidth: 2, pane: 2 }, { title: { text: 'ADX' }, top: '100%', height: '10%', offset: 0, lineWidth: 2, pane: 3 } ], // 各系列数据配置 series: [ { type: 'candlestick', name: 'AAPL', data: ohlc, yAxis: 0, pane: 0 }, { type: 'column', name: '成交量', data: volume, yAxis: 1, pane: 0 }, { type: 'line', name: 'RSI (14)', data: rsiData, yAxis: 2, pane: 1 }, { type: 'line', name: 'MACD', data: macdLine, yAxis: 3, pane: 2, color: '#0070C0' }, { type: 'line', name: '信号线', data: signalLine, yAxis: 3, pane: 2, color: '#FF0000' }, { type: 'column', name: 'MACD柱状图', data: macdHistogram, yAxis: 3, pane: 2, color: '#548235' }, { type: 'line', name: 'ADX (14)', data: adxData, yAxis: 4, pane: 3, color: '#FFC000' } ] }); }); }());
关键细节说明
- 多面板布局:通过
panes数组定义每个面板的高度和间距,再用yAxis的top和height属性对应到面板位置 - 指标计算:所有技术指标都是基于原始OHLCV数据手动计算的,你可以根据需求调整周期参数(比如RSI的14周期、MACD的12/26/9周期)
- 系列绑定:每个
series通过pane和yAxis属性明确指定属于哪个面板和哪个Y轴,避免数据错乱 - 交互优化:Highstock原生支持缩放、拖拽等交互操作,多面板会同步联动,不用额外配置
内容的提问来源于stack exchange,提问作者Dani
相关产品推荐
相关产品推荐

