作者:琥珀里有波罗的海
来源:数据如琥珀
打造自己的股票分析系统
1、前言
数据分析很重要的一步是结果展示与汇报,无论是给客户还是上级。你可以选择PPT展示,也可以给出word文档展示,但是如果有一个交互式的网站(app)展示,无疑是更加吸引人。
说起网站,很多人想到PHP,JS,但是绝大多数“数据分析师”没有这些技术,原因主要是:这完全是两个领域,精力有限的人很少会大量投入时间来精通。
python有很多优秀的开源网页设计框架,Django和Flask便是其中两个“巨头”,在企业中应用广泛。但是Django和Flask 固然优秀,但是它毕竟只是框架,关于页面设计的细节多数还是JS,而且有的时候你学了好几天设计出来的页面依然平庸无奇(我的就是奇丑无比)。
但是数据分析数据师如果想通过网页快速展示自己的成果,一定要学JS?一定要“重量级”框架?一定要忍受奇丑无比?
NO!NO! NO!
今天我们可以用纯python入门一个股票分析系统,不用JS,不用深入框架,入门一样酷炫,简约而不简单。
2、主角登场
作为入门教程,我们要实现以下功能:
设计一个网站app,查看股票的历史记录
获取股票列表,支持下拉选择以及输入检索
支持时间段查询
画出经典的股票趋势图
动态调整时间段
支持鼠标悬停显示当日详细详细
下面我们一一解释每个环节的实现方式,我估计本实现的难度系数在2颗星(只要你懂python)。
2.1 网站如何设计
这是一个轻量版的网站,我们这里自然无需用Django和(原生)Flask的重量级框架,但是即便是一个简单的demo,我依然希望网站保留可扩展的能力,比如实际的应用中我们可能还需要其他功能:
需要实时显示当日股票信息
显示大盘信息
显示多个趋势
显示周趋势,月度趋势
添加表格,显示股票问题
显示实时新闻
甚至增加一键买/卖
增加量化分析模型
以上通通可以实现,依然无需JS,有兴趣的可以进一步交流。
今天的网站设计采用的Plotly Dash,我相信很多人听过Plolty这个包,也用它画过简单的图(相当于交互版的matplotlib),但是dash 的功能很多人没有使用过。
网站链接如下:https://plotly.com/dash/
你可以看到很多交互式Dashboard的设计,比如用于制造业。
关于Plotly Dash,以下优缺点纯属个人亲测(实际项目)感受,可以帮你决定是否适合你:
优点:
html 设计以及用户操作响应采用python,无需了解JS
框架采用的Flask,所以Flask的功能都可以在这里实现,意味着强大的后续功能扩展
对Flask进行了一定程度的封装,所以入手极快
界面美观,如上图,用过plotly的人都喜欢它的设计
支持交互式操作,比如plotly的图画带有各种按钮
被很多开源BI 支持,比如Grafana
适合数据分析师
缺点:
不适合大型项目,用户响应速度没有JS快(因为是纯python)
不适合网站开发者,因为使用习惯不同
2.2 股票信息如何获取
这里不建议采用爬虫的方式进行获取,因为时间成本太高,而且获取的质量不稳定,作为一个网站,我们需要选用稳定的源。
有很多股票信息的开源包可以选择,我亲测过两个接口包,一个是akshare,一个Tushare。
这个demo中我们两个都进行了测试,但是akshare的访问速度和稳定性不佳(有可能是我个人网络问题),最后我选择了Tushare。
关于Tushare,采用注册制,注册即可获取足够积分来稳定的获取股票的信息,为了稳定性,我最终选择了Tushare,它也的确不辜负所望。由于我的积分目前不足以获取股票的周线,月线,以及大盘的信息,所以没法在demo中涵盖。
如果你是新手,不如点击我的邀请链接,注册的同时帮我获取积分吧:
https://tushare.pro/register?reg=129033
2.3 开启实战
首先导入我们所需的“主角”以及“配角”,这里依然将每个所需的包功能放在注释中。
import tushare as ts # 获取股票信息import dash # 设计网站from datetime import datetime, timedeltaimport dash_core_components as dcc #设计网站采用的核心插件库import dash_bootstrap_components as dbc #设计网站采用的美化页面import dash_html_components as html #设计网站采用的核心插件库from dash.dependencies import Input, Output, State # 完成用户点击响应import plotly.graph_objects as go # 画出趋势图from dash.exceptions import PreventUpdateimport pandas as pd
我们需要给我们网站中每个元素定义ID,方便后面进行回调,响应用户的操作。
# ID definitionsid_button_update_a_share_list = 'id-button-update-a-share-list'id_dpl_share_code = 'id-dpl-share-code'id_date_picker_range = 'id-date-picker-range'id_button_query_trend = 'id-button-query-trend'id_graph_hist_trend_graphic = 'id-graph-hist-trend-graphic'
这里主要的控件就是:
下拉列表,用于选择股票
右侧有更新股票清单按钮,用于更新股票清单,因为股票清单很久才更新一次,我们没有必要每次开网页都实时获取,可以从本地数据库或者本地文件中获取。
选择时间控件
查看股票信息按钮
绘图区
2.4 定义多个功能函数
为了方便调用,我们将一些常用的功能变成函数:
update_a_share_list 用于从csv中读取股票代码以及名称
get_A_stock_list 用于从网上下载最新的股票清单信息
write_A_stock_list_to_csv 网上下载的信息同步到本地
share_dict_to_option 将本地文件中的信息,合并成控件的选项,因为有些人知道股票代码,有些人知道股票名称,因此有必要支持两种检索
get_trend_df:用于通过股票代码,以及时间段来获取日线
plot_candlestick:用于画股票的K线,
def update_a_share_list():
# display a stock share list from csv
a_share_list_df = pd.read_csv(A_STOCK_FILE, index_col='ts_code')
share_dict = a_share_list_df.to_dict()['name']
return share_dict_to_option(share_dict)
def get_A_stock_list():
# fetch list from internet
share_df = ts_pro.stock_basic(
exchange='',
list_status='L',
fields='ts_code,name')
share_df.set_index('ts_code', inplace=True)
return share_df
def write_A_stock_list_to_csv(file):
# sync to csv
share_df = get_A_stock_list()
share_df.to_csv(file)
def share_dict_to_option(share_dict):
# convert name and code to options
name_list = [str(key) + '-' + str(value)
for key, value in share_dict.items()]
return list_to_option_list(name_list)
def split_share_to_code(share):
# split options to get code
code = share.split('-')[0]
return code
def list_to_option_list(list):
# quick create dropdown options from list
return [{"label": i, "value": i} for i in list]
def get_trend_df(code, start_date, end_date):
# get history tend by ts_code,start_date,end_datess
df = ts_pro.daily(ts_code=code, start_date=start_date, end_date=end_date)
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df.set_index('trade_date', inplace=True)
df.sort_index(inplace=True) # sort result by date (datetime)
return df
def plot_candlestick(df):
# plot candlestick
# customize the color to China Stock
fig = go.Figure(
data=[
go.Candlestick(
x=df.index,
open=df['open'],
high=df['high'],
low=df['low'],
close=df['close'],
increasing=dict(
line=dict(
color="#FF0000")),
decreasing=dict(
line=dict(
color="#00FF00")),
)])
fig.update_layout(
margin=dict(l=2, r=10, t=20, b=20),
paper_bgcolor="LightSteelBlue",
) # change the layout of figure
return fig
2.5 设计网页
网站框架的建立仅仅需要一行代码:dash.Dash(__name__)。这样就自动创建好一切框架,剩下的就是需要添加按钮,控件等。
这里我们通过按钮的设计来看一下该方案的简单快捷:
update_a_share_button = dbc.Button(
id=id_button_update_a_share_list,
color='light',
children='更新列表', outline=True)
这是一个python函数,意味着你需要的主要配置都在函数的参数中。id 是前文提到的元素id,主要用于回调函数。color 不用说,就是颜色定义。因为这个更新股票列表按钮不常用,所以配色采用‘light’,outline 设为True,让他看起来像隐藏了一样,只有鼠标滑过才会显示。
Dropdown 是下拉列表控件,option参数很关键,也就是下拉的选项。调用我们前面定义的函数update_a_share_list来添加选项。
为了让界面美观一点,这里我们调用bootstrap进行对齐设计。
Graph 控件用于绘图,核心的K线采用plot_candlestick函数绘制。
最后,设计好的所有控件,放在app.layout中即可。
这样一个静态的网页就设计好了。
# create dash app
app = dash.Dash(__name__)
# layout design
header = html.H2("股票查询", className='text-center')
# update list button
update_a_share_button = dbc.Button(
id=id_button_update_a_share_list,
color='light',
children='更新列表', outline=True)
# share select dropdown list
select_share = dcc.Dropdown(
id=id_dpl_share_code,
options=update_a_share_list()
)
# datetime picker
date_pick = dcc.DatePickerRange(
id='id-date-picker-range',
max_date_allowed=datetime.today(),
start_date=datetime.today().date() - timedelta(days=60),
end_date=datetime.today().date()
)
# query button
query_button = dbc.Button(
"查询",
color="warning",
className="mr-1",
id=id_button_query_trend,
outline=False)
# make a better row/col layout
select_share_row = dbc.Container(
[
dbc.Row(
[
dbc.Col(
[select_share],
className='col-5'),
dbc.Col(
[update_a_share_button],
className='col-2')]),
html.Br(),
dbc.Row(
[
dbc.Col(
[date_pick],
className='col-5'),
dbc.Col(
[query_button],
className='col-1')])])
# get default figure
default_fig = plot_candlestick(
get_trend_df(
'000001.SZ',
start_date='20200101',
end_date='20200707'))
# graphic div
graphic_div = dbc.Container(
[dcc.Graph(figure=default_fig, id=id_graph_hist_trend_graphic)])
# fully layout
app.layout = html.Div([
header,
select_share_row,
html.Br(),
graphic_div,
])
2.6 响应用户操作
Dash 采用callback 函数用于响应用户的操作,主要的状态有三个:
Input:表示”一触即发“,也就是用户只要进行了该操作,就会调用callback 函数,常见的如按钮的点击动作
State:表示“整装待发”,也就是只有Input 被执行时,callback会检查该元素的状态。该状态改变时,不会立马执行callback操作。比如下拉列表的选择。注意:如果你想让下拉列表选择变化后,立马会调出相应的趋势,那么可以把下拉列表的相应操作放在Input中
Output:就是callback的结果返回给哪个控件。
所以结合上面的基本信息,我们可以写出绘图callback。
Output, Input,State 函数都会有两个参数,第一个是元素的id,也就是指定元素。第二个是元素的属性,比如按钮的n_clicks属性,下拉列表的当然值value,日期选择控件的start_date和end_date。
Output(id_graph_hist_trend_graphic, 'figure'),
Input(id_button_query_trend, 'n_clicks'),
State(id_dpl_share_code, 'value'),
State(id_date_picker_range, 'start_date'),
State(id_date_picker_range, 'end_date')]
@app.callback(
Output(id_graph_hist_trend_graphic, 'figure'),
[Input(id_button_query_trend, 'n_clicks')],
[State(id_dpl_share_code, 'value'),
State(id_date_picker_range, 'start_date'),
State(id_date_picker_range, 'end_date')]
)
def update_output_div(query, share, start, end):
if query is not None:
share_code = split_share_to_code(share)
start_str = start.replace("-", "")
end_str = end.replace("-", "")
return plot_candlestick(
get_trend_df(
share_code,
start_date=start_str,
end_date=end_str))
else:
raise PreventUpdate
2.7 运行程序
运行程序只有一行代码,但是这里可以指定IP,端口等,让网站被其他人访问。默认的网址 http://127.0.0.1:8050/
if __name__ == '__main__':
app.run_server(debug=True)
3、总结
作为一个入门级,适合数据分析师的网站设计示例,本示例演示如何设计一个股票历史查询网站。Plotly 以及Dash 对于数据分析来说,无疑是利器,可以不需其他前端语言完成一个酷炫的界面设计。Tushare 可以稳定的获取各种股票/金融相关的信息。
最重要的是,虽然该方案入门及其简单,但是扩展能力也很强,适合快速交付成果给客户。
扫描回复「股票02」获取本文代码