VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > python数据分析 >
  • 打包成独立Web应用:用Dash部署看盘工具

第14章 股票看盘工具部署实战
14.1 打包成独立Web应用:用Dash部署看盘工具
14.1.1 先讲个真实需求:老板要我做一个公司内部用的看盘工具
上个月老板让我做一个公司内部用的看盘工具,要求能实时更新数据,支持多股对比、技术指标分析,还要能在公司内部服务器上部署。我之前只会用Plotly生成静态网页,完全不知道怎么部署成Web应用。后来发现Dash可以快速把Plotly图表打包成独立的Web应用,而且不需要前端知识。今天我就vb.net教程C#教程python教程SQL教程access 2010教程把用Dash部署看盘工具的方法讲清楚,帮你快速做出专业的Web看盘工具。

14.1.2 核心技术:Dash的组件与回调机制

  1. Dash的组件
    Dash是基于Flask、Plotly和React的Python Web框架,提供了丰富的组件,比如下拉菜单、按钮、滑块、图表等,可以通过Python代码直接创建Web应用的界面。
  2. Dash的回调机制
    Dash的回调机制可以实现组件之间的交互,比如当用户选择不同的股票时,自动更新图表的数据。回调函数通过@app.callback装饰器定义,输入是组件的属性,输出是组件的属性。

