timestamp 必须是毫秒级时间戳• timestamp , open , close , high , low , volume , turnover 必须是数字类型本文档介绍把历史数据和实时数据接入到图表中。
数据接入核心就是:
setSymbol(...) 设置交易对setPeriod(...) 设置周期setDataLoader(...) 设置数据加载器其中 setDataLoader(...) 里面的三个函数:
getBars:返回历史数据,初始化和分页都靠它subscribeBar:历史数据加载完成后,开始推送最新一条数据unsubscribeBar:切换交易对、切换周期或销毁图表时,停止实时订阅真实项目里,通常会遇到下面几种数据源组合:
getBars 请求 REST 接口subscribeBar 订阅 WebSocketsubscribeBar 内部通过 setInterval 定时拉取最新一条数据getBars 从本地数组切片subscribeBar 按时间推进推送下一根数据无论你的数据来自哪里,最终都要落到同一件事上:
KLineData[]KLineData图表接收的历史数据需要满足固定格式,setDataLoader 中的历史数据和实时数据最终都需要转换成这个结构:
{
// 时间戳,毫秒级别,必要字段
timestamp: number
// 开盘价,必要字段
open: number
// 收盘价,必要字段
close: number
// 最高价,必要字段
high: number
// 最低价,必要字段
low: number
// 成交量,非必须字段
volume: number
// 成交额,非必须字段,如果需要展示技术指标 'EMV' 和 'AVP',则需要为该字段填充数据
turnover: number
}timestamp 必须是毫秒级时间戳• timestamp , open , close , high , low , volume , turnover 必须是数字类型你的后端字段通常不会刚好和 KLineData 一致,所以接入时一般要先做一次标准化。
假设后端返回:
{
t: 1711425600,
o: '68000.1',
h: '68920.5',
l: '67500.2',
c: '68610.8',
v: '1234.56'
}可以映射成:
function normalizeToKLineData(data: any) {
return {
timestamp: data.t * 1000,
open: Number(data.o),
high: Number(data.h),
low: Number(data.l),
close: Number(data.c),
volume: Number(data.v),
}
}如果你的接口返回的是数组,也建议在进入 callback(...) 之前统一做一层 map(normalizeToKLineData)。
setDataLoader({ getBars, subscribeBar, unsubscribeBar }) 中的三个函数 getBars 是必须要实现的,如果你不需要实时更新,可以不实现 subscribeBar 和 unsubscribeBar 。
getBars 。setDataLoader 的 getBars 负责在需要历史数据时拉取并回填。
getBars 的参数签名来自图表内部的数据加载契约:
getBars: ({
type,
timestamp,
symbol,
period,
callback
}: DataLoaderGetBarsParams) => void | Promise<void>你可以直接把它理解成:
callback(...) 回传给图表其中关键含义:
typeinit:初始化/切换交易对或周期后触发。此时 timestamp = null。forward:用于向左边界补充更早的数据(拖到左侧边界触发)。backward:用于向右边界补充更晚的数据(拖到右侧边界触发)。timestampforward:通常为当前最左一根数据的 timestampbackward:通常为当前最右一根数据的 timestampinit:为 nullcallback(data, more)data:KLineData[]more:用于告诉图表“左右边界是否还有更多数据” boolean(表示左右都相同){ forward?: boolean, backward?: boolean } 分别控制左右最常见的实现方式:
init:拉取最近一段历史数据forward:根据左边界 timestamp 拉取更早的数据backward:根据右边界 timestamp 拉取更新的数据如果你的接口只支持“向前翻页”,也可以先只正确处理 init 和 forward。
more 的作用不是告诉图表“本次返回了多少数据”,而是告诉图表“这个方向上后面还有没有更多数据”。
例如:
callback(bars, { forward: true })callback(bars, { forward: false })callback(bars, false)一个实用判断方式是:
hasMore / nextCursor,优先使用后端结果type: 'init':清空已有数据,并用新的数组覆盖当前数据。type: 'forward':把新数据拼接到数组前面(补充左侧更早的 K 线)。type: 'backward':把新数据拼接到数组后面(补充右侧更晚的 K 线)。more 只影响“后续还能否继续触发向左/向右分页加载”。getBars 里直接返回未排序的数据图表在执行 init 的 getBars 回调完成后(也就是历史数据就绪后)才会调用 subscribeBar。
subscribeBar 的参数签名:
subscribeBar: ({
symbol,
period,
callback
}: DataLoaderSubscribeBarParams) => void其中:
callback(data: KLineData):当你的实时源收到一条数据时,把它标准化成 KLineData 并回传给图表。data.timestamp 为毫秒级时间戳。• 推送的是“当前周期对应的那一条数据”,不是任意成交明细。• 当时间进入下一个周期后,再推送的新数据应该使用新的 timestamp 。当图表收到一根实时 K 线时,会按 data.timestamp 与当前最后一根做合并:
data.timestamp 更大:追加为新的最后一根data.timestamp 相同:用新值覆盖最后一根data.timestamp 更小:作为旧数据忽略(不进行插入)当你调用 setSymbol / setPeriod / resetData / dispose 对图表进行重置或销毁时,图表内部会触发 unsubscribeBar。
最佳实践:
dataLoader 侧维护一个“订阅句柄/关闭函数”的 MapsubscribeBar 建立订阅并把关闭函数存起来unsubscribeBar 拿到对应关闭函数并停止推送下面这个示例展示了“REST 拉历史 + WebSocket 推实时”的典型思路:
chart.setDataLoader({
async getBars({ type, timestamp, symbol, period, callback }) {
const response = await api.getKlineList({
symbol: symbol.ticker,
period: `${period.span}${period.type}`,
endTime: timestamp ?? Date.now(),
limit: 500,
direction: type,
})
const bars = response.list
.map(normalizeToKLineData)
.sort((a, b) => a.timestamp - b.timestamp)
callback(bars, {
forward: response.hasMoreBefore,
backward: response.hasMoreAfter,
})
},
subscribeBar({ symbol, period, callback }) {
const key = makeKey(symbol, period)
const ws = createWsConnection(symbol.ticker, period)
ws.onmessage = (message) => {
const bar = normalizeToKLineData(JSON.parse(message.data))
callback(bar)
}
stopMap.set(key, () => ws.close())
},
unsubscribeBar({ symbol, period }) {
const key = makeKey(symbol, period)
stopMap.get(key)?.()
stopMap.delete(key)
},
})getBars 的实现里一定会调用 callback(data) 返回 KLineData[]timestamp 是毫秒级setSymbol 与 setPeriod 已设置callback(bars, more) 里检查 more.forward/backward 是否正确返回subscribeBar 内部确实调用了 callback(KLineData)timestamp 是否小于最新一条数据的 timestampvolume、turnover 是否正确传递