简介:面向微信小程序开发者的股票图表组件源码包,聚焦沪深/港股K线图与走势图,支持解析通达信公式语法。项目围绕行情图表这一核心场景,覆盖数据获取、格式转换、K线渲染、手势缩放、技术指标计算等完整链路,适合需在小程序中快速集成专业行情图表、有一定前端基础的开发者参考。压缩包共72个文件、1.14MB,以JavaScript逻辑、WXML/WXSS页面、JSON配置、HTML示例及PNG效果图为主,内含小程序行情模块用例、历史/分钟K线案例、指数数据及指标编译组件,便于按模块检索。已有1076人学习下载。通过源码可掌握HQChart在微信端的移植思路,理解通达信指标公式的解析与调用方式,并能二次开发自定义样式、数据接口与交互行为,是搭建股票分析小程序底座的实用参考资料。 如果在微信/小程序里做过行情类页面,大概率遇到过这类需求:项目要上沪深/港股K线图,产品经理还特别强调一句“公式得支持通达信语法”。标题里的“HQChart-master.zip”其实就是一套开源方案,这个需求落地时绕不开三座山:绘图性能、通达信公式语法兼容、以及沪深/港股两套市场的数据适配。这篇就把我实际做这块时的选型逻辑、实现步骤和踩过的坑一起梳理出来,给准备在小程序里做行情页面的朋友一个可直接参考的思路。
1. 项目到底在解决什么问题
1.1 需求拆解:不只是画K线
行情页面在小程序里并不仅仅是“画几根蜡烛”那么简单。一个常规的需求清单至少包括:
- 沪深、港股两市K线展示,可切换日K、周K、月K、分时
- 主图指标:MA、BOLL、SAR等;副图指标:MACD、KDJ、RSI等
- 支持通达信公式语法,用户自定义技术指标
- 十字光标、缩放、拖动、最新价标签等交互
- 复权切换(前复权/不复权)
- 在iOS和Android真机上保持流畅
这里最容易被低估的是“通达信语法”。通达信公式是一套带函数库的表达式语言,要在一个前端项目里实现它的解析和计算,工作量并不比画图本身小。如果从零造轮子,解析器加函数库加指标模板,少说也要两到三周。这也是选型时优先考虑HQChart的根本原因:它把“绘图”和“公式引擎”两件事都做了。
1.2 为什么最终选了HQChart
当时对比了三个方案:ECharts小程序版、uCharts、HQChart。对比结果很直接:
| 方案 | 小程序适配 | 通达信公式 | 性能表现 | 二次开发成本 |
|---|---|---|---|---|
| ECharts | 需自行封装canvas | 无 | 中,数据量大时卡顿明显 | 高 |
| uCharts | 轻量封装 | 无 | 好,但图形元素偏基础 | 中 |
| HQChart | 原生适配小程序 | 内置完整解析引擎 | 较好,专为行情优化 | 低 |
ECharts的强项是通用图表,但在K线这种“数据点密集、手势交互复杂、指标计算公式多”的场景里,需要自己处理的事件和定制项非常多。uCharts性能不错,但遇到“用户自定义通达信公式”这种需求基本无解。HQChart则是在金融行情场景里长出来的库,K线、分时、指标、公式语法、画线工具这些都属于“开箱即用”。
提示:HQChart的完整名称是“jones2000/HQChart”,在GitHub上开源,支持H5和小程序双端。下载后的压缩包里,
hqchart/目录下就是核心库。
2. 小程序端K线图的实现核心
2.1 画布组件的正确用法
微信小程序的canvas经历了一次大的API升级,从wx.createCanvasContext升级到了Canvas 2D接口。HQChart的官方示例早期用的是旧接口,但如今在真机上必须优先使用type="2d"的新画布,否则会碰到严重的性能和兼容性问题。
页面里的标准写法是:
<canvas type="2d" id="kLineCanvas" class="kline-canvas" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" />然后是JS侧初始化:
const query = wx.createSelectorQuery() query.select('#kLineCanvas') .fields({ node: true, size: true }) .exec((res) => { const canvas = res[0].node const width = res[0].width const height = res[0].height const dpr = wx.getWindowInfo().pixelRatio canvas.width = width * dpr canvas.height = height * dpr // 创建HQChart实例 const option = { type: 'kline', canvas: canvas, width: width, height: height, dpr: dpr, // ... } const chart = new JsChart(option) chart.Init() })关键点在于dpr(像素比)处理。小程序里CSS逻辑像素和物理像素是两套体系,如果不把canvas的宽高乘上dpr,在高分屏上绘制出来的K线会模糊。HQChart在option里接收dpr参数,但画布本身的宽高必须由开发者事先设置,这是一个容易忽略的地方。
2.2 数据组织与增量更新
K线图数据源通常是接口返回的数组,每项包含open、close、high、low、volume、time等字段。HQChart对数据格式有一定要求,核心是两条:
timestamp必须是毫秒级时间戳- 数据需要按时间正序排列
实际开发中,后端返回的可能是秒级时间戳,或者字段名不匹配。切片对齐是必须做的一步,干脆封装一个转换函数:
function normalizeKLine(list) { return list.map((item) => ({ open: item.openPrice || item.open, close: item.closePrice || item.close, high: item.highPrice || item.high, low: item.lowPrice || item.low, volume: item.volume || item.vol || 0, timestamp: item.time * 1000, // 秒转毫秒 })) }行情数据是高频更新的,尤其是分时图场景。但小程序中setData的性能瓶颈非常明显,全量更新一屏几百根K线的数据,会直接导致页面卡顿。正确的做法是服务端只推最新一条tick,小程序端用chart.UpdateData()做增量更新,而不是重新setOption整个数据源。
2.3 手势交互与十字光标
H5端K线图通常直接用鼠标事件,小程序端就没有那么“顺手”,需要把触摸事件主动转发给HQChart。HQChart在小程序端暴露了OnTouchStart、OnTouchMove、OnTouchEnd等方法,只需要在页面的bindtouchstart等回调里调用即可。
实际操作时,我发现一个体验问题:十字光标的tooltip位置需要动态计算,如果把它做成一个独立的view覆盖层,在快速滑动时会有明显的“滞后感”,因为setData更新DOM位置需要走一遍逻辑层到渲染层的通道。推荐的做法是让HQChart把十字光标和指标数值直接绘制在canvas上,不单独做覆盖层,流畅度会好很多。
3. 通达信语法与HQChart公式引擎
3.1 公式在客户端算什么
通达信公式长这样:
MACD: DIFF : EMA(CLOSE,SHORT) - EMA(CLOSE,LONG); DEA : EMA(DIFF,MID); MACD : (DIFF-DEA)*2, COLORSTICK;这里的EMA是移动平均函数,CLOSE是收盘价序列,整条公式本质上是对一组序列数据做运算,最终输出可供K线图叠加或副图展示的曲线数据。HQChart内置了这套解析和计算能力,意味着开发者可以把它当作一个“公式解释器”来用。
这个能力比很多人预想的要重要。业务上常见两类需求:
- 内置指标参数可调,比如MA5/MA10/MA20
- 用户自定义公式,比如做一个“涨停突破MA20”的选股条件
如果没有公式引擎,这两种需求都需要前端硬编码,每加一个指标就发一次版本。有了HQChart后,公式基本可以作为配置下发。
3.2 常用指标与自定义公式接入
HQChart里的指标分为两种:内置指标和自定义扩展指标。内置指标模板基本涵盖了通达信常见的MA、EMA、MACD、KDJ、BOLL等,初始化时可以直接通过指标数组指定:
option.symbol = '600036' option.isShowRightNowInfo = true, option.kLine = { show: true, // 主图指标 mainIndex: { name: 'MA', args: [5, 10, 20, 60] }, // 副图指标 subIndex: [ { name: 'MACD', args: [12, 26, 9] }, { name: 'KDJ', args: [9, 3, 3] }, ], }但自定义公式才是“通达信语法”落地的关键。HQChart在option里提供了一个extend字段,可以注册自定义计算函数。以“计算某只股票过去N日的振幅均值”为例:
const FormulaExtension = { // 注册在公式中可调用的函数名 CalcAmplitude: function (args) { // args 里是公式传入的参数,例如 N const N = args[0] // data 是当前K线序列 const data = this.data const result = [] for (let i = 0; i < data.length; i++) { if (i < N) { result.push(0) continue } let sum = 0 for (let j = i - N; j < i; j++) { const amp = (data[j].high - data[j].low) / data[j].close sum += amp } result.push(sum / N) } return result }, } const chart = new JsChart(option) chart.AddExtendFunction('CalcAmplitude', FormulaExtension.CalcAmplitude)自定义公式的注册逻辑并不复杂,但有一个前置条件很容易踩坑:注册扩展函数必须在Init()之前完成,否则公式引擎解析时找不到函数会直接报错。
3.3 周期切换与跨市场适配
通达信语法的另一个特点是周期体系,同一套公式在不同周期下都应当能跑。HQChart内置了日K、周K、月K、分钟K等周期枚举,切换周期时只需要调用:
chart.ChangePeriod('week') // 切换为周K但跨市场适配就不只是“改周期”这么简单了。沪深和港股在交易时间、交易单位、复权规则上有差异,这些差异不交给绘图库管,而需要业务层在数据源上处理好。
以港股为例:
- 港股交易时间是9:30-12:00,13:00-16:00,午休一小时,分时图需要有对应的断点
- 港股的成交单位是“股”,但报价精度到0.001元,下单单位是“手”或“股”需要单独约定
- 港股部分老股票有“合股/拆股”历史,复权因子和A股不同
这些信息得在数据接口设计时预留好字段,K线图只负责“画对”,数据靠业务保证。不要指望绘图库来替你处理市场差异。
4. 沪深/港股数据适配的坑
4.1 数据字段差异
两边的接口返回字段命名差异很大,这是最基础却最容易翻车的点。比如同一只股票,沪深接口返回常见字段可能是open、close,而港股接口可能返回openPrice、prevClosePrice,甚至timestamp格式都不一致。
处理方案建议统一收敛到normalizeKLine函数里,这个函数应当在数据进入HQChart之前完成“方言翻译”。换数据源时只改这个函数,不动图形层代码。
还需要注意的是停牌数据处理。港股和A股都有长期停牌的股票,停牌期间K线缺失,绘图时需要决定是“留空隙”还是“连续画”。通达信的标准做法是跳过缺失K线,但如果用户看的是“月K”,而某个月完全无交易,也需要特殊标记。这里我建议在服务端补一条“空K线”而不是让前端猜,前端只做展示。
4.2 复权与涨跌幅计算
计算港股前复权价和A股不太一样。A股复权通常只需考虑分红送股,港股还要考虑“以股代息”这种特殊情形,导致复权因子计算更复杂。但无论后端怎么算,前端必须明确一条规则:K线图的高开低收价格必须使用复权后的价格,否则技术指标会失真。
涨跌幅的计算同样有区别。A股默认以前收盘价作为基准,港股虽然也是这个逻辑,但由于可能发生“特别股息”、“供股”等事件,前收盘价本身可能被交易所调整过。前端切不可自行用“昨日close”去算涨跌幅,必须信任行情接口返回的prevClose字段。
实际项目中,我把复权因子的处理完全放在了后端,前端只接收“已经是复权后的K线数据”,以及一个单独的prevClose字段。这也算是一个团队协作上的最佳实践:数据管道和绘制层各管各的。
4.3 数据对齐与加载时机
沪深/港股市场交易日不同,比如A股10月1日到7日休市,港股可能只有1号休市,这导致K线数据的“对齐”问题。如果你做的是“A+H股对比图”,就会遇到同一时间轴上一只股票有K线、另一只没K线的情况。
HQChart本身按时间戳对齐数据,缺失的数据不会自动补,所以需要业务层决定:是隐藏缺失段,还是填充空数据。我的做法是前端按时间戳做一次对齐,用一张“交易日历表”来补缺失的日期条目,保证两只股票的时间轴长度一致。这个逻辑放在后端做会更省事,否则小程序端处理大量对齐逻辑会拖慢首屏渲染。
5. 常见问题与排查技巧实录
这里整理几个我在真机调试和小程序上线后实际遇到的高频问题,做成一张速查表:
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 真机上K线模糊 | canvas宽高未乘dpr | 设置canvas.width = width * pixelRatio |
| 模拟器正常,真机白屏 | canvas初始化时机过早/页面未完全就绪 | 放在wx.nextTick或onReady后再创建实例 |
| 触摸滑动无反应 | 未将touch事件传给HQChart | 在bindtouchmove中调用chart.OnTouchMove(e) |
| 自定义公式不生效 | 扩展函数在Init()之后才注册 | 确保注册顺序在初始化之前 |
| 卡顿明显 | setData高频更新数据源 | 改用增量更新UpdateData() |
| 十字光标tooltip闪烁 | 覆盖层view频繁setData | 把数值绘制到canvas内部 |
| 港股K线数据错位 | A/H股时间轴不一致 | 服务端用交易日历补齐对齐 |
| iOS端canvas覆盖弹窗 | canvas是原生组件,层级最高 | 需要弹窗时隐藏canvas或改用cover-view |
5.1 真机白屏的排查思路
模拟器正常但真机白屏,是canvas类库最常见的故障。排查路径建议按顺序来:先确认页面onReady时机、再确认canvas节点是否成功创建、最后检查canvas类型是否type="2d"。HQChart官网示例很多时候是H5版的demo,迁移到小程序时容易忽略这些差异。
我在开发中踩过最明显的一个坑:在onLoad里就去创建chart实例,此时canvas节点未完成渲染,拿到的是空值。后来统一改为在onReady里做初始化,并加了一个空值守护判断,问题才稳定解决。
5.2 层级遮挡问题的两种解法
小程序里canvas是原生组件,天然盖在所有普通view和弹窗之上,这是从WebView时代就存在的硬伤。解决方式有两种:
第一种是在弹窗出现时把canvas隐藏掉,关闭弹窗后再恢复,代价是会看到K线闪烁一下。第二种是使用cover-view覆盖在canvas上做自定义弹层,但cover-view的样式受限较严重,只适合按钮、简单文本提示这类场景。
实际项目中,我们默认采用方案一,因为行情弹窗(比如交易确认框)本身频率不高,闪烁问题在用户体验上可接受。
5.3 内存与长列表问题
K线页面如果包含大量历史数据,一次性在HQChart里加载全部K线,内存会被明显吃掉。控制数据量的维度有两个:首屏只请求最近200~500根K线,拖动到末尾时再请求更早的数据;对已加载的数据做降采样,比如最早期的分钟数据只在用户放大时才显示。
另一个容易被忽略的是页面销毁时的资源释放。小程序页面栈后,canvas实例不会自动销毁,需要在onUnload里显式调用chart.Destroy(),否则在安卓低端机上会持续占用内存,导致切页面后卡顿。
踩过几次坑之后我的体会是:行情类页面的核心不是“把图画出来”,而是把数据管道、公式引擎边界和原生组件兼容性这三件事理顺。HQChart解决了80%的绘图和公式解析问题,剩下20%的项目适配工作,只要按数据流分层处理,即使在沪深/港股双市场这种复杂场景下,也不会乱。最后再分享一个小技巧:开发时把HQChart自带的demo工程完整跑通一遍再动手改,官方demo里对指标模板、周期切换、数据格式各种形态的兼容性处理,比你自己翻文档总结要快得多。
本文还有配套的精品资源,点击获取