KLineChart 使用教程
KLineChart 使用教程
KLineChart 是一个基于 HTML5 Canvas 的轻量级金融 K 线图库:零第三方依赖,gzip 后仅约 40KB,内置多种技术指标和画线工具,支持移动端和 TypeScript,可用于任何前端框架(React / Vue / Angular / Svelte / 原生 JS 均可)。
目录
一、安装
包管理器(推荐)
npm install klinecharts
# 或
yarn add klinecharts
# 或
pnpm install klinecharts
# 或
bun add klinecharts
CDN(UMD 版,全局变量 klinecharts)
<script src="cdn.jsdelivr.net/npm/klinecharts/dist/umd/klinecharts.min.js"></script>
二、创建第一个图表
核心三步:init 初始化 → setSymbol 设置标的 → setPeriod 设置周期 → setDataLoader 喂数据。
原生 HTML
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<script src="cdn.jsdelivr.net/npm/klinecharts/dist/umd/klinecharts.min.js"></script>
</head>
<body>
<div id="chart" style="width:600px;height:600px"></div>
<script>
const chart = klinecharts.init('chart')
chart.setSymbol({ ticker: 'TestSymbol' })
chart.setPeriod({ span: 1, type: 'day' })
chart.setDataLoader({
getBars: ({ callback }) => {
callback([
{ timestamp: 1517846400000, open: 7424.6, high: 7511.3, low: 6032.3, close: 7310.1, volume: 224461 },
{ timestamp: 1517932800000, open: 7310.1, high: 8499.9, low: 6810, close: 8165.4, volume: 148807 },
{ timestamp: 1518019200000, open: 8166.7, high: 8700.8, low: 7400, close: 8245.1, volume: 24467 },
{ timestamp: 1518105600000, open: 8244, high: 8494, low: 7760, close: 8364, volume: 29834 },
{ timestamp: 1518192000000, open: 8363.6, high: 9036.7, low: 8269.8, close: 8311.9, volume: 28203 }
])
}
})
</script>
</body>
</html>
React
import { useEffect } from 'react'
import { init, dispose } from 'klinecharts'
export default () => {
useEffect(() => {
const chart = init('chart')
chart.setSymbol({ ticker: 'TestSymbol' })
chart.setPeriod({ span: 1, type: 'day' })
chart.setDataLoader({
getBars: ({ callback }) => { callback(/* KLineData 数组 */) }
})
return () => dispose('chart') // 组件卸载时销毁图表
}, [])
return <div id="chart" style={{ width: 600, height: 600 }} />
}
Vue 3
<script setup>
import { onMounted, onUnmounted } from 'vue'
import { init, dispose } from 'klinecharts'
let chart
onMounted(() => {
chart = init('chart')
chart.setSymbol({ ticker: 'TestSymbol' })
chart.setPeriod({ span: 1, type: 'day' })
chart.setDataLoader({ getBars: ({ callback }) => { callback(/* ... */) } })
})
onUnmounted(() => dispose('chart'))
</script>
<template>
<div id="chart" style="width:600px;height:600px" />
</template>
数据格式(KLineData):每根 K 线一个对象,timestamp 必须是毫秒:
interface KLineData {
timestamp: number // 毫秒时间戳
open: number
high: number
low: number
close: number
volume: number
turnover?: number // 可选:成交额
}
三、数据接入:DataLoader
setDataLoader 是图表取数据的唯一入口,包含三个方法:
interface DataLoader {
getBars: (params) => void | Promise<void> // 拉取历史数据
subscribeBar: (params) => void // 订阅实时数据
unsubscribeBar: (params) => void // 取消订阅
}
getBars:历史数据(支持分页)
chart.setDataLoader({
getBars({ type, timestamp, symbol, period, callback }) {
// type: 'init' 初始加载 / 切换品种周期后重新加载(timestamp 为 null)
// 'forward' 向左滚动,加载更早的数据
// 'backward' 向右滚动,加载更新的数据
// timestamp: 分页锚点(forward 时通常为最左一根的时间戳)
callback(bars, more)
}
})
callback 的 more 参数:告诉图表某方向是否还有更多数据,用于继续触发分页:
callback(bars, { forward: true, backward: false }) // 还有更早数据
callback(bars, false) // 两个方向都没有了
合并逻辑由库自动处理:init 替换全部数据,forward 前插,backward 后插。
注意事项:
- 返回前先按
timestamp排序,避免重复时间戳; - 请求竞态时以当前品种/周期的最新请求为准。
subscribeBar:实时数据
初始 getBars 完成后调用,用于接收实时单条推送:
subscribeBar({ symbol, period, callback }) {
// 有新数据时调用 callback(单条 KLineData)
}
推送规则(库内部自动处理):
- 新
timestamp大于最后一根 → 追加为新的一根; - 等于最后一根 → 覆盖最后一根(即当前周期的实时更新);
- 小于 → 视为过期数据,忽略。
每次只推一条数据,且必须代表当前周期的那根 K 线。
unsubscribeBar:取消订阅
在 setSymbol / setPeriod / resetData / 销毁图表时触发。建议在 subscribeBar 里保存清理函数(如关闭 WebSocket),在 unsubscribeBar 里执行。
四、接入真实数据(REST + WebSocket)
一个完整的模板:
const stopMap = new Map() // 保存每个订阅的清理函数
chart.setDataLoader({
async getBars({ type, timestamp, symbol, period, callback }) {
const res = await api.getKlineList({
symbol: symbol.ticker,
period: `${period.span}${period.type}`,
endTime: timestamp ?? Date.now(),
limit: 500,
direction: type,
})
const bars = res.list
.map(normalizeToKLineData) // 见下方字段映射
.sort((a, b) => a.timestamp - b.timestamp)
callback(bars, {
forward: res.hasMoreBefore,
backward: res.hasMoreAfter,
})
},
subscribeBar({ symbol, period, callback }) {
const ws = createWsConnection(symbol.ticker, period)
ws.onmessage = (message) => {
callback(normalizeToKLineData(JSON.parse(message.data)))
}
stopMap.set(makeKey(symbol, period), () => ws.close())
},
unsubscribeBar({ symbol, period }) {
const key = makeKey(symbol, period)
stopMap.get(key)?.()
stopMap.delete(key)
},
})
后端字段与 KLineData 不一致时,先做映射:
// 后端:{ t: 1711425600, o: "68000.1", h: "68920.5", l: "67500.2", c: "68610.8", v: "1234.56" }
function normalizeToKLineData(d) {
return {
timestamp: d.t * 1000, // 秒 → 毫秒
open: Number(d.o),
high: Number(d.h),
low: Number(d.l),
close: Number(d.c),
volume: Number(d.v),
}
}
五、技术指标
内置指标
库内置了 MA、EMA、BOLL、SAR、MACD、KDJ、RSI、VOL 等常用指标。查看当前版本支持的指标:
import { getSupportedIndicators } from 'klinecharts'
console.log(getSupportedIndicators())
添加 / 移除指标
import { createIndicator, removeIndicator } from 'klinecharts'
// 主图叠加 MA(isStack=true 表示叠加到已有副图/主图上)
chart.createIndicator('MA', true, { id: 'candle_pane' })
// 新建副图显示 MACD
chart.createIndicator('MACD', false, { id: 'pane_macd' })
// 移除
chart.removeIndicator('pane_macd', 'MACD')
自定义指标
import { registerIndicator, utils } from 'klinecharts'
registerIndicator({
name: 'MyMA',
shortName: 'MyMA',
calcParams: [5, 10], // 可调参数
figures: [
{ key: 'ma5', title: 'MA5: ', type: 'line' },
{ key: 'ma10', title: 'MA10: ', type: 'line' },
],
calc: (dataList, indicator) => {
const { calcParams } = indicator
const closeSums = [0, 0]
return dataList.map((kLineData, i) => {
const ma = {}
closeSums.forEach((sum, j) => {
closeSums[j] = sum + kLineData.close
if (i >= calcParams[j] - 1) {
ma[`ma${calcParams[j]}`] = closeSums[j] / calcParams[j]
closeSums[j] -= dataList[i - calcParams[j] + 1].close
}
})
return ma
})
},
})
chart.createIndicator('MyMA', true, { id: 'candle_pane' })
六、样式配置
通过 setStyles 深度定制图表外观(支持部分更新):
chart.setStyles({
grid: {
show: true,
horizontal: { color: '#333' },
vertical: { show: false },
},
candle: {
bar: {
upColor: '#26a69a', upBorderColor: '#26a69a',
downColor: '#ef5350', downBorderColor: '#ef5350',
},
priceMark: { high: { show: true }, low: { show: true } },
tooltip: {
showRule: 'always', // 'always' | 'follow_cross' | 'none'
text: { size: 12 },
},
},
indicator: {
bars: [{ key: '*', upColor: '#26a69a', downColor: '#ef5350' }],
lines: [{ size: 1, style: 'solid' }],
},
crosshair: {
show: true,
horizontal: { text: { show: true } },
},
})
暗色主题:
chart.setStyles('dark') // 内置 'light' / 'dark' 两种预设
七、画线与标注(Overlay)
内置画线工具:线段、矩形、平行线、斐波那契回调、简单标注等。
import { createOverlay, removeOverlay, getOverlayClass } from 'klinecharts'
// 简单标注(如买卖点提示)
chart.createOverlay({ name: 'simpleAnnotation', points: [{ timestamp: 1517932800000, value: 8165.4 }] })
// 线段
chart.createOverlay({
name: 'segment',
points: [
{ timestamp: 1517846400000, value: 6032.3 },
{ timestamp: 1518624000000, value: 9774 },
],
})
// 移除全部
chart.removeOverlay()
八、常用 API 速查
| API | 说明 |
|---|---|
init(dom, options?) |
初始化图表,返回图表实例 |
dispose(dom | instance) |
销毁图表 |
setSymbol({ ticker, pricePrecision?, volumePrecision? }) |
设置标的 |
setPeriod({ span, type }) |
设置周期,type:second/minute/hour/day/week/month/year |
setDataLoader(loader) |
设置数据加载器 |
resetData() |
重新拉取数据 |
getSymbol() / getPeriod() |
获取当前标的 / 周期 |
setStyles(styles | 'light' | 'dark') |
设置样式 / 主题 |
getStyles() |
获取样式 |
createIndicator(name, isStack?, paneOptions?) |
创建指标 |
removeIndicator(paneId, name) |
移除指标 |
getSupportedIndicators() |
所有支持的指标 |
registerIndicator(config) / unregisterIndicator(name) |
注册 / 注销自定义指标 |
createOverlay(overlay) / removeOverlay(...) |
画线 / 标注 |
setPaneOptions({ id, height?, state? }) |
设置窗格 |
scrollToTimestamp(ts) / scrollToDataIndex(i) |
滚动定位 |
subscribeAction(type, callback) |
订阅事件:'tooltip'、'crosshair'、'pane'、'candle_bar' 等 |
setPriceVolumePrecision(pricePrecision, volumePrecision) |
设置价格成交量精度 |
registerLocale(locale, messages) |
注册语言包(i18n) |
九、常见问题(FAQ)
1. 蜡烛显示成一条线 / 价格全被抹平?
设置标的时没有指定精度:
chart.setSymbol({ ticker: 'TestSymbol', pricePrecision: 6, volumePrecision: 6 })
2. 图表空白不显示?
- 容器必须有宽高(
div不能是 0×0); timestamp必须是毫秒(后端秒级时间戳要* 1000);- 数据必须按时间升序。
3. React 组件里图表初始化失败?
init 要在 DOM 挂载后调用(放在 useEffect 中,不要放在渲染期);卸载时调用 dispose。
4. 实时数据不更新?
subscribeBar 的 callback 每次只能推一条数据,且时间戳要 ≥ 最后一根;不要一次推一个数组。
5. 想开箱即用带顶栏和周期切换的完整版?
看官方基于 KLineChart 封装的 KLineChart Pro
- 点赞
- 收藏
- 关注作者
评论(0)