周期与回放
只需给图表推送一路数据:逐笔成交,或某个基础周期(如 1 分钟)的 K 线。图表会按你所在市场的交易日历,由它合成所有更大的周期。行情回放也基于同一份源数据。
声明源数据
调用 set() 时说明这些 K 线的周期:
chart.data.set(minuteBars, { interval: '1m' });不传 interval 时,K 线按原样显示,下文的功能都不生效。传入之后,chart.data 的写入操作针对源数据,读取操作(at()、last()、toArray()、length)返回的是显示的 K 线。
周期写成「数量 + 单位」:15s、1m、5m、1h、4h、1D、2D、1W、1M、3M(m 是分钟,M 是月),也可以写成对象,如 { unit: 'hour', count: 4 }。
推送逐笔成交
socket.onmessage = (message) => {
const { time, price, size } = JSON.parse(message.data);
chart.data.addTrades({ time, price, size });
};落在最后一根源 K 线周期内的成交会更新它的最高价、最低价、收盘价和成交量;更晚的成交会开启一根新 K 线。属于更早周期的成交会抛出错误;被交易日历丢弃的成交(交易时段之外)会被跳过,addTrades() 返回实际合入的笔数。也可以从空数据开始:chart.data.set([], { interval: '1s' });逐笔成交和整根 K 线的 update() 可以混用。
切换周期
chart.data.setTimeframe('15m');
chart.data.setTimeframe(null); // 回到源周期切换周期时原地重新聚合源数据:不需要重新请求,指标、预警和对比序列都会跟着变化。显示周期必须由整数根源 K 线组成:
| 源周期 | 可合成的周期 |
|---|---|
1m | 2m、5m、15m、1h、4h、1D、1W、1M… |
1h | 2h、4h、1D、1W、1M… |
1D | 2D、1W、1M、3M… |
1M | 3M、6M、12M |
由 1m 合成 90s、由 7m 合成 1D(一天不能被 7 分钟整除)、由 1W 合成 1M 都会抛出错误。没有声明源周期时调用 setTimeframe() 也会抛出错误。
合成后的 K 线取第一根源 K 线的开盘价、最高的最高价、最低的最低价、最后一根的收盘价和成交量之和。它的时间是所在周期的起点:日内 K 线是所在时间段的起点,日线、周线、月线是交易日当地的零点。
用户往回滚动时,loadMore 给出的是第一根源 K 线的时间,所以可以继续按基础周期请求数据:
chart.on('loadMore', async ({ before }) => {
chart.data.prepend(await api.minuteBars({ end: before, limit: 2000 }));
});交易日历
calendar 配置项决定每个时间属于哪个周期:
createChart(element, {
timeZone: 'Asia/Shanghai', // 时间的显示时区
calendar: {
timeZone: 'America/New_York', // 市场所在时区,默认与 timeZone 相同
sessions: ['09:30-16:00'],
weekdays: [1, 2, 3, 4, 5]
}
});| 选项 | 默认值 | 含义 |
|---|---|---|
timeZone | null | 日历所在时区;null 时使用图表的 timeZone |
sessions | [] | 'HH:MM-HH:MM' 格式的交易时段,时段外的数据会被丢弃;为空表示全天交易 |
dayStart | '00:00' | 交易日的开始时间;从这个时间起属于下一个日期 |
weekdays | 全部 | 交易日的星期(0 = 周日);其他日期顺延到下一个交易日 |
weekStart | 1 | 周线从星期几开始 |
bucket | null | 自定义周期归属的钩子,见下文 |
日内 K 线不会跨越交易时段:它从时段的起点开始计算,所以在 ['09:30-11:30', '13:00-15:00'] 上,60 分钟 K 线的起点是 09:30、10:30、13:00 和 14:00。结束时间早于开始时间表示跨越午夜(如 '21:00-02:30')。夏令时切换会被正确处理。
示例
// 加密货币:全天交易,4 小时线按 UTC 对齐。
const crypto = { timeZone: 'UTC' };
// A 股:有午休。
const aShares = {
timeZone: 'Asia/Shanghai',
sessions: ['09:30-11:30', '13:00-15:00'],
weekdays: [1, 2, 3, 4, 5]
};
// 国内期货:夜盘归属下一个交易日,周五夜盘归属下周一。
const chinaFutures = {
timeZone: 'Asia/Shanghai',
sessions: ['21:00-02:30', '09:00-10:15', '10:30-11:30', '13:30-15:00'],
dayStart: '21:00',
weekdays: [1, 2, 3, 4, 5]
};
// 美股:只保留常规交易时段,盘前盘后的成交会被丢弃。
const usEquities = {
timeZone: 'America/New_York',
sessions: ['09:30-16:00'],
weekdays: [1, 2, 3, 4, 5]
};
// 外汇:以纽约时间 17:00 换日。
const fx = { timeZone: 'America/New_York', dayStart: '17:00', weekdays: [1, 2, 3, 4, 5] };
createChart(element, { calendar: chinaFutures });节假日与特殊规则
bucket 接收时间、周期和 fallback(同一时间的内置计算结果)。返回周期的起点,或者返回 null 丢弃这个时间。时间往后走时,周期不能往回退。
const holidays = new Set(['2024-10-01', '2024-10-02', '2024-10-03']);
chart.applyOptions({
calendar: {
bucket: (time, interval, fallback) => {
const date = new Date(time + 8 * 3_600_000).toISOString().slice(0, 10);
return holidays.has(date) ? null : fallback(time);
}
}
});修改交易日历后,数据会重新聚合。
对齐你自己的数据
createCalendar() 返回的就是图表内部使用的日历,可以用来处理服务端返回的数据、导出数据或自己做聚合:
import { createCalendar } from '@kumochart-sdk/core';
const calendar = createCalendar({
timeZone: 'Asia/Shanghai',
sessions: ['09:30-11:30', '13:00-15:00']
});
calendar.bucket('2024-05-06T10:15:00+08:00', '1h'); // 上海时间 09:30,以毫秒时间戳表示
calendar.bucket('2024-05-06T12:00:00+08:00', '1h'); // null:午休K 线类型
K 线变换会用周期本身的 K 线生成显示的 K 线:平均 K 线、砖形图或等幅 K 线。它位于周期之后,所以 5 分钟周期上的砖形图是用 5 分钟收盘价生成的;切换数据或周期时变换保持不变。这三种都是扩展功能,需要先注册一次,可以全部注册,也可以逐个注册。
import { registerExtras } from '@kumochart-sdk/core';
registerExtras(chart);
chart.data.setTransform({ type: 'heikin-ashi' });
chart.data.setTransform({ type: 'renko', params: { box: 50 } }); // 砖块大小 50
chart.data.setTransform({ type: 'range', params: { range: 120 } }); // 每根高低差 120
chart.data.setTransform({ type: 'renko' }); // 砖块大小取平均真实波幅
chart.data.setTransform(null); // 恢复为周期本身的 K 线| 类型 | 参数 | 显示的 K 线 |
|---|---|---|
heikin-ashi | 每根 K 线对应一根平滑后的蜡烛:收盘价取 OHLC 的平均值,开盘价取上一根实体的中点 | |
renko | box | 基于收盘价、对齐到砖块整数倍网格的砖块;反转需要两个砖块 |
range | range | 高低差恰好为 range 的 K 线;上涨的 K 线按开、低、高、收的顺序走完价格路径 |
- 不指定大小时,
renko和range使用最近 14 根 K 线的平均真实波幅,保留两位有效数字。推送新数据时大小保持不变,K 线重建时(新数据、切换周期或开始回放)重新计算。 - 指标、预警、标记和回放都基于变换后的 K 线。每个 tick 只重跑周期中的最后一根 K 线,所以实时推送仍是增量的。
- 砖块的时间取它开始形成的时间;在同一根 K 线中开始的砖块依次相隔 1 毫秒。砖形图和等幅 K 线不代表时间段,所以 K 线倒计时会隐藏。
- K 线如何绘制仍由主序列类型决定:蜡烛图适合所有变换,用折线画砖形图的收盘价也可以。
自定义变换
变换是一个流:按顺序接收周期的 K 线,返回每根 K 线完成的输出,并报告仍在形成中的那一根。clone() 让图表在每个 tick 只重跑最新的一根。
import type { Bar, BarTransformStream } from '@kumochart-sdk/core';
/** 每成交 `volume` 生成一根 K 线。 */
class VolumeBars implements BarTransformStream {
constructor(
private readonly volume: number,
private forming: Bar | null = null
) {}
next(bar: Bar): Bar[] {
const merged = this.forming
? {
...this.forming,
high: Math.max(this.forming.high, bar.high),
low: Math.min(this.forming.low, bar.low),
close: bar.close,
volume: this.forming.volume + bar.volume
}
: bar;
this.forming = merged.volume >= this.volume ? null : merged;
return this.forming ? [] : [merged];
}
current(): Bar | null {
return this.forming;
}
clone(): BarTransformStream {
return new VolumeBars(this.volume, this.forming);
}
}
chart.data.registerTransform<{ volume: number }>('volume', {
name: 'Volume bars',
keepsTime: false,
create: ({ volume }) => new VolumeBars(volume)
});
chart.data.setTransform({ type: 'volume', params: { volume: 5000 } });create(params, input) 负责校验参数,也可以读取即将收到的 K 线(input),用来计算自动大小之类的统计量。如果每根输入都恰好变成一根时间相同的输出(例如平均 K 线),请设置 keepsTime,图表会保留 K 线倒计时。输出的时间不能递减,所有数值都必须是有限数。
行情回放
回放会隐藏时间游标之后的源数据。所有功能都只能看到已回放的部分:显示周期、指标、预警、对比序列和标记。在更大的周期上,正在形成的那根 K 线由已回放的源 K 线拼出,所以在小时图上回放分钟数据,可以看到每根小时 K 线逐步成形。
chart.replay.start('2024-05-06T10:00:00+08:00', { speed: 2, step: 'bar' });
chart.replay.play();
chart.replay.pause();
chart.replay.step(); // 前进一根 K 线
chart.replay.configure({ step: 'source' });
chart.replay.step(5); // 前进五根源 K 线
chart.replay.seek('2024-05-07T09:30:00+08:00'); // 可前进也可后退
chart.replay.stop(); // 重新显示全部数据step: 'bar'会补完正在形成的显示 K 线,或显示下一根;step: 'source'每次放出一根源 K 线。speed是播放时每秒的步数。state()返回{ time, playing, ended, speed, step };显示全部数据时返回null。- 回放期间实时数据照常接收(
update()、addTrades()),直到游标走到才显示。切换周期不会改变游标。 - 新放出的 K 线会照常触发预警,
alertTrigger事件中带有replay: true。 clear()会结束回放。
播放过程会报告进度;和其他 API 调用一样,上面这些方法本身不会触发事件:
chart.on('replay', ({ state, reason }) => {
progress.value = state.time;
if (reason === 'end') {
showSummary();
}
});常见的回放界面会让用户点击选择起始 K 线:
chart.on('click', ({ time }) => {
if (pickingStart && time !== null) {
chart.replay.start(time);
chart.replay.step(); // 补完被点击的那根 K 线
pickingStart = false;
}
});Vue 与 React
两个组件都通过属性接收源周期、显示周期和 K 线变换:
<TradingChart
:data="minuteBars"
interval="1m"
:timeframe="timeframe"
:configure="registerExtras"
:transform="{ type: 'renko' }"
/><TradingChart
data={minuteBars}
interval="1m"
timeframe={timeframe}
configure={registerExtras}
transform={{ type: 'renko' }}
/>再次传入相同的变换(例如直接写在模板里的对象)不会重建 K 线。configure 会在应用变换之前注册扩展功能。
推送逐笔成交和控制回放请通过图表实例(@ready / onReady 或 ref);replay 事件和其他事件一样会被转发。