2026-08-13 20:59:05 +08:00
2026-08-13 20:59:05 +08:00
2026-08-13 20:59:05 +08:00
2026-08-13 21:03:50 +08:00
2026-08-13 20:59:05 +08:00
2026-08-13 21:02:03 +08:00

KLineChart CSV 可视化 Demo — 项目说明书

1. 项目概览

基于 KLineChart v10 的独立 K 线图表可视化 Demo,支持从本地 CSV 文件加载 OHLCV 历史数据,提供周期切换、指标叠加、深浅主题、缩放拖拽、截图导出等完整交互能力。

技术栈:

技术 版本 用途
KLineChart 10.0.2 核心 K 线渲染引擎
Vite 5.x 本地开发服务器
Canvas 2D 原生 图表绘制

2. 文件结构

debug/
├── index.html            # 页面结构:工具栏 + 图表容器
├── main.js               # 全部业务逻辑(约 600 行)
├── style.css             # 界面样式,含深色主题
├── package.json          # 依赖声明(klinecharts + vite)
├── package-lock.json     # 版本锁定
└── data/
    └── eurgbp_m1.csv     # 示例数据(EUR/GBP 1分钟,2021-2026,约 186 万行)

3. 功能清单

功能 操作方式 说明
CSV 加载 📂 按钮选择 / 拖拽 自动识别 5 种 CSV 格式
周期切换 1m / 5m / 15m / 1h / 1d 实时聚合原始 1m 数据
缩放 +/−/⟲ 按钮、鼠标滚轮、键盘 +/− barSpace 范围 2–50
翻页 ←/→ 键盘、拖拽、滚动条 分页加载(每次 500 根)
指标 工具栏按钮 或 右键菜单 MA / BOLL / MACD / RSI / VOL
深色主题 ☾/☀ 按钮 或 D 键 chart.setStyles('dark')
截图 ▣ 按钮 或 S 键 getConvertPictureUrl() 下载 PNG
十字光标面板 鼠标移动 顶部实时显示 OHLCV + 涨跌幅
柱体详情 左键点击柱体 弹出详细 OHLCV 浮窗
右键菜单 右键图表 切换指标 / 主题 / 截图
自定义指标 registerIndicator() 扩展任意指标

4. CSV 数据格式

自动识别以下格式(列顺序可自定义,支持带表头/无表头):

格式 A:Binance 格式(时间戳 + OHLCV)

1630000000000,0.00145000,0.00145000,0.00144000,0.00144000,5259702

格式 B:带表头格式

timestamp,open,high,low,close,volume
1630000000000,0.00145000,0.00145000,0.00144000,0.00144000,5259702

格式 C:点号日期格式(当前示例)

2021.07.06,00:10,0.85627,0.85629,0.85625,0.85628,7000000.0

列顺序:日期, 时间, O, H, L, C, V

格式 D:横杠日期格式

2021-07-06,00:10,0.85627,0.85629,0.85625,0.85628,7000000.0

格式 E:YYYYMMDD 格式

20210706,0.85627,0.85629,0.85625,0.85628,7000000.0

时间戳自动处理: 13 位数字 → 毫秒;10 位数字 → 秒(自动 ×1000)

5. 快速开始

cd debug
npm install
npm run dev

浏览器打开 http://localhost:1789,拖入或点击 📂 选择 CSV 文件即可。

6. 核心代码结构(main.js)

┌─────────────────────────────────────┐
│  loadRawCSV(source)                 │  1. 读取文件(File 或 URL)
│    └─ parseCSV(text)                │     2. 自动识别格式,解析到 TypedArray
├─────────────────────────────────────┤
│  aggregateBars(data, range, span)   │  3. 1m → 5m/15m/1h/1d 聚合
├─────────────────────────────────────┤
│  loadDataLoader()                    │  4. 返回 setDataLoader 参数
│    └─ getBars({type, timestamp…})   │     5. init/forward/backward 分页
├─────────────────────────────────────┤
│  mountChart(source)                  │  6. init → setSymbol → setPeriod
│    ├─ chart.setDataLoader(...)      │     7. setDataLoader → 图表加载数据
│    ├─ chart.createIndicator(...)    │     8. 创建指标
│    └─ subscribeAction(...)          │     9. 绑定 crosshair/click 事件
└─────────────────────────────────────┘

