ECharts K 线图

看过股市的朋友们对 K线图 这个图形肯定都不陌生。K线又称“阴阳烛”、“蜡烛线”,是反映价格走势的一种图线,其特色在于一个线段内记录了多项讯息,相当易读易懂且实用有效,广泛用于股票、期货、贵金属、数字货币等行情的技术分析,称为K线分析。

1. 简介

慕课解释

K 线图又称蜡烛图,最初起源于日本德川幕府时期,被用于记录米市的行情与价格波动,后被引入股票与证券交易市场,现如今已经在金融交易活动中大范围使用。K 线图在形态上很像柱状图与折线图的组合,但每个数据节点能够表达出更多的信息。在股票交易中,k 线图通常包含四个维度,即开盘价、收盘价、最高价、最低价的涨跌变化状况,一个典型的 k 线图如下图所示:

包含三个部分:

  1. 上方的细线称为上影线,表示最高价与收盘价的价差;
  2. 下方的细线称为下影线,表示最低价与开盘价的价差;
  3. 中间实体部分表示开盘价与收盘价的价差,当收盘价高于开盘价时称为阳线,在国内通常使用红色实体表示;当收盘价低于开盘价时称为阴线,在国内通常使用绿色实体表示。

2. 实例解说

2.1 基础 K 线图

K 线图在 ECharts 中通过 series.type = kseries.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 线图是一种基于直角坐标系的图表,可以通过 xAxisyAxisgrid 项声明坐标系属性。

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 线图与折线图、柱状图、散点图等其他直角坐标图表有些许不同:

  1. x、y 轴中必须有一条是类目轴,根据用户习惯,通常会选择 x 轴做类目轴;
  2. k 线图无法通过series.data 推断类目属性,所以类目轴必须通过 axis.data 项显式声明类目数据;
  3. 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 线图。