如何使用Tushare和Echarts来画股票K线图
技术支持
Tushare大数据社区官网
首先介绍一下这次要使用的两个工具,Tushare是一个基于Python的金融数据接口,拥有丰富的数据内容,如股票、基金、期货、数字货币等行情数据,公司财务、基金经理等基本面数据等(详细介绍进官网)。如果你之前没有注册过Tushare,而且恰好对金融量化、金融数据分析感兴趣,不妨注册一个Tushare账号,可以获取想要的数据,点击注册。
ECharts官网
常见的数据可视化库:
- D3.js 目前 Web 端评价最高的 Javascript 可视化工具库(入手难)
- ECharts.js 百度出品的一个开源 Javascript 数据可视化库
- Highcharts.js 国外的前端数据可视化库,非商用免费,被许多国外大公司所使用
- AntV 蚂蚁金服全新一代数据可视化解决方案 等等
- Highcharts 和 Echarts 就像是 Office 和 WPS 的关系
ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表,详细介绍可进官网查看.
大白话:
- 是一个JS插件
- 性能好可流畅运行PC与移动设备
- 兼容主流浏览器
- 提供很多常用图表,且可定制(折线图、柱状图、散点图、饼图、K线图)
实现结果
先展示一下最终的结果,下面第一张图是利用Tushare和Echarts做出而来的贵州茅台的日K图,第二张图是同花顺网页版的贵州茅台的日K图。除了数据的展示不一样之外,数据的内容是一样的。
实现步骤
1.搭建运行环境
我这里的运行环境是Python 3.8.0,Tushare的版本是1.2.62的,因为Tushare是Python的第三方包,所以需要导入,详细步骤见:说明。
# 导入tushare
import tushare as ts
# 设置token
ts.set_token('your token here')
# 初始化pro接口
pro = ts.pro_api( )
至此数据获取的环境已经搭建好了,我们来试一下获取的数据是什么样子的。
daily_data=pro.daily(ts_code='600519.SH', start_date='20210101', end_date='20210220')
print(daily_data)
print(type(daily_data))
输出的结果是下面的图中所显示的,你会发现有交易日期,开盘价,收盘价,最高价,最低价,成交量等数据。数据的形式是DataFrame类型的,也就是说daily_data
具备DataFrame的一般方法和属性,因此数据的显示是从最新数据显示在最前面,但是K线需要的是最近成交的数据显示在最后面,待会我们在来处理数据的问题,这一步说明我们数据接口已经可以使用了。
现在开始要引入数据可视化的JavaScript文件,点击进入下载地址,会弹出下面的图片,需要下载的内容是echarts.js
或者echarts.min.js
都可以,下载完后要记得放到对应的工作环境中,方便后面的引入。
2.先画图
在画图之前,我们有必要对Echarts有个基本的了解,建议先去Echarts官网浏览5 分钟上手 ECharts,可以对Echarts有个基本的了解.
使用步骤:
-
引入 ECharts:通过标签方式直接引入构建好的 echarts 文件。
<!DOCTYPE html> <html> <head><meta charset="utf-8"><!-- 引入 ECharts 文件 --><script src="echarts.min.js"></script> </head> </html>
-
准备一个具备大小的DOM容器,用来放图。
<div id="main" style="width: 600px;height:400px;"></div>
-
初始化echarts实例对象
var myChart = echarts.init(document.getElementById('main'));
-
指定配置项和数据(option)
var option = {xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [820, 932, 901, 934, 1290, 1330, 1320],type: 'line'}] };
-
将配置项设置给echarts实例对象
myChart.setOption(option);
需要了解的主要配置:series
xAxis
yAxis
grid
tooltip
title
legend
color
- series:系列列表。每个系列通过
type
决定自己的图表类型;大白话:图标数据,指定什么类型的图标,可以多个图表重叠。 - xAxis:直角坐标系 grid 中的 x 轴
- boundaryGap: 坐标轴两边留白策略 true,这时候刻度只是作为分隔线,标签和数据点都会在两个刻度之间的带(band)中间。
- yAxis:直角坐标系 grid 中的 y 轴
- grid:直角坐标系内绘图网格。
- title:标题组件
- tooltip:提示框组件
- legend:图例组件
- color:调色盘颜色列表[]
OK,对上面的知识点有了一定的了解之后,我们可以开始来画我们需要的K线图了。我先把Echarts的全部代码先贴出来,后面在分别解释没块代码的作用。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>debug</title><style>#k-box{height: 100%;width: 50%;background: white;display: inline-block;}</style><script src="echarts.min.js"></script>
</head>
<body><div id="k-box"></div>
<script>const chartDom = document.getElementById('k-box');const myChart = echarts.init(chartDom);var option;const upColor = '#008000';const downColor = '#c00c00';// 对数据进行处理的函数,将交易日期,成交量,价格信息分别放置在不同的数组function splitData(rawData) {var categoryData = [];var values = [];var volumes = [];for (var i = 0; i < rawData.length; i++) {categoryData.push(rawData[i].splice(0, 1)[0]);values.push(rawData[i]);volumes.push([i, rawData[i][4], rawData[i][0] > rawData[i][1] ? 1 : -1]);}return {categoryData: categoryData,values: values,volumes: volumes};}// 用于计算均线的函数function calculateMA(dayCount, data) {var result = [];for (var i = 0, len = data.values.length; i < len; i++) {if (i < dayCount) {result.push('-');continue;}var sum = 0;for (var j = 0; j < dayCount; j++) {sum += data.values[i - j][1];}result.push(+(sum / dayCount).toFixed(3));}return result;}// data的数据格式是[['交易日期',开盘价,收盘价,最高价,最低价,成交量][][]....]var data = splitData(data);myChart.setOption(option = {animation: false,legend: {bottom: 10,left: 'center',data: ['Dow-Jones index', 'MA5', 'MA10', 'MA20', 'MA30']},tooltip: {trigger: 'axis',axisPointer: {type: 'cross'},borderWidth: 1,borderColor: '#ccc',padding: 10,textStyle: {color: '#000'},position: function (pos, params, el, elRect, size) {var obj = {top: 10};obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;return obj;}// extraCssText: 'width: 170px'},axisPointer: {link: {xAxisIndex: 'all'},label: {backgroundColor: '#777'}},toolbox: {feature: {dataZoom: {yAxisIndex: false},brush: {type: ['lineX', 'clear']}}},brush: {xAxisIndex: 'all',brushLink: 'all',outOfBrush: {colorAlpha: 0.1}},visualMap: {show: false,seriesIndex: 5,dimension: 2,pieces: [{value: 1,color: downColor}, {value: -1,color: upColor}]},grid: [{left: '10%',right: '8%',height: '50%'},{left: '10%',right: '8%',top: '63%',height: '16%'}],xAxis: [{type: 'category',data: data.categoryData,scale: true,boundaryGap: false,axisLine: {onZero: false},splitLine: {show: false},splitNumber: 20,min: 'dataMin',max: 'dataMax',axisPointer: {z: 100}},{type: 'category',gridIndex: 1,data: data.categoryData,scale: true,boundaryGap: false,axisLine: {onZero: false},axisTick: {show: false},splitLine: {show: false},axisLabel: {show: false},splitNumber: 20,min: 'dataMin',max: 'dataMax'}],yAxis: [{scale: true,splitArea: {show: true}},{scale: true,gridIndex: 1,splitNumber: 2,axisLabel: {show: false},axisLine: {show: false},axisTick: {show: false},splitLine: {show: false}}],dataZoom: [{type: 'inside',xAxisIndex: [0, 1],start: 98,end: 100},{show: true,xAxisIndex: [0, 1],type: 'slider',top: '85%',start: 98,end: 100}],series: [{name: 'Dow-Jones index',type: 'candlestick',data: data.values,itemStyle: {color: upColor,color0: downColor,//改动过borderColor: upColor,borderColor0: downColor},tooltip: {formatter: function (param) {param = param[0];return ['Date: ' + param.name + '<hr size=1 style="margin: 3px 0">','Open: ' + param.data[0] + '<br/>','Close: ' + param.data[1] + '<br/>','Lowest: ' + param.data[2] + '<br/>','Highest: ' + param.data[3] + '<br/>'].join('');}}},{name: 'MA5',type: 'line',data: calculateMA(5, data),smooth: true,lineStyle: {opacity: 0.5}},{name: 'MA10',type: 'line',data: calculateMA(10, data),smooth: true,lineStyle: {opacity: 0.5}},{name: 'MA20',type: 'line',data: calculateMA(20, data),smooth: true,lineStyle: {opacity: 0.5}},{name: 'MA30',type: 'line',data: calculateMA(30, data),smooth: true,lineStyle: {opacity: 0.5}},{name: 'Volume',type: 'bar',xAxisIndex: 1,yAxisIndex: 1,data: data.volumes}]}, true);option && myChart.setOption(option);
</script>
</body>
</html>
初始化echarts实例对象,前面的DOM容器已经放好。
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
// 设置股票上涨和下跌的颜色
var upColor = '#008000';
var downColor = '#c00c00';
开始将图片画在网页上,我们先设置标题,即图片展示后显示的名称。
myChart.setOption(option = {// 图片的名称,字体等样式.title: {text: '股票日K',left: 0,// 设置字体样式textStyle: {fontSize:28,}},// 是否开启动画效果.animation: true,
},true);
legend图例组件展现了不同系列的标记(symbol),颜色和名字。可以通过点击图例控制哪些系列不显示。
legend: {// 距离top 9个像数.的值可以是像 20 这样的具体像素值,可以是像 '20%' 这样相对于容器高宽的 百分比,也可以是 'top', 'middle', 'bottom'。top: 9,left: '45%',data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']},
tooltip是否显示提示框组件,包括提示框浮层和 axisPointer。
tooltip: {trigger: 'axis',backgroundColor: 'rgba(255,255,255,0.6)',// 坐标轴指示器是指示坐标轴当前刻度的工具。axisPointer: {type: 'cross'},borderWidth: 1,borderColor: '#ccc',padding: 10,textStyle: {color: '#000'},position: function (pos, params, el, elRect, size) {var obj = {top: 10};obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;return obj;}
},
axisPointer鼠标移动到一定位置后Y轴和X轴上显示的文字说明.
axisPointer: {link: {xAxisIndex: 'all'},label: {// {# 鼠标移到那里x轴和y轴显示的数字#}backgroundColor: '#777'}
},
toolbox 工具栏。内置有导出图片,数据视图,动态类型切换,数据区域缩放,重置五个工具,具体需要什么功能看个人需要。
toolbox: {feature: {dataZoom: {yAxisIndex: false},brush: {type: ['lineX', 'clear']}}
},
brush
是区域选择组件,用户可以选择图中一部分数据,从而便于向用户展示被选中数据,或者他们的一些统计计算结果,隐藏K线数据和5日均线数据。
brush: {xAxisIndex: 'all',brushLink: 'all',outOfBrush: {colorAlpha: 0.1}
},
visualMap
是视觉映射组件,用于进行『视觉编码』,也就是将数据映射到视觉元素(视觉通道)。
visualMap: {show: false,seriesIndex: 5,dimension: 2,pieces: [{value: 1,color: downColor}, {value: -1,color: upColor}]
},
grid直角坐标系内绘图网格,单个 grid 内最多可以放置上下两个 X 轴,左右两个 Y 轴。可以在网格上绘制折线图,柱状图,散点图(气泡图)。
grid: [{ // 用于控制图像与左边的边距left: '5%',right: '8%',height: '50%'},{left: '10%',right: '8%',top: '63%',height: '16%'}
],
xAxis设置X轴等相关的数据,样式,字体样式,宽高等。
xAxis: [{type: 'category',// 日期,格式是['2016-03-01','2016-03-02','2016-03-03'.......]data: data.categoryData,scale: true,boundaryGap: false,axisLine: {onZero: false},splitLine: {show: false},splitNumber: 20,min: 'dataMin',max: 'dataMax',axisPointer: {z: 100}},{ type: 'category',gridIndex: 1,data: data.categoryData,scale: true,boundaryGap: false,axisLine: {onZero: false},axisTick: {show: false},splitLine: {show: false},axisLabel: {show: false},splitNumber: 20,min: 'dataMin',max: 'dataMax'}
],
yAxis设置Y轴等相关的数据,样式,字体样式,宽高等。
yAxis: [{scale: true,splitArea: {show: true}},{scale: true,gridIndex: 1,splitNumber: 2,axisLabel: {show: false},axisLine: {show: false},axisTick: {show: false},splitLine: {show: false}}
],
dataZoom
组件 用于区域缩放,从而能自由关注细节的数据信息,或者概览数据整体,或者去除离群点的影响。
dataZoom: [{type: 'inside',xAxisIndex: [0, 1],start: 98,end: 100},{show: true,xAxisIndex: [0, 1],type: 'slider',top: '85%',start: 98,end: 100}
],
series-candlestickCandlestick 即我们常说的 K线图
。
series: [{name: '日K',type: 'candlestick',// 股票价格,格式是[['开盘价','收盘价','最低价',最高价,成交量],['开盘价','收盘价','最低价',最高价,成交量]....]data: data.values,itemStyle: {color: upColor,color0: downColor,borderColor: null,borderColor0: null},tooltip: {formatter: function (param) {param = param[0];return ['Date: ' + param.name + '<hr size=1 style="margin: 3px 0">','Open: ' + param.data[0] + '<br/>','Close: ' + param.data[1] + '<br/>','Lowest: ' + param.data[2] + '<br/>','Highest: ' + param.data[3] + '<br/>'].join('');}},{name: 'Volume',type: 'bar',xAxisIndex: 1,yAxisIndex: 1,// [0, 168890000, 1]data: data.volumes}},
MA5:5日均线线的样式二设置,下面的代码只展示了5日均线,其他均线的原理其实是一样的,这里的data: calculateMA(5, data)
借助了函数calculateMA( )
function calculateMA(dayCount, data) {var result = [];for (var i = 0, len = data.values.length; i < len; i++) {if (i < dayCount) {result.push('-');continue;}var sum = 0;for (var j = 0; j < dayCount; j++) {sum += data.values[i - j][1];}result.push(+(sum / dayCount).toFixed(3));}return result;
}
{name: 'MA5',type: 'line',data: calculateMA(5, data),smooth: true,showSymbol: false,lineStyle: {opacity: 0.8}
最后不要忘记将刚才指定的配置项和数据显示图表
option && myChart.setOption(option);
到现在,数据展示方面的图已经出做来了,但是如果没有数据的提供,K线图还是画不出来的,接下来,我们要处理数据了,注意数据的格式是[[内容1],[内容2],[内容3]...]
,二维数组的形式展现.
3.后填数据
在搭建运行环境中,我们已经知道从Tushare中提取的数据是DataFrame形式的,但是提供给JavaScript的数据是二维数组,一般来说,数据的处理应该在后端处理,前端就负责实现数据的展现,所以我建议在后端就把数据处理好,后面直接传给前端。
股票的K线图,从上市那天就会有,因此,一般来说我们要获取的是股票上市那天到今天的交易数据。
import tushare as tsimport timepro = ts.pro_api( )# 实际应用用这里用的是参数传递,不会定死。ts_code = '600519.SH'# 我们需要的是公司的上市日期,是否退市,退市日期data = pro.stock_basic(ts_code=ts_code,fields='list_status,list_date,delist_date')data=data.iloc[0] # 输出结果: 0 L 20010827 None# L:上市 D:退市 P:暂停上市# 如果不是退市,那么结束日期(YYYYMMDD)定为今天.start_date =data['list_date']if data['list_status'] !='D':end_date = time.strftime("%Y %m %d").replace(' ','')else:# 如果是退市,,那么结束日期(YYYYMMDD)定为退市日期end_date=data['delist_date']# 获取股票从上市到现在(退市)的所有信息,all_daily_info = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)daily_info_list=[]# ['交易日期','开盘价','收盘价','最低价',最高价,成交量]
sele_daily_info=all_daily_info[['trade_date','open','close','low','high','vol']].sort_index(ascending=False)for num in range(len(sele_daily_info)):# 将DataFrame格式的数据转换成列表,再转换成二维数组daily_info_list.append(list(sele_daily_info.iloc[num]))
OK,数据也处理好了,现在就需要将数据传递到前端了,如果该方法我就不涉及了,方法也挺多的,我用的是Django前端框架,也可以用Ajax传递。好了,至此,数据方面和可视化方面已经全部做好了,最终的效果就是上面第一张图。如果有什么疑问,欢迎咨询。