7. 自定义指标开发

7.1 注册指标

在 init() 调用之前注册:

import { registerIndicator, init } from 'klinecharts'

registerIndicator({
  name: 'MYAVG',
  shortName: 'MYAVG',
  series: 'price',
  calcParams: [5, 10, 20],
  precision: 5,
  shouldOhlc: true,
  figures: [
    { key: 'a1', title: 'A5: ', type: 'line' },
    { key: 'a2', title: 'A10: ', type: 'line' },
    { key: 'a3', title: 'A20: ', type: 'line' }
  ],
  regenerateFigures: (params) =>
    params.map((p, i) => ({ key: `a${i+1}`, title: `A${p}: `, type: 'line' })),
  calc: (dataList, indicator) => {
    const sums = indicator.calcParams.map(() => 0)
    return dataList.map((bar, i) => {
      const out = {}
      indicator.calcParams.forEach((p, idx) => {
        sums[idx] += bar.close
        if (i >= p - 1) out[`a${idx+1}`] = sums[idx] / (i + 1)
      })
      return out
    })
  }
})

// 注册后再 init
const chart = init('chart', { /* options */ })
chart.createIndicator({ name: 'MYAVG', paneId: 'new' })

7.2 字段说明

字段 必需 类型 说明
name 是 string 唯一标识,用于 createIndicator({ name }) 匹配
shortName 是 string 工具提示中显示的简称
series 是 'price' | 'volume' | 'normal' 系列类型,决定 Y 轴行为
calcParams 否 number[] 计算参数,可被 overrideIndicator 修改
precision 否 number 结果数值的小数位数
shouldOhlc 否 boolean 计算是否需要 OHLC 字段
figures 是 Figure[] 图形配置,定义画几条线/柱
calc 是 () => D[] 核心计算函数
minValue / maxValue 否 number 固定 Y 轴最小/最大值
styles 否 Partial<IndicatorStyle> 覆盖默认配色

7.3 Figure 类型

type 说明
line 折线
bar 柱状图(支持 baseValue: 0 画零轴柱)
circle 圆点
text 文字标注

每个 figure 至少需要 key 和 type,key 对应 calc 返回数据中的字段名。

7.4 计算函数约定

calc: (dataList: KLineData[], indicator) => D[]
  • dataList 是当前可见窗口内的 K 线数据(每根含 timestamp/open/high/low/close/volume)
  • 返回数组长度等于 dataList 长度
  • 每项为对象,键名与 figures[].key 对应
  • 数据不足时某键可省略(不会画该点),例如 EMA 前 N 个点

8. KLineChart v10 API 速查

8.1 图表生命周期

方法 说明
init(container, options?) 创建图表实例
dispose(container) 销毁,释放资源
setSymbol({ ticker, pricePrecision, volumePrecision }) 设置交易品种信息
setPeriod({ span, type }) 设置周期,会触发数据重载
setDataLoader(loader) 设置数据加载器
resetData() 强制重新加载数据

调用顺序: init → setSymbol → setPeriod → setDataLoader

8.2 指标

方法 说明
createIndicator(name, isStack?) 添加指标,isStack=true 叠加主图
createIndicator({ name, paneId: 'new' }) 新建独立面板
createIndicator({ name, paneId: 'candle_pane' }, true) 叠加主图
removeIndicator({ name }) 移除指标
overrideIndicator({ id, calcParams: [...] }) 修改指标参数
registerIndicator(template) 注册自定义指标(需 init 前调用)
getSupportedIndicators() 获取内置指标名列表

8.3 样式与布局

