您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

ECharts K 线图

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

解释

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

包含三个部分:

K 线图在 ECharts 中通过 series.type = kseries.type = candlestick 配置,示例:

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		< charset="utf-8" />
		< http-equiv="X-UA-Compatible" content="IE=edge" />
		< name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: px;height: px"></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 = {
				tool@R_229_2@: { feature: { saveAsImage: {} } },
				xAxis: { type: 'category', data: ['2017-10-24', '2017-10-25', '2017-10-26', '2017-10-27'] },
				yAxis: { type: 'value' },
				series: [
					{
						data: [
							// 开盘价、收盘价、最高价、最低价
							[, , , ],
							[, , , ],
							[, , , ],
							[, , , ],
						],
						type: 'k',
					},
				],
			};
			myChart.setOption(option);
		</script>
	</body>
</html>
@H_249_@

示例:

k 线图是一种基于直角坐标系的图表,可以通过 xAxisyAxisgrid 项声明坐标系。

k 线图的 series.data 项是长度为 4 的数组,按次序分别对应 k 线图的开盘价、收盘价、最高价、最低价。

K 线图通过实体颜色表达数据涨、跌,不同国家或地区对于 K 线图的颜色定义不一样,可能是红涨绿跌红涨蓝跌(如台湾、日本、韩国等),可能是绿涨红跌(如西方国家、香港、新加坡等),也可能是有色/无色等表示。认配置项是红涨蓝跌,可通过如下配置项更改:

下例通过 itemStyle , k 线图的颜色:

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		< charset="utf-8" />
		< http-equiv="X-UA-Compatible" content="IE=edge" />
		< name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: px;height: px"></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 = {
				tool@R_229_2@: { feature: { saveAsImage: {} } },
				xAxis: { type: 'category', data: ['2017-10-24', '2017-10-25', '2017-10-26', '2017-10-27'] },
				yAxis: { type: 'value' },
				series: [
					{
						data: [
							// 开盘价、收盘价、最高价、最低价
							[, , , ],
							[, , , ],
							[, , , ],
							[, , , ],
						],
						itemStyle: {
							color: '#d87c7c',
							color0: '#919e8b',
							borderColor: '#d87c7c',
							borderColor0: '#919e8b',
						},
						type: 'k',
					},
				],
			};
			myChart.setOption(option);
		</script>
	</body>
</html>
@H_249_@

示例:

与柱状图、折线图等其他直角坐标图表不同,在坐标系中无论存在多少个 k 线图,认都会被处理为红涨蓝跌,此时必须通过 itemStyle 阴阳线的色值,详见下一节。

由于 k 线图的数据项只能通过这种 4 位数组的格式定义,这会导致 k 线图与折线图、柱状图、散点图等其他直角坐标图表有些许不同:

当序列上只有 k 线图时,问题不大,但若要在同一坐标系上渲染多个 k 线图时,则需要对数据做一些额外的处理。比如,假设要展示下述四种蔬果的价格变化:

[
	{
		sku: '小台农芒果',
		data: [
			['2020-3-4', , , , ],
			['2020-3-6', , , , ],
			['2020-3-9', , , , ],
			['2020-3-12', , , , ],
			['2020-3-14', , , , ],
			['2020-3-16', , , , ],
			['2020-3-19', , , , ],
			['2020-3-21', , , , ],
			['2020-3-23', , , , ],
			['2020-3-25', , , , ],
		],
	},
	{
		sku: '阿克苏苹果',
		data: [
			['2020-3-3', , , , ],
			['2020-3-6', , , , ],
			['2020-3-8', , , , ],
			['2020-3-9', , , , ],
			['2020-3-10', , , , ],
			['2020-3-11', , , , ],
			['2020-3-12', , , , ],
			['2020-3-15', , , , ],
			['2020-3-17', , , , ],
			['2020-3-19', , , , ],
		],
	},
	{
		sku: '海南西州蜜瓜',
		data: [
			['2020-3-1', , , , ],
			['2020-3-2', , , , ],
			['2020-3-4', , , , ],
			['2020-3-7', , , , ],
			['2020-3-9', , , , ],
			['2020-3-12', , , , ],
			['2020-3-14', , , , ],
			['2020-3-17', , , , ],
			['2020-3-19', , , , ],
			['2020-3-21', , , , ],
		],
	},
	{
		sku: '泰国椰青',
		data: [
			['2020-3-2', , , , ],
			['2020-3-3', , , , ],
			['2020-3-4', , , , ],
			['2020-3-6', , , , ],
			['2020-3-7', , , , ],
			['2020-3-9', , , , ],
			['2020-3-11', , , , ],
			['2020-3-13', , , , ],
			['2020-3-16', , , , ],
			['2020-3-18', , , , ],
		],
	},
];
@H_249_@

注意,四个系列的时间不同,由于 k 线图无法推算类目轴的类别数据,所以第一步需要收集所有类目值:

function retriveDates(series) {
	const categories = [];
	const len = series.length;
	for (let i = ; i < len; i++) {
		// 找出尚未收集的时间值
		const dates = series[i].data
			.map(([date]) => date)
			.filter((date) => categories.findIndex((cat) => cat === date) < );
		// 批量插入
		categories.splice(categories.length, , ...dates);
	}
	return categories;
}
@H_249_@

第二步,需要根据应用场景对收集到的类目值进行排序,本例中为简便起见,将借助 库进行时间排序:

function sort(categories) {
	const format = 'YYYY-MM-DD';
	return categories.sort((d1, d2) => moment(d1, format) - moment(d2, format));
}
@H_249_@

第三步,有了类目数据之后,还需要整理系列数据的顺序,使得数据与其对应的类目能够一一对应:

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 >=  ? data[index].slice() : null;
			}),
		};
	});
}
@H_249_@

经过上述步骤就可以得到所有类目值及调整后的系列数据,完整:

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		< charset="utf-8" />
		< http-equiv="X-UA-Compatible" content="IE=edge" />
		< name="viewport" content="width=device-width,initial-scale=1.0" />
		<title>Echarts Example</title>
	</head>
	<body>
		<div id="main" style="width: px;height: px"></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 = ; i < len; i++) {
					// 找出尚未收集的时间值
					const dates = series[i].data
						.map(([date]) => date)
						.filter((date) => categories.findIndex((cat) => cat === date) < );
					// 批量插入
					categories.splice(categories.length, , ...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 >=  ? data[index].slice() : [];
						}),
					};
				});
			}

			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, );
						return {
							...serie,
							itemStyle: {
								color: color,
								color0: liftColor,
								borderColor: color,
								borderColor0: liftColor,
							},
						};
					});

				const option = {
					tool@R_229_2@: { 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>
@H_249_@

示例:


联系我
置顶