Echarts:产品前后端开发
项目结构
接下来进入研发阶段,这也是本章的重点。研发阶段通常会有架构师、前端工程师、后端工程师、测试工程师、运维工程师、项目管理者等多人参与。本节主要讲解由前端工程师和后端工程师负责的产品前后端开发工作。
首先从VS Code中看看整个产品的项目代码结构,如图9-2所示。
static echarts.js templates query.html getdata.py query.py
如图9-2所示, static文件夹中存放要调用的echartsjs , templates中存放前端展示的网页模板query.html , getdata.py为获取处理股票数据的Python代码, query.py为调用Flask框架的主要Python代码。
获取股票数据
使用Python的tushare包获取股票数据。tushare是一 个免费的、开源的Python财经数据接口包。安装完Python的集成环境Anaconda后,在cmd命令行输入如下命令即可安装tushare包:
pip install tushare
安装后在getdata.py代码文件中写入如下代码,以获取并处理股票数据。
import tushare as ts def get_data(code): dict_return = {} # 存放需要的数据 data = ts.get_hist_data(code) # 通过股票代码获取股票最近的数据 data_30 = data[:30].iloc[::-1] # 按照日期正序排列数据 data_30['rise'] = data_30['price_change'] > 0 # 涨 data_30['fall'] = data_30['price_change'] < 0 # 跌 close = data_30['close'] #最近30个交易日的收盘价 close_index = list(close.index) # 收盘价x轴数据 close_value = close.values.tolist() # 收盘价y轴数据 df_diff = data_30[['rise','fall']].sum() # 统计近30交易日的涨跌次数 df_diff_index = list(df_diff.index) # 将数据转为列表格式 df_diff_value = df_diff.values.tolist() # 将数据转为列表格式 dict_return['diff'] = [{"name":item[0],"value":item[1]} for item in list(zip(df_diff_index,df_diff_value))] # 将数据制作成饼图需要的数据格式 price_change = data_30['price_change'].values.tolist() # 统计近30交易日的价格变化 volume = data_30['volume'].values.tolist() # 统计近30交易日的成交量 # 以下为将处理好的数据加入字典 dict_return['close_index'] = close_index dict_return['close_value'] = close_value dict_return['price_change'] = price_change dict_return['volume'] = volume dict_return['df_diff_index'] = df_diff_index return dict_return # def get_name(code): # '''通过股票代码导出公司名称''' # pro=ts.pro_api() # dat = pro.query('stock_basic', fields='symbol,name') # company_name = list(dat.loc[dat['symbol'] == stoke_code].name)[0] # return company_name #get_data('002503')
搭建网页应用
以下代码主要是Flask的内容。Flask是一款使用Python编写的轻量级Web应用框架,是一款后端框架。通过Flask可以开启Web服务。
代码中首先导入相关Python包,然后创建了app这个Flask对象,之后的@app.route为Flask的路由。路由route的作用是当通过GET或者POST请求方式访问http://127.0.0.1:5000/query/这个URL时调用query()函数。
在上述代码中,当调用query()函数时,默认查询股票代码为“601318”(中国平安)的股票信息。通过getdata.py文件中的get_data()函数将查询处理后的数据返回给dict_return变量,然后将该变量传入query.html中实现前端页面渲染后的显示。如果用户在图9-1所示的前端页面输入正确的股票代码,即使用了POST请求,则可返回该股票代码所对应的数据可视化
query.py:
from flask import Flask, request, render_template from getdata import get_data app = Flask(__name__) @app.route('/query/', methods=['GET', 'POST']) def query(): if request.method == 'POST': code = request.form.get('name') dict_return = get_data(code) return render_template('query.html', dict_return = dict_return) else: dict_return = get_data('601318') return render_template('query.html', dict_return = dict_return) if __name__ == '__main__': app.run(debug = True)
编写网页模板
来看templates中存放前端展示的query.html网页模板,这部分代码主要是ECharts内容,代码如下
下述代码与以往ECharts代码的最大区别是从外界获取了数据。例如,代码中的{{dict_return['volume']|tojson}}表示从传入的dict_return字典中获取volume(成交量)的列表数据,Flask中两对大括号{{ }}表示该位置填写传入的参数,“|tojson”主要是为了防止引号解析错误问题。 代码开始部分是从static文件夹引入charts.js,其对应的代码片段为<script src="{{ url_for('static', filename='echarts.js') }}"></script>。
templates/query.html:
<!DOCTYPE html> <head> <meta charset="utf-8"> <title>股票实时查询产品DEMO</title> <script src="{{ url_for('static',filename='echarts.js') }}"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <center> <form id="form" name="form" method='POST' action='/query/' style="text-alige:left"> <h1>请输入需要查询股票代码: <input type="text" name="name" style="height:30px;width:160px;font-size:30px;"> <input type="submit" value="点击查询" ><h1> </form> <div id="main" style="width:1500px;height:750px;alige:center"></div> <!-- ECharts单文件引入 --> <script type="text/javascript"> // 基于准备好的dom,初始化echarts图表 var myChart = echarts.init(document.getElementById('main')); var option = { title: [ { text: '近30交易日\n涨跌天数', x: '25%', y: '25%', textAlign: 'center', textBaseline: 'middle', textStyle: { fontSize: 20 } }, { text: '近30交易日净值变化', x: '73%', y: '9%', textAlign: 'center', textBaseline: 'middle', textStyle: { fontSize: 20 } }, { text: '近30交易日成交量', x: '20.8%', y: '55%', textStyle: { fontSize: 20 } }, { text: '近30个交易日收盘价', x: '73%', y: '55%', textAlign: 'center', textBaseline: 'middle', textStyle: { fontSize: 20 } }, ], tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, backgroundColor:'rgba(255,255,255,1)', grid: [{ left: '5%', right: '55%', top: '60%', bottom: '5%', containLabel: true }, { gridindex: 1, left: '50%', right: '5%', top: '60%', bottom: '5%', containLabel: true }, { gridindex: 2, left: '50%', right: '5%', top: '17%', bottom: '55%', containLabel: true } ], xAxis: [ { type: 'category', data: {{dict_return['close_index']|tojson}}, axisLabel: { interval: 0, rotate:90 }, }, { gridIndex: 1, type: 'category', boundaryGap: false, data: {{dict_return['close_index']|tojson}} }, { gridIndex: 2, type: 'category', boundaryGap: false, data: {{dict_return['close_index']|tojson}} } ], yAxis: [ { type: 'value', axisLabel: { formatter: '{value} ' }, boundaryGap: [0, 0.02] }, { gridIndex: 1, type: 'value', axisLabel: { formatter: '{value} ' } }, { gridIndex: 2, type: 'value', axisLabel: { formatter: '{value} ' } } ], series: [{ name: '', type: 'bar', label: { normal: { show: true, position: 'top', rotate:90, show: false } }, data: {{dict_return['volume']|tojson}} }, { name: '', type: 'pie', center: ['25%', '25%'], radius: ['25%', '35%'], label: { normal: { formatter: '{b} :\n{c}({d}%)' } }, data: {{dict_return['diff']|tojson}} }, { xAxisIndex: 1, yAxisIndex: 1, name: '', type: 'line', lineStyle: { normal: { color: '' } }, //data: ['416', '382', '318', '184', '215', '265', '557', '954', '1627', '1180', '2416', '2678', '3021', '2590','2100','1809','2300','2539',], data: {{dict_return['close_value']|tojson}}, smooth: true, markPoint: { data: [{ type: 'max', name: '最大值', symbolSize: 60 }, { type: 'min', name: '最小值', symbolSize: 60 } ], itemStyle: { normal: { color: '#F36100' } } }, markLine: { data: [{ type: 'average', name: '平均值' }] } }, { xAxisIndex: 2, yAxisIndex: 2, name: '', type: 'line', lineStyle: { normal: { color: '' } }, //data: ['416', '382', '318', '184', '215', '265', '557', '954', '1627', '1180', '2416', '2678', '3021', '2590','2100','1809','2300','2539',], data: {{dict_return['price_change']|tojson}}, smooth: true, markPoint: { data: [{ type: 'max', name: '最大值', symbolSize: 60 }, { type: 'min', name: '最小值', symbolSize: 60 } ], itemStyle: { normal: { color: '#F36100' } } }, markLine: { data: [{ type: 'average', name: '平均值' }] } } ] }; // 为echarts对象加载数据 myChart.setOption(option); </script> </center> </body> </html>
下面来看form表单这段代码。这里的表单是以POST方式提交,提交的URL在action中写入,所以可以在每次查询后返回相同页面,只是页面中的可视化会随着POST的股票代码不同而有所差异。
form id="form" name="form" method='POST' action='/query/' style="text-alige:left"> <h1>请输入需要查询股票代码: <input type="text" name="name" style="height:30px;width:160px;font-size:30px;"> <input type="submit" value="点击查询" ><h1> </form>