方法 说明
setStyles('dark' | 'light' | partial) 切换主题或局部修改样式
setBarSpace(value) 设置柱间距(等同于缩放)
getBarSpace() 获取当前 barSpace {bar, gapBar}
setPaneOptions({ paneId, height }) 设置面板高度
scrollToRealTime() 滚动到最新数据
scrollByDistance(dist) 按像素距离滚动

8.4 事件订阅

Action 类型 触发时机 回调参数
onCrosshairChange 鼠标移动 { x, y, paneId, kLineData, timestamp, dataIndex }
onCandleBarClick 点击柱体 同上
onZoom 滚轮缩放 barSpace
onScroll 拖动翻页 —
onVisibleRangeChange 可见范围变化 起始/结束索引
onPaneDrag 面板高度拖拽 paneId, height

8.5 截图与导出

const url = chart.getConvertPictureUrl(
  false,   // 是否包含叠加层
  'png',   // 'png' | 'jpeg' | 'bmp'
  '#ffffff' // 背景色
)

9. 内置指标一览

指标 叠加/独立 说明
MA 叠加 简单移动平均,参数默认 6/12/20
EMA 独立 指数移动平均
BOLL 叠加 布林带
MACD 独立 异同移动平均
RSI 独立 相对强弱指数
SAR 独立 抛物线转向
BBI 叠加 多空指标
SMA 叠加 简易平均线
VOL 独立 成交量

10. 扩展场景

10.1 接入后端 API

替换 loadRawCSV() 中的 fetch('/data/eurgbp_m1.csv') 为后端接口:

function loadRawCSV(source) {
  return fetch('/api/kline?symbol=BTCUSDT&limit=1000')
    .then(r => r.json())
    .then(rows => {
      // rows 格式: [{t: 1630000000000, o: ..., h: ..., l: ..., c: ..., v: ...}]
      // 转换为内部格式 {ts, o, h, l, c, v, len}
      const count = rows.length
      return {
        ts: new Float64Array(rows.map(r => r.t)),
        o:  new Float64Array(rows.map(r => r.o)),
        h:  new Float64Array(rows.map(r => r.h)),
        l:  new Float64Array(rows.map(r => r.l)),
        c:  new Float64Array(rows.map(r => r.c)),
        v:  new Float64Array(rows.map(r => r.v || 0)),
        len: count
      }
    })
}

getBars 中的 forward/backward 分页自动交给后端处理。

10.2 WebSocket 实时推送

chart.setDataLoader({
  getBars: ({ type, timestamp, period, callback }) => { /* REST 历史 */ },
  subscribeBar: ({ symbol, period, callback }) => {
    ws = new WebSocket(`wss://.../ws?symbol=${symbol}`)
    ws.onmessage = (evt) => {
      const bar = JSON.parse(evt.data)
      // 回调单根 K 线,相同 timestamp 更新最后根
      callback(bar)
    }
  },
  unsubscribeBar: () => { ws.close() }
})

10.3 多图表联动

const chart1 = init('chart1')
const chart2 = init('chart2')

chart1.subscribeAction('onCrosshairChange', (crosshair) => {
  // 通过 executeAction 同步到另一图表
  chart2.executeAction('onCrosshairChange', { x: crosshair.x, y: null })
})

11. 故障排查

现象 可能原因 解决方法
图表空白 调用顺序不对 确认 init → setSymbol → setPeriod → setDataLoader
无数据 getBars 未调用 callback 确保 callback 一定被调用(即使失败)
时间错乱 时间戳单位错误 确认 timestamp 为毫秒
分页失败 more 标记错误 检查 { forward, backward } 返回值
指标不显示 name 不匹配 createIndicator 的 name 必须等于注册时的 name
滚动条消失 data 太少 数据量 < 2 不会显示滚动条
深色/浅色切换失败 样式名拼写错误 使用 'dark' / 'light'
S
Description
No description provided
Readme
84 KiB
Languages
JavaScript 76.3%
CSS 17.2%
HTML 6.5%