14.1.3 实战1:用Dash实现基础看盘工具

  1. 实战代码:基础看盘工具
    python
	# 1. 导入需要的库
	import dash
	from dash import dcc, html, Input, Output, State
	import plotly.graph_objects as go
	import pandas as pd
	import tushare as ts
	import numpy as np
	from datetime import datetime, timedelta
	
	# 2. 初始化Dash应用
	app = dash.Dash(__name__, title='专业股票看盘工具')
	server = app.server # 用于部署到服务器
	
	# 3. 获取股票列表(用于下拉菜单)
	pro = ts.pro_api()
	# 获取沪深A股的股票列表
	stock_list = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,industry,list_date')
	# 转换为下拉菜单的选项格式
	stock_options = [{'label': f'{row["name"]} ({row["ts_code"]})', 'value': row['ts_code']} for _, row in stock_list.iterrows()]
	
	# 4. 创建应用布局
	app.layout = html.Div([
	# 顶部导航栏
	html.Div([
	html.H1('专业股票看盘工具', style={'textAlign': 'center', 'color': '#333333', 'margin': '20px 0'}),
	html.Div([
	# 股票选择下拉菜单
	html.Div([
	html.Label('选择股票:', style={'fontSize': '16px', 'marginRight': '10px'}),
	dcc.Dropdown(
	id='stock-selector',
	options=stock_options,
	value='600519.SH', # 默认选择贵州茅台
	style={'width': '300px', 'marginRight': '20px'}
	)
	], style={'display': 'inline-block', 'verticalAlign': 'middle'}),
	# 时间范围选择下拉菜单
	html.Div([
	html.Label('选择时间范围:', style={'fontSize': '16px', 'marginRight': '10px'}),
	dcc.Dropdown(
	id='time-range-selector',
	options=[
	{'label': '最近1个月', 'value': '1M'},
	{'label': '最近3个月', 'value': '3M'},
	{'label': '最近6个月', 'value': '6M'},
	{'label': '最近1年', 'value': '1Y'},
	{'label': '全部数据', 'value': 'ALL'}
	],
	value='6M', # 默认选择最近6个月
	style={'width': '200px', 'marginRight': '20px'}
	)
	], style={'display': 'inline-block', 'verticalAlign': 'middle'}),
	# 刷新按钮
	html.Div([
	html.Button('刷新数据', id='refresh-button', n_clicks=0, style={'fontSize': '16px', 'padding': '8px 16px', 'backgroundColor': '#1f77b4', 'color': 'white', 'border': 'none', 'borderRadius': '4px'})
	], style={'display': 'inline-block', 'verticalAlign': 'middle'})
	], style={'textAlign': 'center', 'marginBottom': '20px'})
	], style={'backgroundColor': '#f0f0f0', 'padding': '20px', 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'}),
	
	# 主要内容区域
	html.Div([
	# K线图和技术指标区域
	html.Div([
	dcc.Graph(id='k线图', style={'height': '600px'}),
	dcc.Graph(id='成交量图', style={'height': '200px'}),
	dcc.Graph(id='macd图', style={'height': '200px'}),
	dcc.Graph(id='kdj图', style={'height': '200px'})
	], style={'width': '70%', 'display': 'inline-block', 'verticalAlign': 'top', 'padding': '0 20px'}),
	
	# 股票信息和数据统计区域
	html.Div([
	html.Div([
	html.H3('股票基本信息', style={'color': '#333333', 'marginBottom': '20px'}),
	html.Div(id='stock-info', style={'fontSize': '16px', 'lineHeight': '1.8'})
	], style={'backgroundColor': '#f0f0f0', 'padding': '20px', 'borderRadius': '8px', 'marginBottom': '20px', 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'}),
	
	html.Div([
	html.H3('数据统计指标', style={'color': '#333333', 'marginBottom': '20px'}),
	html.Div(id='data-stats', style={'fontSize': '16px', 'lineHeight': '1.8'})
	], style={'backgroundColor': '#f0f0f0', 'padding': '20px', 'borderRadius': '8px', 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'})
	], style={'width': '25%', 'display': 'inline-block', 'verticalAlign': 'top', 'padding': '0 20px'})
	], style={'width': '100%', 'maxWidth': '1600px', 'margin': '0 auto', 'padding': '20px 0'}),
	
	# 隐藏的Interval组件,用于自动刷新数据
	dcc.Interval(
	id='interval-component',
	interval=5*60*1000, # 每隔5分钟自动刷新一次
	n_intervals=0
	)
	])
	
	# 5. 定义回调函数:获取股票数据
	@app.callback( 
	[Output('k线图', 'figure'),
	Output('成交量图', 'figure'),
	Output('macd图', 'figure'),
	Output('kdj图', 'figure'),
	Output('stock-info', 'children'),
	Output('data-stats', 'children')],
	[Input('stock-selector', 'value'),
	Input('time-range-selector', 'value'),
	Input('refresh-button', 'n_clicks'),
	Input('interval-component', 'n_intervals')],
	[State('stock-selector', 'value')]
	)
	def update_dashboard(selected_stock, time_range, n_clicks, n_intervals, current_stock):
	# 确定要获取的股票代码(如果用户没有选择新的股票,使用当前股票)
	stock_code = selected_stock if selected_stock is not None else current_stock
	
	# 根据时间范围确定开始日期
	end_date = datetime.now().strftime('%Y%m%d')
	if time_range == '1M':
	start_date = (datetime.now() - timedelta(days=30)).strftime('%Y%m%d')
	elif time_range == '3M':
	start_date = (datetime.now() - timedelta(days=90)).strftime('%Y%m%d')
	elif time_range == '6M':
	start_date = (datetime.now() - timedelta(days=180)).strftime('%Y%m%d')
	elif time_range == '1Y':
	start_date = (datetime.now() - timedelta(days=365)).strftime('%Y%m%d')
	else:
	start_date = '20000101' # 全部数据
	
	# 获取股票的日线数据
	pro = ts.pro_api()
	df = pro.daily(ts_code=stock_code, start_date=start_date, end_date=end_date)
	
	# 数据预处理
	df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
	df.sort_values('trade_date', inplace=True)
	df.set_index('trade_date', inplace=True)
	
	# 计算技术指标
	# 计算均线
	df['ma5'] = df['close'].rolling(window=5).mean()
	df['ma20'] = df['close'].rolling(window=20).mean()
	df['ma60'] = df['close'].rolling(window=60).mean()
	
	# 计算MACD
	ema12 = df['close'].ewm(span=12, adjust=False).mean()
	ema26 = df['close'].ewm(span=26, adjust=False).mean()
	df['dif'] = ema12 - ema26
	df['dea'] = df['dif'].ewm(span=9, adjust=False).mean()
	df['macd'] = 2 * (df['dif'] - df['dea'])
	
	# 计算KDJ
	low9 = df['low'].rolling(window=9).min()
	high9 = df['high'].rolling(window=9).max()
	df['rsv'] = (df['close'] - low9) / (high9 - low9) * 100
	df['k'] = np.nan
	df['d'] = np.nan
	if len(df) >= 9:
	df.loc[df.index[8], 'k'] = df.loc[df.index[8], 'rsv']
	df.loc[df.index[8], 'd'] = df.loc[df.index[8], 'rsv']
	for i in range(9, len(df)):
	df.loc[df.index[i], 'k'] = (2/3) * df.loc[df.index[i-1], 'k'] + (1/3) * df.loc[df.index[i], 'rsv']
	df.loc[df.index[i], 'd'] = (2/3) * df.loc[df.index[i-1], 'd'] + (1/3) * df.loc[df.index[i], 'k']
	df['j'] = 3 * df['k'] - 2 * df['d']
	
	# 绘制K线图
	fig_k = go.Figure()
	fig_k.add_trace(go.Candlestick(
	x=df.index,
	open=df['open'],
	high=df['high'],
	low=df['low'],
	close=df['close'],
	name='K线图',
	increasing_line_color='red',
	decreasing_line_color='green',
	increasing_fillcolor='rgba(255, 0, 0, 0.3)',
	decreasing_fillcolor='rgba(0, 255, 0, 0.3)'
	))
	fig_k.add_trace(go.Scatter(
	x=df.index,
	y=df['ma5'],
	name='5日均线',
	line=dict(color='#1f77b4', width=2),
	opacity=0.8
	))
	fig_k.add_trace(go.Scatter(
	x=df.index,
	y=df['ma20'],
	name='20日均线',
	line=dict(color='#ff7f0e', width=2),
	opacity=0.8
	))
	fig_k.add_trace(go.Scatter(
	x=df.index,
	y=df['ma60'],
	name='60日均线',
	line=dict(color='#2ca02c', width=2),
	opacity=0.8
	))
	fig_k.update_layout(
	title=f'{stock_code} K线图',
	yaxis_title='价格(元)',
	xaxis_title='日期',
	template='plotly_white',
	hovermode='x unified',
	legend={'x': 0.02, 'y': 0.98}
	)
	fig_k.update_xaxes(rangeslider_visible=False)
	
	# 绘制成交量图
	fig_vol = go.Figure()
	fig_vol.add_trace(go.Bar(
	x=df.index,
	y=df['vol'],
	name='成交量',
	marker_color=np.where(df['close'] >= df['open'], 'red', 'green'),
	opacity=0.7
	))
	fig_vol.update_layout(
	title='成交量',
	yaxis_title='成交量(手)',
	xaxis_title='日期',
	template='plotly_white',
	hovermode='x unified'
	)
	fig_vol.update_xaxes(rangeslider_visible=False)
	
	# 绘制MACD图
	fig_macd = go.Figure()
	fig_macd.add_trace(go.Scatter(
	x=df.index,
	y=df['dif'],
	name='DIF(快线)',
	line=dict(color='#1f77b4', width=2),
	opacity=0.8
	))
	fig_macd.add_trace(go.Scatter(
	x=df.index,
	y=df['dea'],
	name='DEA(慢线)',
	line=dict(color='#ff7f0e', width=2),
	opacity=0.8
	))
	fig_macd.add_trace(go.Bar(
	x=df.index,
	y=df['macd'],
	name='MACD柱状图',
	marker_color=np.where(df['macd'] > 0, '#1f77b4', '#d62728'),
	opacity=0.5
	))
	fig_macd.update_layout(
	title='MACD指标',
	yaxis_title='MACD',
	xaxis_title='日期',
	template='plotly_white',
	hovermode='x unified'
	)
	fig_macd.update_xaxes(rangeslider_visible=False)
	
	# 绘制KDJ图
	fig_kdj = go.Figure()
	fig_kdj.add_trace(go.Scatter(
	x=df.index,
	y=df['k'],
	name='K线',
	line=dict(color='#1f77b4', width=2),
	opacity=0.8
	))
	fig_kdj.add_trace(go.Scatter(
	x=df.index,
	y=df['d'],
	name='D线',
	line=dict(color='#ff7f0e', width=2),
	opacity=0.8
	))
	fig_kdj.add_trace(go.Scatter(
	x=df.index,
	y=df['j'],
	name='J线',
	line=dict(color='#2ca02c', width=2),
	opacity=0.8
	))
	fig_kdj.add_hline(y=80, line=dict(color='#d62728', width=1, dash='dash'))
	fig_kdj.add_hline(y=20, line=dict(color='#2ca02c', width=1, dash='dash'))
	fig_kdj.update_layout(
	title='KDJ指标',
	yaxis_title='KDJ',
	xaxis_title='日期',
	template='plotly_white',
	hovermode='x unified'
	)
	fig_kdj.update_xaxes(rangeslider_visible=False)
	
	# 获取股票基本信息
	stock_info = stock_list[stock_list['ts_code'] == stock_code].iloc[0]
	stock_info_children = [
	html.P(f'股票名称: {stock_info["name"]}'),
	html.P(f'股票代码: {stock_info["symbol"]}'),
	html.P(f'行业: {stock_info["industry"]}'),
	html.P(f'上市日期: {stock_info["list_date"]}')
	]
	
	# 计算数据统计指标
	latest_data = df.iloc[-1]
	data_stats_children = [
	html.P(f'最新价: {latest_data["close"]:.2f}元'),
	html.P(f'开盘价: {latest_data["open"]:.2f}元'),
	html.P(f'最高价: {latest_data["high"]:.2f}元'),
	html.P(f'最低价: {latest_data["low"]:.2f}元'),
	html.P(f'成交量: {latest_data["vol"]:.0f}手'),
	html.P(f'5日均线: {latest_data["ma5"]:.2f}元'),
	html.P(f'20日均线: {latest_data["ma20"]:.2f}元'),
	html.P(f'60日均线: {latest_data["ma60"]:.2f}元')
	]
	
	return fig_k, fig_vol, fig_macd, fig_kdj, stock_info_children, data_stats_children
	
	# 6. 运行应用
	if __name__ == '__main__':
	app.run_server(debug=True, host='0.0.0.0', port=8050)

逐行讲解:
import dash:导入Dash库
app = dash.Dash(name, title='专业股票看盘工具'):初始化Dash应用,设置应用标题
server = app.server:获取Flask服务器实例,用于部署到服务器
dcc.Dropdown(...):创建下拉菜单组件,用于选择股票和时间范围
html.Button(...):创建按钮组件,用于手动刷新数据
dcc.Interval(...):创建Interval组件,用于自动刷新数据,每隔5分钟刷新一次
@app.callback(...):定义回调函数,当用户选择不同的股票、时间范围,或者点击刷新按钮、自动刷新时,更新图表和数据
Input(...):回调函数的输入,比如股票选择下拉菜单的value属性
State(...):回调函数的状态,比如当前选择的股票代码
app.run_server(debug=True, host='0.0.0.0', port=8050):运行应用,debug=True表示开启调试模式,host='0.0.0.0'表示允许外部访问,port=8050表示使用8050端口
运行结果:
在命令行中运行代码,会启动一个Web服务器,在浏览器中访问 http://localhost:8050 ,就可以看到专业的股票看盘工具,支持选择不同的股票、时间范围,手动或自动刷新数据,显示K线图、成交量、MACD、KDJ等技术指标,以及股票基本信息和数据统计指标。

14.1.4 实战2:用Dash实现多股对比看盘工具

  1. 实战代码:多股对比看盘工具
    python
	# 1. 导入需要的库
	import dash
	from dash import dcc, html, Input, Output, State
	import plotly.graph_objects as go
	import pandas as pd
	import tushare as ts
	import numpy as np
	from datetime import datetime, timedelta
	
	# 2. 初始化Dash应用
	app = dash.Dash(__name__, title='多股对比看盘工具')
	server = app.server
	
	# 3. 获取股票列表
	pro = ts.pro_api()
	stock_list = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,industry,list_date')
	stock_options = [{'label': f'{row["name"]} ({row["ts_code"]})', 'value': row['ts_code']} for _, row in stock_list.iterrows()]
	
	# 4. 创建应用布局
	app.layout = html.Div([
	# 顶部导航栏
	html.Div([
	html.H1('多股对比看盘工具', style={'textAlign': 'center', 'color': '#333333', 'margin': '20px 0'}),
	html.Div([
	# 股票选择下拉菜单(支持多选)
	html.Div([
	html.Label('选择股票(可多选):', style={'fontSize': '16px', 'marginRight': '10px'}),
	dcc.Dropdown(
	id='stock-selector',
	options=stock_options,
	value=['600519.SH', '000858.SZ'], # 默认选择贵州茅台和五粮液
	multi=True, # 支持多选
	style={'width': '600px', 'marginRight': '20px'}
	)
	], style={'display': 'inline-block', 'verticalAlign': 'middle'}),
	# 时间范围选择下拉菜单
	html.Div([
	html.Label('选择时间范围:', style={'fontSize': '16px', 'marginRight': '10px'}),
	dcc.Dropdown(
	id='time-range-selector',
	options=[
	{'label': '最近1个月', 'value': '1M'},
	{'label': '最近3个月', 'value': '3M'},
	{'label': '最近6个月', 'value': '6M'},
	{'label': '最近1年', 'value': '1Y'},
	{'label': '全部数据', 'value': 'ALL'}
	],
	value='6M',
	style={'width': '200px', 'marginRight': '20px'}
	)
	], style={'display': 'inline-block', 'verticalAlign': 'middle'}),
	# 刷新按钮
	html.Div([
	html.Button('刷新数据', id='refresh-button', n_clicks=0, style={'fontSize': '16px', 'padding': '8px 16px', 'backgroundColor': '#1f77b4', 'color': 'white', 'border': 'none', 'borderRadius': '4px'})
	], style={'display': 'inline-block', 'verticalAlign': 'middle'})
	], style={'textAlign': 'center', 'marginBottom': '20px'})
	], style={'backgroundColor': '#f0f0f0', 'padding': '20px', 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'}),
	
	# 主要内容区域
	html.Div([
	# 涨跌幅对比图
	dcc.Graph(id='return-comparison', style={'height': '400px', 'marginBottom': '20px'}),
	
	# 技术指标对比区域
	html.Div([
	dcc.Graph(id='macd-comparison', style={'height': '400px', 'width': '50%', 'display': 'inline-block', 'verticalAlign': 'top'}),
	dcc.Graph(id='kdj-comparison', style={'height': '400px', 'width': '50%', 'display': 'inline-block', 'verticalAlign': 'top'})
	])
	], style={'width': '100%', 'maxWidth': '1600px', 'margin': '0 auto', 'padding': '20px 0'}),
	
	# 隐藏的Interval组件
	dcc.Interval(
	id='interval-component',
	interval=5*60*1000,
	n_intervals=0
	)
	])
	
	# 5. 定义回调函数:更新多股对比数据
	@app.callback( 
	[Output('return-comparison', 'figure'),
	Output('macd-comparison', 'figure'),
	Output('kdj-comparison', 'figure')],
	[Input('stock-selector', 'value'),
	Input('time-range-selector', 'value'),
	Input('refresh-button', 'n_clicks'),
	Input('interval-component', 'n_intervals')],
	[State('stock-selector', 'value')]
	)
	def update_comparison(selected_stocks, time_range, n_clicks, n_intervals, current_stocks):
	stock_codes = selected_stocks if selected_stocks is not None else current_stocks
	if not stock_codes:
	stock_codes = ['600519.SH']
	
	# 根据时间范围确定开始日期
	end_date = datetime.now().strftime('%Y%m%d')
	if time_range == '1M':
	start_date = (datetime.now() - timedelta(days=30)).strftime('%Y%m%d')
	elif time_range == '3M':
	start_date = (datetime.now() - timedelta(days=90)).strftime('%Y%m%d')
	elif time_range == '6M':
	start_date = (datetime.now() - timedelta(days=180)).strftime('%Y%m%d')
	elif time_range == '1Y':
	start_date = (datetime.now() - timedelta(days=365)).strftime('%Y%m%d')
	else:
	start_date = '20000101'
	
	# 获取多只股票的数据
	pro = ts.pro_api()
	dfs = {}
	for stock_code in stock_codes:
	df = pro.daily(ts_code=stock_code, start_date=start_date, end_date=end_date)
	df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
	df.sort_values('trade_date', inplace=True)
	df.set_index('trade_date', inplace=True)
	# 计算涨跌幅
	df['return'] = (df['close'] / df['close'].iloc[0] - 1) * 100
	# 计算MACD
	ema12 = df['close'].ewm(span=12, adjust=False).mean()
	ema26 = df['close'].ewm(span=26, adjust=False).mean()
	df['dif'] = ema12 - ema26
	df['dea'] = df['dif'].ewm(span=9, adjust=False).mean()
	df['macd'] = 2 * (df['dif'] - df['dea'])
	# 计算KDJ
	low9 = df['low'].rolling(window=9).min()
	high9 = df['high'].rolling(window=9).max()
	df['rsv'] = (df['close'] - low9) / (high9 - low9) * 100
	df['k'] = np.nan
	df['d'] = np.nan
	if len(df) >= 9:
	df.loc[df.index[8], 'k'] = df.loc[df.index[8], 'rsv']
	df.loc[df.index[8], 'd'] = df.loc[df.index[8], 'rsv']
	for i in range(9, len(df)):
	df.loc[df.index[i], 'k'] = (2/3) * df.loc[df.index[i-1], 'k'] + (1/3) * df.loc[df.index[i], 'rsv']
	df.loc[df.index[i], 'd'] = (2/3) * df.loc[df.index[i-1], 'd'] + (1/3) * df.loc[df.index[i], 'k']
	df['j'] = 3 * df['k'] - 2 * df['d']
	dfs[stock_code] = df
	
	# 绘制涨跌幅对比图
	fig_return = go.Figure()
	colors = ['#ff0000', '#0000ff', '#800080', '#00ff00', '#ff00ff', '#00ffff']
	for i, (stock_code, df) in enumerate(dfs.items()):
	stock_name = stock_list[stock_list['ts_code'] == stock_code].iloc[0]['name']
	fig_return.add_trace(go.Scatter(
	x=df.index,
	y=df['return'],
	name=f'{stock_name} ({stock_code})',
	line=dict(color=colors[i % len(colors)], width=3),
	opacity=0.8,
	hovertemplate='<b>日期: %{x}</b><br>'
	f'{stock_name}涨跌幅: %{y:.2f}%<br>'
	'收盘价: %{customdata:.2f}元<br>'
	'<extra></extra>',
	customdata=df['close']
	))
	fig_return.add_hline(y=0, line=dict(color='#cccccc', width=2, dash='dash'), name='基准线')
	fig_return.update_layout(
	title='多股涨跌幅对比',
	yaxis_title='涨跌幅(%)',
	xaxis_title='日期',
	template='plotly_white',
	hovermode='x unified',
	legend={'x': 0.02, 'y': 0.98}
	)
	fig_return.update_xaxes(rangeslider_visible=False)
	
	# 绘制MACD对比图
	fig_macd = go.Figure()
	for i, (stock_code, df) in enumerate(dfs.items()):
	stock_name = stock_list[stock_list['ts_code'] == stock_code].iloc[0]['name']
	fig_macd.add_trace(go.Scatter(
	x=df.index,
	y=df['macd'],
	name=f'{stock_name} ({stock_code})',
	line=dict(color=colors[i % len(colors)], width=2),
	opacity=0.8,
	hovertemplate='<b>日期: %{x}</b><br>'
	f'{stock_name}MACD: %{y:.2f}<br>'
	'<extra></extra>'
	))
	fig_macd.add_hline(y=0, line=dict(color='#cccccc', width=2, dash='dash'), name='基准线')
	fig_macd.update_layout(
	title='多股MACD对比',
	yaxis_title='MACD',
	xaxis_title='日期',
	template='plotly_white',
	hovermode='x unified',
	legend={'x': 0.02, 'y': 0.98}
	)
	fig_macd.update_xaxes(rangeslider_visible=False)
	
	# 绘制KDJ对比图
	fig_kdj = go.Figure()
	for i, (stock_code, df) in enumerate(dfs.items()):
	stock_name = stock_list[stock_list['ts_code'] == stock_code].iloc[0]['name']
	fig_kdj.add_trace(go.Scatter(
	x=df.index,
	y=df['k'],
	name=f'{stock_name} K线 ({stock_code})',
	line=dict(color=colors[i % len(colors)], width=2),
	opacity=0.8,
	hovertemplate='<b>日期: %{x}</b><br>'
	f'{stock_name}K线: %{y:.2f}<br>'
	'<extra></extra>'
	))
	fig_kdj.add_hline(y=80, line=dict(color='#d62728', width=1, dash='dash'), name='超买线')
	fig_kdj.add_hline(y=20, line=dict(color='#2ca02c', width=1, dash='dash'), name='超卖线')
	fig_kdj.update_layout(
	title='多股KDJ对比',
	yaxis_title='KDJ',
	xaxis_title='日期',
	template='plotly_white',
	hovermode='x unified',
	legend={'x': 0.02, 'y': 0.98}
	)
	fig_kdj.update_xaxes(rangeslider_visible=False)
	
	return fig_return, fig_macd, fig_kdj
	
	# 6. 运行应用
	if __name__ == '__main__':
	app.run_server(debug=True, host='0.0.0.0', port=8051)

逐行讲解:
dcc.Dropdown(..., multi=True):设置下拉菜单支持多选
stock_codes = selected_stocks if selected_stocks is not None else current_stocks:确定要对比的股票代码
for stock_code in stock_codes::循环获取多只股票的数据,计算涨跌幅、MACD、KDJ等技术指标
colors = ['#ff0000', '#0000ff', ...]:定义不同股票的颜色,方便区分
运行结果:
在命令行中运行代码,在浏览器中访问 http://localhost:8051 ,就可以看到多股对比看盘工具,支持选择多只股票、时间范围,手动或自动刷新数据,显示涨跌幅对比、MACD对比、KDJ对比等图表。

14.1.5 基础知识拓展:Dash应用的部署方法

  1. 部署到本地服务器
    在本地服务器上运行Dash应用,只需要在命令行中运行代码,然后在浏览器中访问对应的地址即可。如果需要让其他用户访问,需要确保本地服务器的防火墙开放了对应的端口,并且其他用户可以访问本地服务器的IP地址。
  2. 部署到云服务器
    可以把Dash应用部署到云服务器(比如阿里云、腾讯云、AWS等),步骤如下:
    1.在云服务器上安装Python和必要的库(dash、plotly、pandas、tushare等)
    2.把代码上传到云服务器
    3.在云服务器上运行代码,使用nohup命令让应用在后台运行:
    bash

nohup python app.py > app.log 2>&1 &

配置云服务器的防火墙,开放对应的端口(比如8050)
在浏览器中访问云服务器的IP地址和端口,就可以访问Dash应用了
部署到Heroku
可以把Dash应用部署到Heroku(一个云平台即服务),步骤如下:
创建Heroku账号并安装Heroku CLI
在项目目录中创建Procfile文件,内容为:
web: gunicorn app:server

创建requirements.txt文件,列出所有依赖的库:

	dash==2.11.0
	plotly==5.15.0
	pandas==2.0.3
	tushare==1.2.89
	numpy==1.25.2
	gunicorn==21.2.0

初始化Git仓库,提交代码:
bash

	git init
	git add .
	git commit -m "Initial commit"

创建Heroku应用并部署:
bash

	heroku create my-dash-app
	git push heroku master

打开Heroku应用:
bash

heroku open

14.1.6 总结:Dash看盘工具的应用场景
1.公司内部使用:部署到公司内部服务器,方便员工查看股票行情、分析技术指标
2.个人使用:部署到云服务器,随时随地查看股票行情,支持多股对比、技术指标分析
3.量化交易:结合量化策略,实现实时监控股票行情,当指标达到预设条件时自动执行交易
4.教学演示:在教学中使用Dash看盘工具,更直观地展示股票行情和技术指标的变化
通过这个实战,你应该已经掌握了用Dash部署看盘工具的方法,包括基础看盘工具和多股对比看盘工具,以及Dash应用的部署方法。接下来可以尝试用这些方法制作更专业的股票分析工具和量化交易系统。
下一节咱们就讲如何用Python做量化策略回测,帮你验证策略的有效性。

 本站原创,转载请注明出处:https://www.xin3721.com/ArticlePrograme/csharp49710.html


相关教程