微信小程序 K 线行情页实战:HQChart 接入、港股适配与通达信指标 简介面向微信小程序端股票行情开发的实战源码包聚焦沪深/港股K线图、分时走势图与技术指标展示适合已掌握小程序基础、希望快速搭建行情页面的开发者参考。包内围绕行情模块提供分时、历史K线等示例并涉及通达信语法公式解析、指标计算、图表缩放平移、用户交互与数据接口适配等环节可用于理解行情图从数据组织到渲染呈现的完整链路。压缩包共72个文件、约1.14MB以JavaScript源码为核心配合PNG截图、HTML演示页、CSS/WXSS样式、JSON配置与WXML模板另有字体文件和API说明文档目录按模块划分便于检索和复用。已有1081人学习适合对照实现K线主图、指标副图、手势操作及微信端性能调试快速形成可扩展的股票图表方案。1. 从一张“拖一下就卡住”的行情页说起在微信小程序里做行情页最容易低估的是最后那 20%K 线图能画出来但一拖就卡、一放大十字光标就对不上价格、切到港股时间轴整体错位。这套 HQChart 源码包要解决的正是这些收尾问题——沪深与港股共用一套 K 线图与分时走势图渲染指标部分直接吃通达信语法写MA5:MA(CLOSE,5);这样的公式就能挂上去。它适合三类人毕设需要一个能当场演示的行情模块、课设要交一份完整图表方案、业务里要接行情页面的前端。下面按需求、架构、数据、绘图、避坑、验收的顺序走一遍每一步都给到能照着改的代码和参数。2. 总体架构三层结构与各自不该碰的边界图表类项目最容易写成一锅粥——页面里既请求数据又算指标还管 canvas 尺寸改一行动全身。这套方案的第一件事就是把边界切干净。2.1 为什么不在小程序里从零手写 canvas 图表手写并非不可能但成本分布在很偏的地方。画一根 K 线是几何题真正吃时间的是这几件可视区裁剪几百根 K 线只画屏幕上那几十根、坐标轴的刻度自适应价格区间从 3.2 到 380 都要好看、十字光标吸附与信息浮层、双指缩放与单指拖动的冲突判定、指标窗口高度分配。这些每一条都能单独耗掉两天而且做出来通常还不如成熟库稳。HQChart 的价值就在于它把这些当成基础设施做好了K 线主图、成交量副图、任意多个指标窗口共用一套坐标系和事件分发你只需要提供数据、告诉它画什么。选型对比大致是这样方案上手成本交互完备度通达信语法小程序适配纯 canvas 自绘极高需自己实现不支持需自行处理通用图表库折线/柱状低只有缩放平移不支持好本方案HQChart 内核中内置十字光标/拖拽/多窗口支持已适配 canvas 2d代价是引入一个体量不小的库需要处理分包和初始化顺序这一点在第 5 章会专门讲。2.2 三层结构与职责划分整个工程切成三层任何一层都不许越界调用另一层的内部实现数据适配层负责把行情接口返回的沪深、港股数据转成内核认识的定长数组负责复权标记、分页游标、增量追加。它不认识 canvas也不认识页面。绘图内核层HQChart 本体。只认Symbol、Period、Windows这些配置项只通过NetworkFilter往外要数据。业务代码不许直接改它的内部变量。业务控制层页面。管周期切换按钮、指标下拉、加载态、空态、错误提示以及触摸事件转发。这样切的好处是换数据源只动第一层换图表库只动第二层加个“横屏看盘”只动第三层。毕设答辩时评委问“如果行情接口换了怎么办”你能指着分层说清楚这比背功能列表有说服力得多。2.3 工程目录与核心库放置位置把压缩包解压后核心绘图库整个搬进小程序工程不要试图只挑几个文件出来内部有大量循环依赖。推荐的落位方式# 小程序工程根目录 miniprogram/ pages/kline/ # K线页 pages/minute/ # 分时页 libs/hqchart/ # 核心库整体放入解压目录的原样拷贝 utils/quote.js # 数据适配层 utils/symbol.js # 市场代码转换页面里的引用写成相对路径示例工程里的引用路径以你解压后的实际目录为准别照抄网上的路径很容易少一级。app.json里记得给图表页单独分包subpackages配置的root指向pages/之外的目录更干净{ subpackages: [ { root: packageChart, pages: [pages/kline/kline, pages/minute/minute] } ], preloadRule: { pages/index/index: { network: all, packages: [packageChart] } } }preloadRule的作用是在首页就预下载分包用户点进图表页时不用等。这一步在小程序里很关键否则首屏会有一段空白体验上很难看。3. 数据适配层沪深与港股怎么对齐成一套 K 线结构内核不认识“沪市”“港股”它只认识一个Symbol字符串和一串数字。适配层的核心任务就是把两套市场规则压平。3.1 统一 Bar 数据结构与字段顺序HQChart 的 K 线数据是定长数组不是对象数组顺序固定写错一位整张图就废了。这是最容易踩的坑之一// 把任意来源的行情数据转成内核要的定长数组 // 顺序[日期, 昨收, 开, 高, 低, 收, 成交量, 成交额] function toBarList(list) { return list.map(function (item) { return [ item.date, // 2024-05-13 日线分钟线为 2024-05-13 10:31 item.yclose, // 昨收缺了会导致涨跌幅全错 item.open, item.high, item.low, item.close, item.vol, // 成交量统一成「手」 item.amount // 成交额统一成「元」 ] }) }第三个位置是昨收不是开盘。很多人按[日期,开,高,低,收]的自然顺序去拼图能出来但颜色全反、涨跌幅一塌糊涂而且不报错——这种静默错误最难查。港股这边要额外做两件事一是成交量单位港股有的接口给“股”有的给“手”必须统一否则量柱高度差两个数量级二是价格精度港股低价股有三位小数比如 0.062四舍五入到两位会丢掉有效信息导致 K 线粘连。我一般会在适配层留一个precision字段跟着Symbol走。市场代码也在这里归一化// 市场后缀决定复权规则和交易时段 function toSymbol(code, market) { var map { sh: .sh, sz: .sz, hk: .hk } return code (map[market] || .sh) }沪市600000.sh、深市000001.sz、港股00700.hk后缀错了内核不会报错只会一直拉不到数据表现为“图是空的”排查时容易被误导到 canvas 上去。3.2 请求封装、分页与增量拉取图表页的数据加载分三种场景一定要分开写别指望一个接口通吃首屏历史数据进入页面拉最近 N 根日线一般 300 到 500 根够看。向左拖拽翻页用户拖到最左边时按游标往前再拉一段追加到数组头部。实时更新盘中每隔几秒更新最后一根。把wx.request包一层统一超时、重试和错误态function requestQuote(path, params, retry) { retry retry || 0 return new Promise(function (resolve, reject) { wx.request({ url: https://your-domain.example.com/ path, data: params, timeout: 8000, success: function (res) { if (res.statusCode 200 res.data res.data.code 0) { resolve(res.data.data) } else { reject(new Error(bad response: res.statusCode)) } }, fail: function (err) { // 单次重试避免弱网下直接白屏 if (retry 1) { setTimeout(function () { requestQuote(path, params, retry 1).then(resolve, reject) }, 600) } else { reject(err) } } }) }) }参数上count别一次给到 2000。小程序端一次性解析几千根 K 线会有明显的主线程停顿尤其低端安卓机。分页大小建议 120 到 200翻页时用beforeTime或offset游标不要用“页码”因为盘中新数据会插到末尾导致页码错位。翻页的实现要点在合并而不是请求新拿到的数据要concat到旧数组前面同时更新内核的起始位置否则拖动会“跳回去”。追加完调一次刷新接口让内核重算可视区。3.3 复权、停牌与港股特有字段复权是行情里最容易被忽略、又最容易在答辩现场被抓的点。日线必须默认前复权否则除权那天会出现一个巨大的跳空缺口看上去像数据错误。配置项里通常是一个整型KLine: { Right: 1, // 0 不复权1 前复权2 后复权 Period: 0, // 0 日线1 周线2 月线4 一分钟 MaxReqeustDataCount: 800, PageSize: 150 }港股还有两个 A 股没有的情况要处理。第一是半日市和台风停牌当天可能只有半天数据K 线的日期序列不能简单按“每个交易日一根”去补否则会和真实日期错开正确做法是以接口返回的日期为准缺失就是缺失。第二是碎股和暗盘这些数据一般不进日线如果你的数据源混进来了记得在适配层过滤掉不然成交量会突然冒出一根异常柱。另外提醒一句别在客户端做复权计算。复权因子涉及分红送配数据量大且容易算错交给服务端一次性算好、前端只负责展示这是血泪经验。4. 绘图内核接入canvas 初始化、手势与通达信指标挂载这一章是整个方案里最容易出问题的地方因为它同时涉及小程序的渲染机制、事件系统和第三方库的初始化时序。4.1 canvas 2d 节点初始化与尺寸自适应小程序现在有两套 canvas旧版canvas和新版type2d。图表类一定要用 2d 版本它是同层渲染不会被原生组件盖住也不会出现拖动时残影。WXML 里这样写canvas type2d idklineCanvas classkline-canvas bindtouchstartonTouchStart bindtouchmoveonTouchMove bindtouchendonTouchEnd /canvas初始化必须等节点真正挂载后再查onReady里做别写在onLoadonReady: function () { var that this wx.createSelectorQuery() .select(#klineCanvas) .fields({ node: true, size: true }) .exec(function (res) { if (!res[0] || !res[0].node) return var canvas res[0].node var dpr wx.getSystemInfoSync().pixelRatio // 物理像素和逻辑像素要分开设否则安卓上字会糊 canvas.width res[0].width * dpr canvas.height res[0].height * dpr that.chart HQChart.Chart.JSChart.Init(canvas) that.chart.SetOption(that.buildOption()) }) }两个参数必须成对出现canvas.width用物理像素乘了dpr样式宽高用逻辑像素rpx 或 px。只设一个安卓高分屏上要么模糊要么只画左下角四分之一。这是最高频的翻车点没有之一。buildOption()里至少要给全Symbol、Type、Windows三样缺一个可能就不渲染buildOption: function () { return { Type: 历史K线图, Symbol: this.data.symbol, // 如 00700.hk IsAutoUpdate: false, // 自己控制刷新节奏 IsShowRightMenu: false, // 小程序里没地方放右键菜单 KLine: { Right: 1, Period: 0, MaxReqeustDataCount: 800, PageSize: 150 }, Windows: [ { Index: MA, Modify: false, Change: false }, { Index: VOL, Modify: false, Change: false } ] } }Windows数组的顺序就是从上到下的窗口顺序第一个是主图后面是副图。Modify和Change关掉用户就没法在小程序里改指标参数——如果你想让用户能改第 5 章会讲怎么开。4.2 触摸事件转发与十字光标内核不自己绑事件它要你把小程序的touch事件原样喂进去。这一步看着简单但坐标系必须换算onTouchStart: function (e) { if (!this.chart) return var t e.touches[0] this.chart.OnTouchStart({ x: t.x, // 相对 canvas 左上角不是 clientX y: t.y, identifier: t.identifier }) }, onTouchMove: function (e) { if (!this.chart) return var t e.touches[0] this.chart.OnTouchMove({ x: t.x, y: t.y, identifier: t.identifier }) }, onTouchEnd: function () { if (!this.chart) return this.chart.OnTouchEnd() }t.x/t.y在小程序 canvas 上是相对组件的坐标直接用如果你拿到的是clientX得先减去 canvas 的boundingClientRect.left否则十字光标会整体偏移。很多人卡在这里以为是库的 bug。还有一个细节双指缩放要传两个点的identifier。单点拖动和双指缩放内核靠identifier数量区分只传touches[0]会让缩放手势失效用户两根手指搓半天没反应。主图默认开启十字光标长按出现、拖动移动、松手消失。如果你想让浮层跟着手指显示“开高低收、涨跌幅、成交量”只需要在内核回调里拿当前选中项不用自己算坐标。这块建议直接用它自带的自己实现一套对齐逻辑很容易在缩放后错位。4.3 通达信语法指标的组织与编译这是这套方案相对其他图表库的核心差异点。你在通达信里写惯了MA5:MA(CLOSE,5);、DIF:EMA(CLOSE,12)-EMA(CLOSE,26);在这里基本可以原样搬过来不用翻译成 JS。脚本的组织方式建议按“一指标一文件”放在libs/hqchart/indicator/下用一个注册表串起来方便增删// 指标注册表新增指标只改这里页面不动 var INDEX_LIST [ { Name: MY_MA, Description: 自定义均线组, IsMainIndex: true, OutCount: 3, Script: [ MA5:MA(CLOSE,5);, MA10:MA(CLOSE,10);, MA20:MA(CLOSE,20); ].join(\n) }, { Name: MY_MACD, Description: MACD 副图, IsMainIndex: false, OutCount: 3, Script: [ DIF:EMA(CLOSE,12)-EMA(CLOSE,26);, DEA:EMA(DIF,9);, MACD:(DIF-DEA)*2; ].join(\n) } ]OutCount必须等于脚本里输出的变量个数写少了后面的线直接不画写多了会多出空线两种情况都不报错。IsMainIndex决定它挂主图还是副图主图指标和 K 线共用 Y 轴副图指标自己一套轴放错了线会挤成一团。注册进内核时函数名各版本略有差异可能叫AddIndex、SetIndexScript之类接入前先看一眼解压目录里相关模块的导出别照抄。注册完之后Windows里把Index换成你的指标名就能挂上Windows: [ { Index: MY_MA, Modify: false, Change: false }, { Index: VOL, Modify: false, Change: false }, { Index: MY_MACD, Modify: false, Change: false } ]支撑的通达信函数是这套东西的实用边界。常用的这些基本都有MA、EMA、SMA、REF、HHV、LLV、CROSS、SUM、ABS、MAX、MIN。写指标时两个习惯能省很多事一是所有除法都先判断分母IF(V0, A/V, 0)这种保护写上去否则停牌股成交量为 0 会算出NaN整条线断掉二是REF的周期参数不要超过你加载的 K 线根数越界取到的是空值同样会让线断。至于“用户能不能自己改参数”如果要做就在页面上放几个输入框改完重新拼Script字符串再注册一次同名指标然后刷新窗口。别直接改已注册的脚本对象缓存可能不刷新。5. 避坑与排查真机上最容易翻车的五个点模拟器上一切正常、真机上一塌糊涂是这类项目的常态。下面五条按出现频率排序。5.1 真机上图表区域全白控制台没有任何报错现象开发者工具里图表正常扫码到手机上进入页面canvas 区域一片白日志干净。原因绝大多数是初始化时序问题。onReady里查节点时如果页面有wx:if包裹 canvas或者分包还没下载完res[0].node是undefined而代码里没做判空就直接往下走后面所有调用都在一个空对象上静默失败。解决exec回调第一行加if (!res[0] || !res[0].node) { setTimeout(retry, 100); return }做有限次重试同时确认 canvas 不要放在wx:if里改用hidden控制显隐。5.2 拖动 K 线时掉帧越拖越卡现象刚进页面很流畅拖动十几秒后开始卡顿低端安卓机尤其明显。原因把内核的每次重绘结果通过setData同步到了页面比如把当前选中价格写到 data 里做浮层拖动时每秒触发几十次setData逻辑层和渲染层来回通信直接把帧率打下来。解决所有跟随手指变化的内容都画在 canvas 里不要走setData。只有周期切换、股票切换这种低频操作才允许setData。这一条改完之后通常是数量级的提升。5.3 港股 K 线整体错位日期对不上现象同一只股票小程序上看到的价格和别的软件差一截或者日期整体偏移一天。原因一是没做时区归一港股数据源返回的时间戳可能是 UTC 或带时区偏移直接当本地时间解析会差 8 小时二是复权开关没对上A 股默认前复权、港股数据源默认不复权两边一对就错。解决适配层统一把时间转成YYYY-MM-DD字符串再进内核不用时间戳复权参数由Symbol后缀决定.hk也走前复权并在切换股票时重建 option 而不是复用旧对象。5.4 自定义指标挂上了但不显示现象Windows里加了指标名副图窗口出来了但里面是空的。原因三种可能——OutCount和脚本输出变量数不一致脚本里用了内核不支持的函数编译直接返回空IsMainIndex设成了true却挂到了副图位置Y 轴范围被 K 线价格撑爆线被压成一条贴底的直线。解决先单独用一个最简单的MA5:MA(CLOSE,5);验证通路是否打通通了再往上加复杂度把脚本编译的返回值打出来看一眼绝大多数错误在那里就能看到。5.5 真机请求全部失败开发者工具却正常现象工具里数据正常加载真机上一个请求都发不出去。原因小程序的服务器域名白名单是跟着小程序配置走的开发者工具默认不校验真机强制校验。另外内核初始化的资源加载如果走网络也要一起加进白名单。解决把行情接口域名、静态资源域名都配进 request 合法域名调试阶段可以临时勾选“不校验合法域名”但交付前必须关掉再验一遍这一步别省。6. 验收与答辩一套能当场跑通的检查清单方案写得再好答辩现场崩一次就白搭。我一般会准备一份固定的检查清单按顺序走一遍全部通过才算交付。这套流程分三块功能、性能、异常。功能这一块按“市场 × 周期 × 图表类型”做正交覆盖别只演示一只股票检查项通过标准沪深日线K 线、成交量、均线三层都出十字光标数值与顶部一致港股日线日期序列连续无跨日错位低价股小数位正确分时图均价线连续开盘前无数据段不画线周期切换日/周/月/分钟来回切 3 次不残留旧数据指标切换主图副图各切一次窗口高度分配正常通达信指标自定义公式能显示参数改动后立即生效性能这块只看两个数一是首屏从点击到出图的时间控制在 2 秒内二是连续拖拽 30 秒观察是否出现肉眼可见的卡顿。工具里的性能面板只是参考真机手感才是准的高低端机各拿一台跑一遍。异常这块最容易被跳过但答辩老师往往就爱问这些“断网了怎么办”“接口返回空怎么办”“切后台再回来呢”。对应做法分别是断网显示重试按钮而不是空白空数据显示“暂无数据”占位不要让 canvas 保持上一只票的残影切后台时停掉定时刷新onShow时重新拉一次最新数据否则回到前台会看到一段过期的分时线。演示顺序也有讲究。先讲架构分层让评委知道你有设计再打开日线演示十字光标和缩放这是最直观的然后切港股顺带讲复权和时区怎么处理的最后加一个自定义通达信指标把Script字符串在屏幕上展示一下——这一步是整套方案里最有辨识度的地方别放在最后草草带过。整个流程控制在五分钟内留时间回答问题。最后说个习惯。从我第一次做行情页开始每次提测前都会强制自己在一台旧安卓机上从头走一遍清缓存、断网、进页面、拖动、切股票、切后台再回来。这套流程走下来会翻出七八个模拟器上完全看不到的问题。图表这东西的玄学之处就在于它不报错只是画得不对而“不对”只有眼睛能发现。希望帮到你。本文还有配套的精品资源点击获取