
简介面向微信小程序开发者的股票图表源码包聚焦沪深、港股K线图与走势图实现并内置通达信语法支持可用于快速搭建行情展示与技术指标计算页面。资源共72个文件以js业务逻辑与图表渲染、wxml/wxss页面视图与样式、json配置等小程序核心类型为主同时包含html/css演示页、png图表素材及字体文件整体仅1.14MB轻量易集成。目前已有1080人学习下载。内容基于HQChart-master完整项目涵盖小程序行情模块用例、分时/历史K线图、通达信公式编译器、数据接口封装以及知临行情图API说明文档docx并附手机端演示页面和投资者、板块、公告等示例图。适合熟悉JavaScript、期望在微信生态内实现专业股票图表的开发者借鉴可直接复用源码或作为二次开发底座省去从零搭建图表组件的高昂成本。1. 微信小程序里做K线图为什么绕不开 HQChart-master 这个方案做小程序股票行情页最容易踩的坑不是找不到组件而是组件买到手才发现分时图一个玩法、K线一个玩法、指标又一套玩法三个功能要三个插件最后塞进包体几百 KB还互不通信。HQChart-master 这个开源方案恰好在这些点上都卡得比较准一套纯 JavaScript 写的行情图表库同时覆盖沪深与港股的 K 线、分时走势并且内嵌了一套通达信公式语法解析器——指标不是写死在图上的而是你用公式文本现场描述的。对于已经会写微信小程序的开发者这意味着可以彻底摆脱组件商的版本绑架把分时、K线、自选指标统一到一个画布上。这是一篇实操复盘从库结构原理到小程序落地、数据引入、公式调试与实战翻车点尽量一次讲清楚。适合手里有小程序基础、正在评估自绘行情页可行性的人。2. 先看懂 HQChart 的架构才知道它为什么能塞进小程序2.1 为什么选它而不是自绘或通用图表库三层结构与无 DOM 依赖先说选型。自绘 K 线图不是不行但工作量集中在几个容易被低估的地方十字光标跟随、缩放时的 Y 轴重算、分时与K线的坐标切换、MACD/KDJ 这些指标的序列对齐。大部分团队自绘到“能看”要两到三周到“敢上线”得一个月往上。通用图表库的问题在另一头它不是为证券行情设计的蜡烛图全靠自己拼量价坐标联动、周期切换、复权标记这些功能没有现成方案到最后封装成本不低。HQChart 走的是中间路线。它的核心由三部分组成数据管理器负责持有 K 线序列与分时序列指标分派层负责把通达信公式翻译成可计算的序列运算渲染层只负责把最终得出的数据画到 Canvas 上。我不会把这三层说得太玄你只需要记住一个判断数据层不关心你画什么指标层只做序列计算渲染层不关心数据从哪来。这个解耦决定了它可以不需要 DOM、不需要 WebGL、不需要浏览器专用 API所以它天然具备跑进微信小程序的潜质。小程序里的 Canvas 与浏览器 Canvas 底层不同但只要能拿到 canvas 2d 上下文HQChart 的渲染逻辑就走得通。下面的表格是我在做选型时压在桌面上的对比方案十字光标通达信公式周期切换小程序适配成本后续维护成本自绘画布自己写不支持自己写中等高通用图表库需二次开发不支持需二次开发低中HQChart内置内置内置中低低所以结论很直接如果你明确要做行情类页面且希望指标可配置、后续能接用户自定义公式HQChart 这类专用方案是性价比最高的。通用图表库适合做数据报表不适合做行情主图。2.2 通达信公式不是魔法序列运算与指标分派的执行流程通达信公式语言初看像一组函数调用但它的执行模型和普通 JavaScript 完全不一样。拿一句最简单的 MA(CLOSE,5) 来说CLOSE 不是一个数字而是一个与 K 线一一对应的序列MA 计算的结果仍然是序列。公式引擎拿到文本后会先把整段公式拆成一个有依赖关系的计算图然后逐根 K 线求值。这也是为什么 HQChart 能脱离 PC 端行情软件独立运行——它实现的是一套“序列虚拟机”不是简单地把公式文本转成 JS 再执行。在公式文本里有两个符号最容易让新手困惑。一个是 :代表中间变量只参与计算不输出到界面一个是 :代表输出变量计算完成后要画到指标窗格里的。比如MA5:MA(CLOSE,5); MA10:MA(CLOSE,10); DIFF:EMA(CLOSE,12)-EMA(CLOSE,26);这里 MA5 与 MA10 会显示到副图指标上而如果你写成 MA5:MA(CLOSE,5)指标窗格里就什么都不会显示因为引擎认为那只是个临时变量。这个细节我见过不止一个开发者栽跟头。HQChart 的指标管理器会逐个解析这类文本内置了一批常用函数也支持你按它的扩展接口注册自定义计算函数。理解了这个流程你在后面调试公式时才不会对着空白指标窗格发愣——先分清是语法没解析出来还是算出来但没被输出。3. 把 HQChart 装进小程序工程从压缩包到首屏 K 线的最小实现3.1 拷贝目录、require 与 canvas 2d 初始化一套能跑通的最小步骤你拿到的资源大概率是一个名为 HQChart-master.zip 的压缩包。解压后不要急着把整个仓库塞进小程序里面可能含有示例页面、文档和构建脚本。小程序对包体大小敏感我只拷贝运行期必要的 JS 目录到项目里。常见的做法是在 miniprogram 目录下新建 utils/hqchart将库文件复制进去然后在页面文件中通过相对路径 require。小程序里 HQChart 的载体是 canvas 2d不是旧版 canvas。初始化时最关键的坑是必须在画布节点真正布局完成之后再取节点、再创建图表实例。否则你拿到的 canvas 宽度高度全是 0图表画出来白屏。下面是一段可以照抄的基础初始化代码// pages/kline/kline.js const { JSChart } require(../../utils/hqchart/hqchart); Page({ onReady() { // 等画布完成布局后再创建图表避免拿到宽高为 0 的节点 const query wx.createSelectorQuery(); query.select(#klineCanvas).fields({ node: true, size: true }).exec((res) { if (!res || !res[0] || !res[0].node) { console.error(画布节点未就绪); return; } const canvas res[0].node; const ctx canvas.getContext(2d); // 适配高分屏把画布物理像素放大防止真机上线条发虚 const dpr wx.getWindowInfo().pixelRatio; canvas.width res[0].width * dpr; canvas.height res[0].height * dpr; ctx.scale(dpr, dpr); const option { type: 0, // 0 表示 K 线主图1 表示分时走势图 symbol: sh600000, name: 示例股票, isClickShow: false, dayOffset: 2, }; // 创建图表实例并保存到页面供后续数据更新 this.chart new JSChart(ctx, option); this.chart.Update(); }); }, });逻辑上一共四步等待画布节点取出 2d 上下文按设备像素比放大物理画布创建图表实例并调用 Update 触发首次渲染。参数说明里type 决定主图是 K 线还是分时symbol 目前可以先用占位符后续接入真实行情时再替换成股票代码。dayOffset 表示右侧预留空白 K 线的数量这是为了让最后一根 K 线不被十字光标或者最新价标签挡住。有个容易忽略的点ctx.scale(dpr, dpr) 必须在创建图表实例之前执行否则 HQChart 内部拿到的坐标系统还是物理像素画出来的图形会偏移到画布左下角。我在最初联调时就是因为顺序写反真机上所有图形挤成一团排查了半天才发现是坐标系缩放没有先执行。3.2 喂数据的第一句话K 线数据结构与 addData 常见写法初始化完成只是把空画布立起来了要让走势图真正出现需要给图表实例传入 K 线数据。HQChart 对 K 线数据项的字段有约定常见字段包括日期、开盘、最高、最低、收盘、成交量、成交额等。这里最容易犯的错误是自己定义了一套字段名比如把最高价命名为 high_price然后发现图表上蜡烛高低点全乱套。库内部是通过固定字段名取值不是通过位置映射。下面这段代码模拟从接口拿到数据后添加到图表// 假设 res.data 是接口返回的 K 线数组 const rows res.data || []; const klineData rows.map((item) ({ date: item.date, // 格式2025-03-14 open: Number(item.open), // 开盘价注意转成数字 high: Number(item.high), // 最高价 low: Number(item.low), // 最低价 close: Number(item.close), // 收盘价 volume: Number(item.volume), // 成交量单位是股还是手要和数据源对齐 amount: Number(item.amount), // 成交额用于副图指标 })); this.chart.AddData(klineData); this.chart.Update();字段映射这一步没有多少技术含量但它是后续所有指标计算的基础。一个隐藏问题是成交量单位某公开行情接口返回的是“手”另一个可能返回的是“股”如果直接混用VOL 指标数值会差 100 倍均量线看起来就是一条奇怪的直线。我一般会在接入阶段就把单位统一成“股”因为通达信公式里的 VOL 默认按股计算后续对接公式时少一层换算。如果你要显示的是分时走势图而不是 K 线数据协议则是另一套分时数据是当天从 9:30 到 11:30、13:00 到 15:00 的离散点序列每个点包含时间、价格、均价和累计成交量。HQChart 里切换的方式就是初始化时的 type 字段。我的建议是先跑通 K 线再搞分时因为 K 线涉及的指标渲染逻辑更多分时本质上是一根 240 个点的连线有了基础后半天就能接上。4. 沪深/港股数据接入与通达信公式两个最容易出问题的输入模块4.1 数据字段适配手数、千分位与分钟周期的映射关系沪深与港股在数据细节上存在一些需要提前处理的差异如果不做适配图表上会出现两种典型的翻车现象要么最新价对不上要么成交量柱状图高得离谱。A股代码是 6 位数字港股代码是 5 位数字且需要额外字段标识市场A股成交量单位通常是手港股可能是股A股日K天然没有 5 位小数港股部分股票价格小数位更多。数据源给你的字段往往还带一些前缀比如 sh 代表上海、sz 代表深圳、hk 代表香港。所以在进入 HQChart 之前先做一个统一的清洗函数。function formatStockRow(raw) { // raw 可能来自不同接口字段名不同这里做一层收敛 return { date: raw.tradeDate || raw.date, open: Number(raw.openPrice || raw.open), high: Number(raw.highPrice || raw.high), low: Number(raw.lowPrice || raw.low), close: Number(raw.closePrice || raw.close), // 统一成交量为股若接口给的是手需要乘以 100 volume: Number(raw.volume || raw.vol) * (raw.volUnit lot ? 100 : 1), amount: Number(raw.amount || raw.turnover), }; }这段代码的关键不是字段名而是把单位与字段差异在一处消灭。我一般把 formatStockRow 放在独立的 data 层文件里这样将来切换行情源时只需要改这一个文件页面和图表库都不需要知道上游长什么样。港股还有一个需要留意的地方部分接口返回的 date 是字符串 2025/03/14而不是标准的 ISO 格式。HQChart 内部排序依赖日期字符串比较所以格式统一很重要。如果日期格式混合出现排序会乱十字光标的索引对不上图表就会表现出“走势对光标不对”的怪异现象。分钟周期映射是另一个容易踩的地方。日K数据转换逻辑简单但分钟K线涉及时间聚合5 分钟K线由 1 分钟数据聚合成15 分钟由 5 分钟聚合。这不是 HQChart 负责的事是数据源或者你自己的聚合层负责。如果你手里的数据源只提供 1 分钟线你需要在小程序端做一个聚合函数按交易时段把若干根 1 分钟 K 线合并成一根。这里有个交易时段的细节11:30 到 13:00 是午间休市不能把 11:30 和 13:01 的数据合并到一根 5 分钟线里。通达信端会把休市隔开但部分接口返回的时间戳是连续的聚合时必须主动跳过。4.2 通达信公式的两种加载方式内置指标文件与运行时代码注入公式引擎作为 HQChart 的核心卖点加载方式在不同版本里略有差别但大的方向有两种。第一种是预定义把公式文本写在配置对象里初始化时一次性传入。适合固定指标比如系统自带的 MACD、KDJ、RSI。第二种是运行时注入用户在前端界面里输入一段通达信公式文本点完“应用”后动态挂到当前图表上。适合做自定义指标功能这也是很多人选择 HQChart 而不是其他组件库的根本原因。运行时注入的核心代码大致长这样// 用户输入的公式文本例如 // MA5:MA(CLOSE,5); // MA10:MA(CLOSE,10); const formula this.data.formulaText; // 先移除旧指标避免重复叠加 this.chart.RemoveIndicator(MA5); this.chart.RemoveIndicator(MA10); // 动态添加新指标 this.chart.AddIndicator({ name: 自定义均线, script: formula, isShow: true, lineColor: #ff0000, }); this.chart.Update();这里有个安全点需要强调HQChart 执行的不是 JavaScript而是它内部实现的通达信公式解释器。这意味着用户输入的文本不会被 eval 执行不会有操作 DOM、发网络请求的能力。从安全角度讲比“把用户公式拼进 JS 再执行”的思路稳妥一截。这也是我认定这个方向值得投入的原因之一。指标显示通常还需要调整参数指标窗格的高度比例、线条宽度、是否显示数值标签等。这些参数在 AddIndicator 里都有对应配置不同版本命名有差异。我在调试时习惯把 isShow 先设成 false只让它参与计算不画线确认数值正确后再打开显示。这样可以隔离“公式算错”和“渲染不对”两类问题避免排查时两头猜。5. 小程序环境下的常见问题避坑从画布白屏到指标乱套5.1 真机白屏画布初始化时机与尺寸未就绪现象开发者工具里显示完全正常一到 iPhone 真机上预览图表区域一片空白console 里也没有报错。原因开发者工具的模拟器对 canvas 节点尺寸的处理比真机宽容真机上 onReady 触发时画布还没有完成布局查询到的高度为 0。HQChart 拿到的画布尺寸是 0渲染出来的东西自然不可见。这种情况多半不是库本身的问题是初始化时序踩坑。解决把创建图表实例的代码从 onReady 挪到 SelectorQuery 的 exec 回调里并且先判断 size 是否合法。上面 3.1 里的写法已经规避了这个问题。如果你发现 exec 回调里 size 仍然为 0可以在回调里用 wx.nextTick 再包一层给布局多留一帧时间。5.2 公式写进去没显示全角符号与输出语句问题现象把一段从某行情软件里复制的通达信公式粘贴进小程序指标窗格完全空白或只在左上角显示一个数字。原因大概率是公式文本里混入了中文全角分号、全角括号或者变量声明的输出冒号写成了中文冒号。通达信公式语法只认半角字符。另一个常见原因是公式里只有中间变量没有输出变量比如全是 :没有一条 : 输出语句。解决写一个小函数在公式注入前做一次清洗把全角字符替换成半角。这不是什么高深技巧但能省掉大量调试时间。function normalizeFormula(input) { return input .replace(//g, ;) .replace(//g, :) .replace(//g, () .replace(//g, )) .replace(/,/g, ,) .replace(//g, ,); }做完字符清洗后如果还是不显示再检查输出语句。至少保留一条形如 NAME:EXPR; 的输出语句指标窗格才会有内容。还有一个边界HQChart 内置函数集合不一定覆盖所有通达信函数个别冷门函数在库版本里未实现遇到这种情况要么换等价写法要么扩展库注册自定义函数。5.3 十字光标错位日期未排序、重复 K 线与增量刷新冲突现象图表显示正常但手指点到某一根 K 线上时十字光标的数值标签对应的是另一天的数据而且越往右偏得越厉害。原因HQChart 内部通过数组索引定位十字光标对应的数据如果 K 线数组没有严格按日期升序或者存在重复日期索引与日期就会错位。另一个触发点是增量刷新在旧数据尾部直接 append 新数据而新数据里混入了重复的同一根K线数组就出现了两个相同日期的条目。解决在数据进入图表之前做两件事排序和按日期去重。排序逻辑很简单字符串日期可以直接用 比较。去重时保留最后一条即可因为最后一条通常是修正后的数据。我一般把它放在 formatStockRow 之后的公共处理函数里保证滑入图表的数据永远是干净的序列。5.4 手势冲突页面滚动与图表缩放的触摸事件竞争现象真机上手指在 K 线图上做捏合缩放时页面也跟着上下滚动或者左右滑动浏览历史K线时整个页面被拖走。原因小程序页面默认的滚动行为与 canvas 上的触摸手势冲突。模板上如果绑的是 bindtouchmove事件会继续冒泡页面滚动和图表手势同时响应。解决把 canvas 的触摸绑定改成 catchtouchmove阻止事件冒泡。这样页面滚动不会在 canvas 区域内触发捏合与单指滑动就能留给图表库处理。代价是图表区域内的上下滑动不再滚页面——考虑到行情页通常整页就是图表区这个取舍是合理的。5.5 长时间挂机卡顿指标数据累积与实例未销毁现象页面从早上挂到下午期间反复切换股票与周期操作开始变卡响应越来越慢。原因两个。一是反复 AddIndicator 但没有 RemoveIndicator指标实例层层叠加二是页面 onUnload 时没有销毁图表实例canvas 相关的监听与数据仍然驻留。小程序页面栈卸载不代表 JavaScript 对象被回收如果图表实例引用还挂在全局变量上内存只会只增不减。解决每次切换指标前先按名称移除旧指标切换股票时调用一次图表实例的销毁方法把实例引用置为 null。这个习惯应该在开发早期就建立起来而不是等用户反馈卡顿再来排查。6. 把图表封装成组件并校验精度才算真正落地6.1 封装成自定义组件解耦页面逻辑如果你只在一个页面使用图表直接在 Page 里写没问题。但稍微正规一点的行情应用自选页、个股详情页、分时弹窗都会用到图表。这时候我建议把 HQChart 的完整生命周期封装成一个自定义组件 KLineChart对外只暴露一个 data 属性。页面只管传数组组件内部负责创建、更新、销毁。组件里可以做到收到新数据时自动排序去重页面切换时自动销毁实例。这个封装不复杂但它能防止“页面一多图表初始化和销毁逻辑散落各处”的失控局面。// components/kline-chart/index.js Component({ properties: { klineData: { type: Array, value: [] }, type: { type: Number, value: 0 }, }, observers: { klineData: function (rows) { if (!this.chart || !rows.length) return; this.chart.RemoveAllIndicator(); this.chart.AddData(rows); this.chart.Update(); }, }, });这个组件的价值在于页面开发者不需要知道 HQChart 的 API只需要把数据准备好传给组件。后续如果要从 HQChart 换成别的库改动的范围也被限制在组件内部页面代码不用动。做过行情类项目的开发者应当能感受到这种边界划分能省下多少排查成本。6.2 校验指标精度用已知 K 线手算一遍再信跑结果接完数据、配上指标不要急着上线。我习惯做一次“人工对照测试”挑最近 5 根日 K用计算器手算 MA5 的收盘均值然后对比图表指标窗格里的值误差应当控制在 1e-6 数量级。如果对不上多半是复权类型混了——前复权、后复权与不复权数据混在同一个数组里计算公式没有问题但输入数据本身口径不一致。这种问题靠肉眼看图看不出来只有数值对比能暴露。同样值得做的是性能基准测试在低端 Android 机上连续快速的切 30 次周期记录白屏时间与卡顿帧率。HQChart 的指标计算量不算大比较吃性能的是在高分屏上重绘大量 K 线时Canvas 2D 的绘制次数。我一般会把 K 线显示范围限制在 200 根左右历史数据通过滑动按需加载而不是一次性把 3 年日 K 全部铺进画布。这个技巧对长周期走势图尤其有用能在不牺牲体验的前提下显著降低掉帧概率。最后说一个我自己的教训只要项目里涉及通达信公式语法不管你的公式是从哪个软件里抄来的先在本地跑一遍最简单的 MA(CLOSE,5)确认公式引擎工作正常再往上叠加复杂指标。这样能保证复杂公式出错时你能确定是公式本身的问题还是库引擎的边界问题。行情渲染这条路没有捷径但按“数据先通、指标后调、性能最后压”的顺序走方向基本不会错。希望帮到你。本文还有配套的精品资源点击获取