ECharts K 线图
看过股市的朋友们对 K线图 这个图形肯定都不陌生。K线又称“阴阳烛”、“蜡烛线”,是反映价格走势的一种图线,其特色在于一个线段内记录了多项讯息,相当易读易懂且实用有效,广泛用于股票、期货、贵金属、数字货币等行情的技术分析,称为K线分析。
1. 简介
慕课解释
K 线图又称蜡烛图,最初起源于日本德川幕府时期,被用于记录米市的行情与价格波动,后被引入股票与证券交易市场,现如今已经在金融交易活动中大范围使用。K 线图在形态上很像柱状图与折线图的组合,但每个数据节点能够表达出更多的信息。在股票交易中,k 线图通常包含四个维度,即开盘价、收盘价、最高价、最低价的涨跌变化状况,一个典型的 k 线图如下图所示:
包含三个部分:
- 上方的细线称为上影线,表示最高价与收盘价的价差;
- 下方的细线称为下影线,表示最低价与开盘价的价差;
- 中间实体部分表示开盘价与收盘价的价差,当收盘价高于开盘价时称为阳线,在国内通常使用红色实体表示;当收盘价低于开盘价时称为阴线,在国内通常使用绿色实体表示。
2. 实例解说
2.1 基础 K 线图
K 线图在 ECharts 中通过 series.type = k
或 series.type = candlestick
配置,示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<div id="main" style="width: 600px;height: 400px"></div>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
<script type="text/javascript">
const myChart = echarts.init(document.getElementById('main'));
const option = {
toolbox: { feature: { saveAsImage: {} } },
xAxis: { type: 'category', data: ['2017-10-24', '2017-10-25', '2017-10-26', '2017-10-27'] },
yAxis: { type: 'value' },
series: [
{
data: [
// 开盘价、收盘价、最高价、最低价
[20, 30, 10, 35],
[40, 35, 30, 55],
[33, 38, 33, 40],
[40, 40, 32, 42],
],
type: 'k',
},
],
};
myChart.setOption(option);
</script>
</body>
</html>
示例效果:
k 线图是一种基于直角坐标系的图表,可以通过 xAxis
、yAxis
、grid
项声明坐标系属性。
k 线图的 series.data
项是一个长度为 4 的数组,按次序分别对应 k 线图的开盘价、收盘价、最高价、最低价。
2.2 颜色配置
K 线图通过实体颜色表达数据涨、跌,不同国家或地区对于 K 线图的颜色定义不一样,可能是红涨绿跌或红涨蓝跌(如台湾、日本、韩国等),可能是绿涨红跌(如西方国家、香港、新加坡等),也可能是有色/无色等表示方法。默认配置项是红涨蓝跌,可通过如下配置项更改:
配置名 | 类型 | 默认值 | 说明 |
---|---|---|---|
itemStyle.color | string | #c23531 | 阳线填充色,用于表达”涨“势 |
itemStyle.color0 | string | #314656 | 阴线填充色,用于表达”跌“势 |
itemStyle.borderColor | string | #c23531 | 阳线边框色,用于表达”涨“势 |
itemStyle.borderColor0 | string | #314656 | 阴线边框色,用于表达”跌“势 |
下例通过 itemStyle
属性,修改 k 线图的颜色:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<div id="main" style="width: 600px;height: 400px"></div>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
<script type="text/javascript">
const myChart = echarts.init(document.getElementById('main'));
const option = {
toolbox: { feature: { saveAsImage: {} } },
xAxis: { type: 'category', data: ['2017-10-24', '2017-10-25', '2017-10-26', '2017-10-27'] },
yAxis: { type: 'value' },
series: [
{
data: [
// 开盘价、收盘价、最高价、最低价
[20, 30, 10, 35],
[40, 35, 30, 55],
[33, 38, 32, 40],
[40, 48, 32, 50],
],
itemStyle: {
color: '#d87c7c',
color0: '#919e8b',
borderColor: '#d87c7c',
borderColor0: '#919e8b',
},
type: 'k',
},
],
};
myChart.setOption(option);
</script>
</body>
</html>
示例效果:
与柱状图、折线图等其他直角坐标图表不同,在一个坐标系中无论存在多少个 k 线图,默认都会被处理为红涨蓝跌,此时必须通过 itemStyle
修改阴阳线的色值,详见下一节。
2.3 多个 K 线图
由于 k 线图的数据项只能通过这种 4 位数组的格式定义,这会导致 k 线图与折线图、柱状图、散点图等其他直角坐标图表有些许不同:
- x、y 轴中必须有一条是类目轴,根据用户习惯,通常会选择 x 轴做类目轴;
- k 线图无法通过
series.data
推断类目属性,所以类目轴必须通过axis.data
项显式声明类目数据; - k 线图的
series.data
与类目轴的axis.data
根据数据出现的位置关联。
当序列上只有一个 k 线图时,问题不大,但若要在同一坐标系上渲染多个 k 线图时,则需要对数据做一些额外的处理。比如,假设要展示下述四种蔬果的价格变化:
[
{
sku: '小台农芒果',
data: [
['2020-3-4', 9, 7, 14, 1],
['2020-3-6', 7, 3, 12, 1],
['2020-3-9', 3, 2, 8, 1],
['2020-3-12', 2, 1, 5, 1],
['2020-3-14', 1, 4, 6, 1],
['2020-3-16', 4, 7, 3, 1],
['2020-3-19', 7, 10, 3, 1],
['2020-3-21', 10, 12, 6, 1],
['2020-3-23', 12, 15, 5, 1],
['2020-3-25', 15, 13, 8, 1],
],
},
{
sku: '阿克苏苹果',
data: [
['2020-3-3', 6, 7, 13, 4],
['2020-3-6', 7, 5, 8, 3],
['2020-3-8', 5, 8, 11, 3],
['2020-3-9', 8, 11, 15, 2],
['2020-3-10', 11, 9, 13, 5],
['2020-3-11', 9, 12, 20, 2],
['2020-3-12', 12, 9, 16, 6],
['2020-3-15', 9, 11, 13, 6],
['2020-3-17', 11, 14, 19, 8],
['2020-3-19', 14, 17, 21, 11],
],
},
{
sku: '海南西州蜜瓜',
data: [
['2020-3-1', 16, 17, 23, 11],
['2020-3-2', 17, 15, 24, 10],
['2020-3-4', 15, 12, 20, 6],
['2020-3-7', 12, 9, 16, 3],
['2020-3-9', 9, 6, 12, 1],
['2020-3-12', 6, 3, 8, 1],
['2020-3-14', 3, 0, 5, 1],
['2020-3-17', 0, 2, 6, 1],
['2020-3-19', 2, 5, 8, 1],
['2020-3-21', 5, 8, 11, 1],
],
},
{
sku: '泰国椰青',
data: [
['2020-3-2', 18, 21, 22, 14],
['2020-3-3', 21, 19, 28, 13],
['2020-3-4', 19, 22, 27, 17],
['2020-3-6', 22, 21, 27, 17],
['2020-3-7', 21, 22, 24, 13],
['2020-3-9', 22, 21, 26, 16],
['2020-3-11', 21, 19, 23, 12],
['2020-3-13', 19, 21, 25, 15],
['2020-3-16', 21, 22, 23, 15],
['2020-3-18', 22, 20, 29, 19],
],
},
];
注意,四个系列的统计时间不同,由于 k 线图无法自动推算类目轴的类别数据,所以第一步需要收集所有类目值:
function retriveDates(series) {
const categories = [];
const len = series.length;
for (let i = 0; i < len; i++) {
// 找出尚未收集的时间值
const dates = series[i].data
.map(([date]) => date)
.filter((date) => categories.findIndex((cat) => cat === date) < 0);
// 批量插入
categories.splice(categories.length, 0, ...dates);
}
return categories;
}
第二步,需要根据应用场景对收集到的类目值进行排序,本例中为简便起见,将借助 moment
库进行时间排序:
function sort(categories) {
const format = 'YYYY-MM-DD';
return categories.sort((d1, d2) => moment(d1, format) - moment(d2, format));
}
第三步,有了类目数据之后,还需要整理系列数据的顺序,使得数据与其对应的类目能够一一对应:
function reschedule(series, categories) {
return series.map(({ sku, data }) => {
return {
name: sku,
data: categories.map((cat) => {
const index = data.findIndex((c) => c === cat);
return index >= 0 ? data[index].slice(1) : null;
}),
};
});
}
经过上述步骤就可以得到所有类目值及调整后的系列数据,完整代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Echarts Example</title>
</head>
<body>
<div id="main" style="width: 960px;height: 400px"></div>
<script src="//cdn.bootcss.com/axios/0.19.2/axios.min.js"></script>
<script src="//cdn.bootcss.com/moment.js/2.24.0/moment.min.js"></script>
<script src="//cdn.bootcss.com/echarts/4.5.0/echarts.js"></script>
<script type="text/javascript">
// 解析系列中的类目数据
function retriveDates(series) {
const categories = [];
const len = series.length;
for (let i = 0; i < len; i++) {
// 找出尚未收集的时间值
const dates = series[i].data
.map(([date]) => date)
.filter((date) => categories.findIndex((cat) => cat === date) < 0);
// 批量插入
categories.splice(categories.length, 0, ...dates);
}
return categories;
}
// 借助 moment.js 为时间值排序
function sort(categories) {
const format = 'YYYY-MM-DD';
return categories.sort((d1, d2) => moment(d1, format) - moment(d2, format));
}
// 根据类目序列,调整系列数值的位置
function reschedule(series, categories) {
return series.map(({ sku, data }, index) => {
return {
name: sku,
type: 'k',
data: categories.map((cat) => {
const index = data.findIndex(([c]) => c === cat);
return index >= 0 ? data[index].slice(1) : [];
}),
};
});
}
async function run() {
const myChart = echarts.init(document.getElementById('main'));
const { data: statics } = await axios.get('./statics.json');
const dates = retriveDates(statics);
const sortedDates = sort(dates);
const themeColors = ['#d87c7c', '#919e8b', '#d7ab82', '#6e7074'];
const series = reschedule(statics, sortedDates)
// 为系列设置不同的色值
.map((serie, index) => {
const color = themeColors[index];
const liftColor = echarts.color.lift(color, 0.4);
return {
...serie,
itemStyle: {
color: color,
color0: liftColor,
borderColor: color,
borderColor0: liftColor,
},
};
});
const option = {
toolbox: { feature: { saveAsImage: {} } },
xAxis: { type: 'category', data: sortedDates },
yAxis: { type: 'value' },
legend: { data: series.map((s) => s.name) },
series,
};
myChart.setOption(option);
}
run();
</script>
</body>
</html>
示例效果:
3. 小结
本节主要简述 K 线图的由来、特性,并结合实例讲述 K 线图的各类功能特性,包括基础 K 线图、颜色配置、配置多个 K 线图。