首页 > 编程开发 > python数据分析 >
-
实战:开发支持多周期切换的在线看盘系统
第15章 专业在线看盘系统实战
15.1 实战:开发支持多周期切换的在线看盘系统
15.1.1 先讲个真实需求:老板要我做一个支持多周期切换的在线看盘系统
上个月老板让我做一个支持多周期切换的在线看盘系统,要求能实时更新数据,支持日线、周线、月线、60分钟线、30分钟线等多周期切换,还要能在公司内部服务器上部署。我之前只会用Plotly生成静态K线图,完全不知道怎么实现多vb.net教程C#教程python教程SQL教程access 2010教程周期切换和实时更新。后来发现Dash可以快速实现这些功能,而且不需要前端知识。今天我就把开发支持多周期切换的在线看盘系统的方法讲清楚,帮你快速做出专业的在线看盘系统。
15.1.2 核心技术:多周期数据获取与动态切换
-
多周期数据获取
Tushare提供了日线、周线、月线数据的获取接口,对于分钟线数据,可以通过pro.bar()接口获取,支持1分钟、5分钟、15分钟、30分钟、60分钟等周期。 -
动态切换周期
Dash的回调机制可以实现周期的动态切换,当用户选择不同的周期时,自动获取对应周期的数据并更新图表。
15.1.3 实战:开发支持多周期切换的在线看盘系统
-
实战代码:支持多周期切换的在线看盘系统
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', # 默认选择贵州茅台
style={'width': '300px', 'marginRight': '20px'}
)
], style={'display': 'inline-block', 'verticalAlign': 'middle'}),
# 周期选择下拉菜单
html.Div([
html.Label('选择周期:', style={'fontSize': '16px', 'marginRight': '10px'}),
dcc.Dropdown(
id='period-selector',
options=[
{'label': '日线', 'value': 'D'},
{'label': '周线', 'value': 'W'},
{'label': '月线', 'value': 'M'},
{'label': '60分钟线', 'value': '60MIN'},
{'label': '30分钟线', 'value': '30MIN'},
{'label': '15分钟线', 'value': '15MIN'},
{'label': '5分钟线', 'value': '5MIN'},
{'label': '1分钟线', 'value': '1MIN'}
],
value='D', # 默认选择日线
style={'width': '200px', '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': '最近100个周期', 'value': '100'},
{'label': '最近200个周期', 'value': '200'},
{'label': '最近500个周期', 'value': '500'},
{'label': '全部数据', 'value': 'ALL'}
],
value='200', # 默认选择最近200个周期
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('period-selector', 'value'),
Input('time-range-selector', 'value'),
Input('refresh-button', 'n_clicks'),
Input('interval-component', 'n_intervals')],
[State('stock-selector', 'value'),
State('period-selector', 'value')]
)
def update_dashboard(selected_stock, period, time_range, n_clicks, n_intervals, current_stock, current_period):
# 确定要获取的股票代码和周期
stock_code = selected_stock if selected_stock is not None else current_stock
period = period if period is not None else current_period
# 根据周期获取数据
pro = ts.pro_api()
if period == 'D':
# 获取日线数据
df = pro.daily(ts_code=stock_code, start_date='20000101', end_date=datetime.now().strftime('%Y%m%d'))
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df.rename(columns={'trade_date': 'datetime'}, inplace=True)
elif period == 'W':
# 获取周线数据
df = pro.weekly(ts_code=stock_code, start_date='20000101', end_date=datetime.now().strftime('%Y%m%d'))
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df.rename(columns={'trade_date': 'datetime'}, inplace=True)
elif period == 'M':
# 获取月线数据
df = pro.monthly(ts_code=stock_code, start_date='20000101', end_date=datetime.now().strftime('%Y%m%d'))
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df.rename(columns={'trade_date': 'datetime'}, inplace=True)
else:
# 获取分钟线数据
# 转换周期格式,比如'60MIN'转换为'60min'
period_tushare = period.lower()
# 获取最近1年的分钟线数据
start_date = (datetime.now() - timedelta(days=365)).strftime('%Y%m%d')
df = pro.bar(ts_code=stock_code, freq=period_tushare, start_date=start_date, end_date=datetime.now().strftime('%Y%m%d'))
df['datetime'] = pd.to_datetime(df['trade_time'])
# 数据预处理
df.sort_values('datetime', inplace=True)
df.set_index('datetime', inplace=True)
# 根据时间范围筛选数据
if time_range != 'ALL':
df = df.tail(int(time_range))
# 计算技术指标
# 计算均线(根据周期调整均线参数)
if period in ['D', 'W', 'M']:
df['ma5'] = df['close'].rolling(window=5).mean()
df['ma20'] = df['close'].rolling(window=20).mean()
df['ma60'] = df['close'].rolling(window=60).mean()
else:
# 分钟线使用更短的均线参数
df['ma5'] = df['close'].rolling(window=20).mean()
df['ma20'] = df['close'].rolling(window=60).mean()
df['ma60'] = df['close'].rolling(window=240).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日均线' if period in ['D', 'W', 'M'] else '20周期均线',
line=dict(color='#1f77b4', width=2),
opacity=0.8
))
fig_k.add_trace(go.Scatter(
x=df.index,
y=df['ma20'],
name='20日均线' if period in ['D', 'W', 'M'] else '60周期均线',
line=dict(color='#ff7f0e', width=2),
opacity=0.8
))
fig_k.add_trace(go.Scatter(
x=df.index,
y=df['ma60'],
name='60日均线' if period in ['D', 'W', 'M'] else '240周期均线',
line=dict(color='#2ca02c', width=2),
opacity=0.8
))
period_name = {'D': '日线', 'W': '周线', 'M': '月线', '60MIN': '60分钟线', '30MIN': '30分钟线', '15MIN': '15分钟线', '5MIN': '5分钟线', '1MIN': '1分钟线'}
fig_k.update_layout(
title=f'{stock_code} {period_name[period]} 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'{period_name[period]}5均线: {latest_data["ma5"]:.2f}元'),
html.P(f'{period_name[period]}20均线: {latest_data["ma20"]:.2f}元'),
html.P(f'{period_name[period]}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=8052)
逐行讲解:
pro.bar(ts_code=stock_code, freq=period_tushare, ...):获取分钟线数据,freq参数支持1min、5min、15min、30min、60min等周期
period_tushare = period.lower():将周期格式转换为Tushare支持的格式,比如'60MIN'转换为'60min'
if period in ['D', 'W', 'M']::根据周期调整均线参数,日线、周线、月线使用5、20、60周期均线,分钟线使用20、60、240周期均线
period_name = {'D': '日线', ...}:定义周期名称的映射,用于在图表标题和数据统计中显示中文周期名称
运行结果:
在命令行中运行代码,在浏览器中访问 http://localhost:8052 ,就可以看到专业的多周期在线看盘系统,支持选择不同的股票、周期(日线、周线、月线、60分钟线、30分钟线、15分钟线、5分钟线、1分钟线)、时间范围,手动或自动刷新数据,显示K线图、成交量、MACD、KDJ等技术指标,以及股票基本信息和数据统计指标。
15.1.4 基础知识拓展:多周期分析的应用场景
-
趋势分析
通过不同周期的K线图,可以分析股票的长期趋势和短期波动。比如日线图用于分析长期趋势,分钟线图用于分析短期波动。 -
买卖时机判断
结合不同周期的技术指标,可以更准确地判断买卖时机。比如日线图显示股票处于上升趋势,60分钟线图显示股票出现买入信号,可以考虑买入。 -
量化交易
在量化交易中,多周期分析可以用于构建更复杂的交易策略。比如当日线图和60分钟线图同时出现买入信号时,执行买入操作;当日线图和60分钟线图同时出现卖出信号时,执行卖出操作。
15.1.5 实战优化:添加多周期对比功能
-
实战代码:添加多周期对比功能
python
# 在应用布局中添加多周期对比区域
html.Div([
html.H2('多周期对比', style={'textAlign': 'center', 'color': '#333333', 'margin': '20px 0'}),
html.Div([
dcc.Graph(id='daily-k线图', style={'height': '300px', 'width': '50%', 'display': 'inline-block', 'verticalAlign': 'top'}),
dcc.Graph(id='60min-k线图', style={'height': '300px', 'width': '50%', 'display': 'inline-block', 'verticalAlign': 'top'})
])
], style={'width': '100%', 'maxWidth': '1600px', 'margin': '0 auto', 'padding': '20px 0'})
# 定义回调函数:更新多周期对比图
@app.callback(
[Output('daily-k线图', 'figure'),
Output('60min-k线图', 'figure')],
[Input('stock-selector', 'value'),
Input('refresh-button', 'n_clicks'),
Input('interval-component', 'n_intervals')],
[State('stock-selector', 'value')]
)
def update_multi_period_comparison(selected_stock, n_clicks, n_intervals, current_stock):
stock_code = selected_stock if selected_stock is not None else current_stock
pro = ts.pro_api()
# 获取日线数据
df_daily = pro.daily(ts_code=stock_code, start_date=(datetime.now() - timedelta(days=180)).strftime('%Y%m%d'), end_date=datetime.now().strftime('%Y%m%d'))
df_daily['trade_date'] = pd.to_datetime(df_daily['trade_date'], format='%Y%m%d')
df_daily.sort_values('trade_date', inplace=True)
df_daily.set_index('trade_date', inplace=True)
df_daily['ma5'] = df_daily['close'].rolling(window=5).mean()
df_daily['ma20'] = df_daily['close'].rolling(window=20).mean()
# 获取60分钟线数据
df_60min = pro.bar(ts_code=stock_code, freq='60min', start_date=(datetime.now() - timedelta(days=30)).strftime('%Y%m%d'), end_date=datetime.now().strftime('%Y%m%d'))
df_60min['datetime'] = pd.to_datetime(df_60min['trade_time'])
df_60min.sort_values('datetime', inplace=True)
df_60min.set_index('datetime', inplace=True)
df_60min['ma20'] = df_60min['close'].rolling(window=20).mean()
df_60min['ma60'] = df_60min['close'].rolling(window=60).mean()
# 绘制日线K线图
fig_daily = go.Figure()
fig_daily.add_trace(go.Candlestick(
x=df_daily.index,
open=df_daily['open'],
high=df_daily['high'],
low=df_daily['low'],
close=df_daily['close'],
name='日线K线图',
increasing_line_color='red',
decreasing_line_color='green'
))
fig_daily.add_trace(go.Scatter(
x=df_daily.index,
y=df_daily['ma5'],
name='5日均线',
line=dict(color='#1f77b4', width=2),
opacity=0.8
))
fig_daily.add_trace(go.Scatter(
x=df_daily.index,
y=df_daily['ma20'],
name='20日均线',
line=dict(color='#ff7f0e', width=2),
opacity=0.8
))
fig_daily.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_daily.update_xaxes(rangeslider_visible=False)
# 绘制60分钟线K线图
fig_60min = go.Figure()
fig_60min.add_trace(go.Candlestick(
x=df_60min.index,
open=df_60min['open'],
high=df_60min['high'],
low=df_60min['low'],
close=df_60min['close'],
name='60分钟线K线图',
increasing_line_color='red',
decreasing_line_color='green'
))
fig_60min.add_trace(go.Scatter(
x=df_60min.index,
y=df_60min['ma20'],
name='20周期均线',
line=dict(color='#1f77b4', width=2),
opacity=0.8
))
fig_60min.add_trace(go.Scatter(
x=df_60min.index,
y=df_60min['ma60'],
name='60周期均线',
line=dict(color='#ff7f0e', width=2),
opacity=0.8
))
fig_60min.update_layout(
title=f'{stock_code} 60分钟线K线图',
yaxis_title='价格(元)',
xaxis_title='日期',
template='plotly_white',
hovermode='x unified',
legend={'x': 0.02, 'y': 0.98}
)
fig_60min.update_xaxes(rangeslider_visible=False)
return fig_daily, fig_60min
运行结果:
在浏览器中可以看到多周期对比区域,同时显示日线K线图和60分钟线K线图,方便用户对比不同周期的走势。
15.1.6 总结:多周期在线看盘系统的应用场景
1.专业投资者:专业投资者可以通过多周期在线看盘系统分析股票的长期趋势和短期波动,更准确地判断买卖时机。
2.量化交易员:量化交易员可以通过多周期在线看盘系统构建更复杂的交易策略,提高交易胜率。
3.公司内部使用:公司内部的投资团队可以通过多周期在线看盘系统实时监控股票行情,辅助投资决策。
4.教学演示:在教学中使用多周期在线看盘系统,更直观地展示多周期分析的方法和应用场景。
通过这个实战,你应该已经掌握了开发支持多周期切换的在线看盘系统的方法,包括多周期数据获取、动态切换周期、多周期对比等功能。接下来可以尝试用这些方法制作更专业的股票分析工具和量化交易系统。
下一节咱们就讲如何用Python做量化策略回测,帮你验证策略的有效性。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlePrograme/csharp49711.html










